MSDN Magazine - February 2008 - (Page 71) Figure 12 Store Expressions to a Variable private void Store(string name, Type type) { if (this.symbolTable.ContainsKey(name)) { Emit.LocalBuilder locb = this.symbolTable[name]; if (locb.LocalType == type) { this.il.Emit(Emit.OpCodes.Stloc, this.symbolTable[name]); } else { throw new Exception(“’” + name + “’ is of type “ + locb.LocalType.Name + “ but attempted to store value of type “ + type.Name); } the loop body is executed, the x variable is incremented, and the test is run again. The for loop code in Figure 13 generates the code required to perform the assignment and increment operations on the counter variable. It also uses the MarkLabel method on ILGenerator to generate labels that the branch instructions can branch to. Wrapping Up … Almost I’ve walked through the code base to a simple .NET compiler and explored some of the underlying theory. This article is intended to Figure 13 For Loop Code else if (stmt is ForLoop) { // example: // var x = 0; // for x = 0 to 100 do // print “hello”; // end; // x = 0 ForLoop forLoop = (ForLoop)stmt; Assign assign = new Assign(); assign.Ident = forLoop.Ident; assign.Expr = forLoop.From; this.GenStmt(assign); // jump to the test Emit.Label test = this.il.DefineLabel(); this.il.Emit(Emit.OpCodes.Br, test); // statements in the body of the for loop Emit.Label body = this.il.DefineLabel(); this.il.MarkLabel(body); this.GenStmt(forLoop.Body); // to (increment the value of x) this.il.Emit(Emit.OpCodes.Ldloc, this.symbolTable[forLoop.Ident]); this.il.Emit(Emit.OpCodes.Ldc_I4, 1); this.il.Emit(Emit.OpCodes.Add); this.Store(forLoop.Ident, typeof(int)); // **test** does x equal 100? (do the test) this.il.MarkLabel(test); this.il.Emit(Emit.OpCodes.Ldloc, this.symbolTable[forLoop.Ident]); this.GenExpr(forLoop.To, typeof(int)); this.il.Emit(Emit.OpCodes.Blt, body); } } else { throw new Exception(“undeclared variable ‘” + name + “’”); } The last section of code shown in Figure 11 deals with converting the expression type to the expected type (called type coercion). For instance, a type may need conversion in a call to the print method where an integer needs to be converted to a string so that print can be successful. Figure 12 demonstrates how variables are assigned expressions in the Store method. The name is looked up via the symbol table and the respective LocalBuilder is then passed to the stloc IL instruction. This simply pops the current expression from the stack and assigns it to the local variable. The code that is utilized to generate the IL for the Print AST node is interesting because it calls in to a BCL method. The expression is generated onto the stack, and the IL call instruction is used to call the System.Console.WriteLine method. Reflection is used to obtain the WriteLine method handle that is needed to pass to the call instruction: else if (stmt is Print) { this.GenExpr(((Print)stmt).Expr, typeof(string)); this.il.Emit(Emit.OpCodes.Call, typeof(System.Console).GetMethod(“WriteLine”, new Type[] { typeof(string) })); } } Figure 14 For Loop IL Code // for x = 0 IL_0006: ldc.i4 IL_000b: stloc.0 0x0 When a call to a method is performed, method arguments are popped off the stack last on, first in. In other words, the first argument of the method is the top stack item, the second argument the next item, and so on. The most complex code here is the code that generates IL for my Good for Nothing for loops (see Figure 13). It is quite similar to how commercial compilers would generate this kind of code. However, the best way to explain the for loop code is to look at the IL that is generated, which is shown in Figure 14. The IL code starts with the initial for loop counter assignment and immediately jumps to the for loop test using the IL instruction br (branch). Labels like the ones listed to the left of the IL code are used to let the runtime know where to branch to the next instruction. The test code checks to see if the variable x is less than 100 using the blt (branch if less than) instruction. If this is true, // jump to the test IL_000c: br IL_0023 // execute the loop body IL_0011: // increment the x variable by 1 IL_001b: ldloc.0 IL_001c: ldc.i4 0x1 IL_0021: add IL_0022: stloc.0 // TEST // load x, load 100, branch if // x is less than 100 IL_0023: ldloc.0 IL_0024: ldc.i4 0x64 IL_0029: blt IL_0011 .NET Compiler february2008 71
For optimal viewing of this digital publication, please enable JavaScript and then refresh the page. If you would like to try to load the digital publication without using Flash Player detection, please click here.