生成 IL 以减少 for 循环中的计数器
Generate IL to decrease counter in for loop
我正在研究 Good For Nothing (GFN) 编译器,试图让它做一些不同的事情。我正在使用这里的代码:https://github.com/johandanforth/good-for-nothing-compiler
循环的常规 GFN:
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 class:
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#Loop and https://ninjaferret.wordpress.com/2009/12/23/msil-4-for-loops/。我只是不太了解 IL
在 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
我正在研究 Good For Nothing (GFN) 编译器,试图让它做一些不同的事情。我正在使用这里的代码:https://github.com/johandanforth/good-for-nothing-compiler
循环的常规 GFN:
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 class:
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#Loop and https://ninjaferret.wordpress.com/2009/12/23/msil-4-for-loops/。我只是不太了解 IL
在 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