【问题标题】:Generate IL to decrease counter in for loop生成 IL 以减少 for 循环中的计数器
【发布时间】:2015-11-01 15:19:34
【问题描述】:

我正在尝试使用 Good For Nothing (GFN) 编译器,试图让它做一些不同的事情。我正在使用这里的代码:https://github.com/johandanforth/good-for-nothing-compiler

常规 GFN for 循环:

var x = 0;
for x = 0 to 3 do
    print x;
end;

这个 for 循环总是递增的。我想添加减量功能:

var x = 0;
for x = 3 to 0 down //up for increment (works same as do)
    print x;
end;

我苦苦挣扎的主要领域是 CodeGen。

ForLoop 类:

public class ForLoop : Stmt
{
    public Stmt Body { get; set; }
    public Expr From { get; set; }
    public string Ident { get; set; }
    public Expr To { get; set; }
    public ArithOp Type { get; set; }
}

ArithOp 枚举:

public enum ArithOp
{
    Add,
    Sub,
    Mul,
    Div,
    Up,
    Down
}

CodeGen.cs 内部:

private void GenStmt(Stmt stmt)
{
   //code omitted for brevity 

    else if (stmt is ForLoop)
            {
                // example: 
                // for x = 0 to 100 up
                //    "hello";
                // end;

                // x = 0
                var forLoop = (ForLoop)stmt;
                var assign = new Assign { Ident = forLoop.Ident, Expr = forLoop.From };
                GenStmt(assign);
                // jump to the test
                var test = _il.DefineLabel();
                _il.Emit(OpCodes.Br, test);

                // statements in the body of the for loop
                var body = _il.DefineLabel();
                _il.MarkLabel(body);
                GenStmt(forLoop.Body);

                // to (increment/decrement the value of x)
                _il.Emit(OpCodes.Ldloc, SymbolTable[forLoop.Ident]);
                _il.Emit(OpCodes.Ldc_I4, 1);
                _il.Emit(forLoop.Type == ArithOp.Up ? OpCodes.Add : OpCodes.Sub);
                GenerateStoreFromStack(forLoop.Ident, typeof(int));

                // **test** does x equal 100? (do the test)
                _il.MarkLabel(test);
                _il.Emit(OpCodes.Ldloc, SymbolTable[forLoop.Ident]);
                GenerateLoadToStackForExpr(forLoop.To, typeof(int));
                _il.Emit(OpCodes.Blt, body);
            }
}

private void GenerateStoreFromStack(string name, Type type)
    {
        if (!SymbolTable.ContainsKey(name))
            throw new Exception("undeclared variable '" + name + "'");

        var locb = SymbolTable[name];
        var localType = locb.LocalType;

        if (localType != type)
            throw new Exception(string.Format("'{0}' is of type {1} but attempted to store value of type {2}", name,
                localType == null ? "<unknown>" : localType.Name, type.Name));

        _il.Emit(OpCodes.Stloc, SymbolTable[name]);
    }

    private void GenerateLoadToStackForExpr(Expr expr, Type expectedType)
    {
        Type deliveredType;

        if (expr is StringLiteral)
        {
            deliveredType = typeof(string);
            _il.Emit(OpCodes.Ldstr, ((StringLiteral)expr).Value);
        }
        else if (expr is IntLiteral)
        {
            deliveredType = typeof(int);
            _il.Emit(OpCodes.Ldc_I4, ((IntLiteral)expr).Value);
        }
        else if (expr is Variable)
        {
            var ident = ((Variable)expr).Ident;
            deliveredType = expr.GetType();

            if (!SymbolTable.ContainsKey(ident))
            {
                throw new Exception("undeclared variable '" + ident + "'");
            }

            _il.Emit(OpCodes.Ldloc, SymbolTable[ident]);
        }
        else if (expr is ArithExpr)
        {
            var arithExpr = (ArithExpr)expr;
            var left = arithExpr.Left;
            var right = arithExpr.Right;
            deliveredType = expr.GetType();

            GenerateLoadToStackForExpr(left, expectedType);
            GenerateLoadToStackForExpr(right, expectedType);
            switch (arithExpr.Op)
            {
                case ArithOp.Add:
                    _il.Emit(OpCodes.Add);
                    break;
                case ArithOp.Sub:
                    _il.Emit(OpCodes.Sub);
                    break;
                case ArithOp.Mul:
                    _il.Emit(OpCodes.Mul);
                    break;
                case ArithOp.Div:
                    _il.Emit(OpCodes.Div);
                    break;
                default:
                    throw new NotImplementedException("Don't know how to generate il load code for " + arithExpr.Op +
                                                      " yet!");
            }
        }
        else
        {
            throw new Exception("don't know how to generate " + expr.GetType().Name);
        }

        if (deliveredType == expectedType) return;

        if (deliveredType != typeof (int) || expectedType != typeof (string))
            throw new Exception("can't coerce a " + deliveredType.Name + " to a " + expectedType.Name);

        _il.Emit(OpCodes.Box, typeof (int));
        _il.Emit(OpCodes.Callvirt, typeof (object).GetMethod("ToString"));
    }

这当前会生成一个不执行任何操作的 .exe。我看过的资料可以帮助解决这个问题:http://www.codeproject.com/Articles/3778/Introduction-to-IL-Assembly-Language#Loophttps://ninjaferret.wordpress.com/2009/12/23/msil-4-for-loops/。我只是不太了解IL

【问题讨论】:

  • 这看起来像 for i = 0 to 3 do 运行了 3 次,分别是 i = 0i = 1i = 2。这是正确的,还是实际上运行了四次?如果它是正确的,你期望down 版本做什么?它应该运行i = 3i = 2i = 1,还是运行i = 2i = 1i = 0
  • @hvd 它运行了三次,所以0 1 2up 的输出(与do 相同)。对于down,我希望它输出3 2 1
  • @HansPassant 是否意味着 down 我需要使用 OpCodes.Bgt?我确实试了一下,但生成的 .exe 仍然没有输出任何内容
  • @HansPassant 忽略最后一条评论。我的代码有错误,OpCodes.Bgt 有效!

标签: c# compiler-construction code-generation cil reflection.emit


【解决方案1】:

在 C# 代码中执行此操作以获得洞察力:

   for (int ix = 0; ix < 3; ++ix)     // up
   for (int ix = 3; ix > 0; --ix)     // down

两个 变化,你得到了 inc/dec 运算符的区别。您没有得到循环终止条件的变化。这使得这个错误:

   _il.Emit(OpCodes.Blt, body);

您必须将其反转为 Opcodes.Bgt

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-22
    • 2011-10-29
    相关资源
    最近更新 更多