// Optimizes a loop that starts at loop_start (the opening JUMP_IF_DATA_ZERO). // The loop runs until the end of ops (implicitly there's a back-jump after the // last op in ops). // // If optimization succeeds, returns a sequence of instructions that replace the // loop; otherwise, returns an empty vector. private static List <BfOp> optimize_loop(List <BfOp> ops, int loop_start) { List <BfOp> new_ops = new List <BfOp>(); if (ops.Count - loop_start == 2) { BfOp repeated_op = ops[loop_start + 1]; if (repeated_op.kind == BfOpKind.INC_DATA || repeated_op.kind == BfOpKind.DEC_DATA) { new_ops.Add(new BfOp(BfOpKind.LOOP_SET_TO_ZERO, 0)); } else if (repeated_op.kind == BfOpKind.INC_PTR || repeated_op.kind == BfOpKind.DEC_PTR) { new_ops.Add(new BfOp(BfOpKind.LOOP_MOVE_PTR, repeated_op.kind == BfOpKind.INC_PTR ? repeated_op.argument : -repeated_op.argument)); } } else if (ops.Count - loop_start == 5) { // Detect patterns: -<+> and ->+< if (ops[loop_start + 1].kind == BfOpKind.DEC_DATA && ops[loop_start + 3].kind == BfOpKind.INC_DATA && ops[loop_start + 1].argument == 1 && ops[loop_start + 3].argument == 1) { if (ops[loop_start + 2].kind == BfOpKind.INC_PTR && ops[loop_start + 4].kind == BfOpKind.DEC_PTR && ops[loop_start + 2].argument == ops[loop_start + 4].argument) { new_ops.Add(new BfOp(BfOpKind.LOOP_MOVE_DATA, ops[loop_start + 2].argument)); } else if (ops[loop_start + 2].kind == BfOpKind.DEC_PTR && ops[loop_start + 4].kind == BfOpKind.INC_PTR && ops[loop_start + 2].argument == ops[loop_start + 4].argument) { new_ops.Add(new BfOp(BfOpKind.LOOP_MOVE_DATA, -ops[loop_start + 2].argument)); } } } return(new_ops); }
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); }