private MethodBuilder DefineInvokeMethod(TypeBuilder myType, List <BfOp> ops) { MethodBuilder simpleMethod = myType.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), // returnType new Type[] { typeof(byte[]) }); // ;paramTypes ILGenerator generator = simpleMethod.GetILGenerator(); MethodInfo putcharMI = typeof(BfUtil).GetMethod("PutChar", new Type[] { typeof(char) }); MethodInfo getcharMI = typeof(BfUtil).GetMethod("GetChar", new Type[] {}); Stack <BracketLabels> openBracketStack = new Stack <BracketLabels>(); LocalBuilder dataptr = generator.DeclareLocal(typeof(int)); // local0: pc LocalBuilder tempptr = generator.DeclareLocal(typeof(int)); // local1: temporary generator.Emit(OpCodes.Ldc_I4_0); generator.Emit(OpCodes.Stloc, dataptr); // dataptr = 0 for (int pc = 0; pc < ops.Count; ++pc) { BfOp op = ops[pc]; switch (op.kind) { case BfOpKind.INC_PTR: generator.Emit(OpCodes.Ldloc, dataptr); generator.Emit(OpCodes.Ldc_I4, op.argument); generator.Emit(OpCodes.Add); generator.Emit(OpCodes.Stloc, dataptr); // dataptr += <op.argument> break; case BfOpKind.DEC_PTR: generator.Emit(OpCodes.Ldloc, dataptr); generator.Emit(OpCodes.Ldc_I4, op.argument); generator.Emit(OpCodes.Sub); generator.Emit(OpCodes.Stloc, dataptr); // dataptr += <op.argument> break; case BfOpKind.INC_DATA: generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, op.argument); generator.Emit(OpCodes.Add); generator.Emit(OpCodes.Stelem_I4); // memory[dataptr] += <op.argument> break; case BfOpKind.DEC_DATA: generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, op.argument); generator.Emit(OpCodes.Sub); generator.Emit(OpCodes.Stelem_I4); // memory[dataptr] -= <op.argument> break; case BfOpKind.WRITE_STDOUT: generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] for (int i = 0; i < op.argument; ++i) { if (i < op.argument - 1) { generator.Emit(OpCodes.Dup); } generator.EmitCall(OpCodes.Call, putcharMI, null); // putchar(memory[dataptr]) } break; case BfOpKind.READ_STDIN: for (int i = 0; i < op.argument; ++i) { generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.EmitCall(OpCodes.Call, getcharMI, null); // getchar() generator.Emit(OpCodes.Stelem_I4); // memory[pc] = getchar() } break; case BfOpKind.LOOP_SET_TO_ZERO: generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Stelem_I4); // memory[dataptr] = 0 break; case BfOpKind.LOOP_MOVE_PTR: { Label loop = generator.DefineLabel(); Label endloop = generator.DefineLabel(); // Emit a loop that moves the pointer in jumps of op.argument; it's // important to do an equivalent of while(...) rather than do...while(...) // here so that we don't do the first pointer change if already pointing // to a zero. // // loop: // cmpb 0(%r13), 0 // jz endloop // %r13 += argument // jmp loop // endloop: generator.MarkLabel(loop); generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, 255); generator.Emit(OpCodes.And); generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Beq, endloop); // if memory[dataptr] == 0 goto endloop generator.Emit(OpCodes.Ldloc, dataptr); // dataptr if (op.argument < 0) { generator.Emit(OpCodes.Ldc_I4, -op.argument); generator.Emit(OpCodes.Sub); } else { generator.Emit(OpCodes.Ldc_I4, op.argument); generator.Emit(OpCodes.Add); } generator.Emit(OpCodes.Stloc, dataptr); // dataptr += <op.argument> generator.Emit(OpCodes.Br, loop); generator.MarkLabel(endloop); } break; case BfOpKind.LOOP_MOVE_DATA: { // Only move if the current data isn't 0: // // cmpb 0(%r13), 0 // jz skip_move // <...> move data // skip_move: Label skip_move = generator.DefineLabel(); generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, 255); generator.Emit(OpCodes.And); generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Beq, skip_move); generator.Emit(OpCodes.Ldloc, dataptr); // dataptr if (op.argument < 0) { generator.Emit(OpCodes.Ldc_I4, -op.argument); generator.Emit(OpCodes.Sub); } else { generator.Emit(OpCodes.Ldc_I4, op.argument); generator.Emit(OpCodes.Add); } generator.Emit(OpCodes.Stloc, tempptr); // temp = dataptr + <op.argument> // Use rax as a temporary holding the value of at the original pointer; // then use al to add it to the new location, so that only the target // location is affected: addb %al, 0(%r13) generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, tempptr); // temp generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, tempptr); // temp generator.Emit(OpCodes.Ldelem_I4); // memory[temp] generator.Emit(OpCodes.Add); // memory[dataptr] + memory[temp] generator.Emit(OpCodes.Stelem_I4); // memory[temp] = memory[dataptr] + memory[temp] generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Stelem_I4); // memory[dataptr] = 0 generator.MarkLabel(skip_move); } break; case BfOpKind.JUMP_IF_DATA_ZERO: { Label openLabel = generator.DefineLabel(); Label closeLabel = generator.DefineLabel(); generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, 255); generator.Emit(OpCodes.And); generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Beq, closeLabel); // if memory[dataptr] == 0 goto closeLabel generator.MarkLabel(openLabel); openBracketStack.Push(new BracketLabels(openLabel, closeLabel)); } break; case BfOpKind.JUMP_IF_DATA_NOT_ZERO: { if (openBracketStack.Count == 0) { BfUtil.DIE($"Unmatched closing ']' at pc={pc}"); } BracketLabels labels = openBracketStack.Pop(); generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, 255); generator.Emit(OpCodes.And); generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Bne_Un, labels.openLabel); // if memory[dataptr] != 0 goto openLabel generator.MarkLabel(labels.closeLabel); } break; default: BfUtil.DIE($"INVALID_OP encountered on pc={pc}"); break; } } generator.Emit(OpCodes.Ret); return(simpleMethod); }
private MethodBuilder DefineInvokeMethod(TypeBuilder myType, string instructions) { MethodBuilder simpleMethod = myType.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), // returnType new Type[] { typeof(byte[]) }); // ;paramTypes ILGenerator generator = simpleMethod.GetILGenerator(); MethodInfo putcharMI = typeof(BfUtil).GetMethod("PutChar", new Type[] { typeof(char) }); MethodInfo getcharMI = typeof(BfUtil).GetMethod("GetChar", new Type[] {}); Stack <BracketLabels> openBracketStack = new Stack <BracketLabels>(); LocalBuilder dataptr = generator.DeclareLocal(typeof(int)); // local0: pc generator.Emit(OpCodes.Ldc_I4_0); generator.Emit(OpCodes.Stloc, dataptr); // dataptr = 0 for (int pc = 0; pc < instructions.Length; ++pc) { char c = instructions[pc]; switch (c) { case '>': generator.Emit(OpCodes.Ldloc, dataptr); generator.Emit(OpCodes.Ldc_I4_1); generator.Emit(OpCodes.Add); generator.Emit(OpCodes.Stloc, dataptr); // ++dataptr break; case '<': generator.Emit(OpCodes.Ldloc, dataptr); generator.Emit(OpCodes.Ldc_I4_1); generator.Emit(OpCodes.Sub); generator.Emit(OpCodes.Stloc, dataptr); // --dataptr break; case '+': generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4_1); generator.Emit(OpCodes.Add); generator.Emit(OpCodes.Stelem_I4); // memory[dataptr] += 1 break; case '-': generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4_1); generator.Emit(OpCodes.Sub); generator.Emit(OpCodes.Stelem_I4); // memory[pc] -= 1 break; case '.': generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.EmitCall(OpCodes.Call, putcharMI, null); // putchar(memory[dataptr]) break; case ',': generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.EmitCall(OpCodes.Call, getcharMI, null); // getchar() generator.Emit(OpCodes.Stelem_I4); // memory[dataptr] = getchar() break; case '[': { Label openLabel = generator.DefineLabel(); Label closeLabel = generator.DefineLabel(); generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, 255); generator.Emit(OpCodes.And); generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Beq, closeLabel); // if memory[pc] == 0 goto closeLabel generator.MarkLabel(openLabel); openBracketStack.Push(new BracketLabels(openLabel, closeLabel)); } break; case ']': { if (openBracketStack.Count == 0) { BfUtil.DIE($"Unmatched closing ']' at pc={pc}"); } BracketLabels labels = openBracketStack.Pop(); generator.Emit(OpCodes.Ldarg_1); // memory generator.Emit(OpCodes.Ldloc, dataptr); // dataptr generator.Emit(OpCodes.Ldelem_I4); // memory[dataptr] generator.Emit(OpCodes.Ldc_I4, 255); generator.Emit(OpCodes.And); generator.Emit(OpCodes.Ldc_I4_0); // 0 generator.Emit(OpCodes.Bne_Un, labels.openLabel); // if memory[pc] != 0 goto openLabel generator.MarkLabel(labels.closeLabel); } break; default: Console.WriteLine("Unhandled instruction: " + c); break; } } generator.Emit(OpCodes.Ret); return(simpleMethod); }