Exemplo n.º 1
0
    // 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);
    }
Exemplo n.º 2
0
    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);
    }