using System; using System.Reflection; using System.Reflection.Emit; namespace MicroCompiler { public class CodeGen { /// /// metadata builder for the program assembly /// private readonly AssemblyBuilder fAssembly; /// /// The parser is only used to print errors. /// private readonly Parser fError; /// /// metadata builder for the program module /// private readonly ModuleBuilder fModule; /// /// metadata builder for the main class /// private readonly TypeBuilder fProgram; /// /// IL stream of currently compiled method /// private ILGenerator fIl; /// /// Creates a code generator. /// /// name for the assembly /// only used for error reporting public CodeGen(string target, Parser err) { fError = err; AssemblyName assemblyName = new AssemblyName(); assemblyName.Name = target; AppDomain cd = AppDomain.CurrentDomain; fAssembly = cd.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save); fModule = fAssembly.DefineDynamicModule(target + "Module", target + ".exe", true); fProgram = fModule.DefineType(target + "$", TypeAttributes.Class | TypeAttributes.Public); } /// /// Creates the required metadata builder objects for the given Symbol. /// Call this after you inserted your Symbol into the symbol table. /// /// the symbol to create metadata for public void CreateMetadata(Symbol sym) { switch (sym.Kind) { case SymbolKind.Local: fIl.DeclareLocal(sym.Type); break; case SymbolKind.Func: // build argument list Type[] args = new Type[sym.NArgs]; for (int i = 0; i < sym.NArgs; ++i) { Symbol arg = sym.Locals[i]; args[arg.Adr] = arg.Type; } sym.Meth = fProgram.DefineMethod(sym.Name, MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, sym.Type, args); fIl = sym.Meth.GetILGenerator(); if ("main".Equals(sym.Name)) { fAssembly.SetEntryPoint(sym.Meth, PEFileKinds.ConsoleApplication); } break; default: fError.SemErr("Unexpected symbol kind: " + sym.Kind); break; } } // ---------- instruction generation /// /// Load the operand x onto the expression stack. /// /// item to load public void Load(Item x) { switch (x.Kind) { case ItemKind.Const: LoadConst(x.Val); break; case ItemKind.Arg: LoadArg(x.Adr); break; case ItemKind.Local: LoadLocal(x.Adr); break; case ItemKind.Stack: // nothing to do (already loaded) break; default: fError.SemErr("Compiler error in Code.load, unexpected item kind"); break; } x.Kind = ItemKind.Stack; } /// /// Load the local variable on the given address /// /// address of the argument to load private void LoadLocal(int adr) { switch (adr) { case 0: fIl.Emit(OpCodes.Ldloc_0); break; case 1: fIl.Emit(OpCodes.Ldloc_1); break; case 2: fIl.Emit(OpCodes.Ldloc_2); break; case 3: fIl.Emit(OpCodes.Ldloc_3); break; default: fIl.Emit(OpCodes.Ldloc, adr); break; } } /// /// Load the argument on the given address /// /// address of the argument to load private void LoadArg(int adr) { switch (adr) { case 0: fIl.Emit(OpCodes.Ldarg_0); break; case 1: fIl.Emit(OpCodes.Ldarg_1); break; case 2: fIl.Emit(OpCodes.Ldarg_2); break; case 3: fIl.Emit(OpCodes.Ldarg_3); break; default: fIl.Emit(OpCodes.Ldarg, adr); break; } } /// /// Load an integer constant onto the expression stack. /// /// integer constant to load private void LoadConst(int n) { switch (n) { case -1: fIl.Emit(OpCodes.Ldc_I4_M1); break; case 0: fIl.Emit(OpCodes.Ldc_I4_0); break; case 1: fIl.Emit(OpCodes.Ldc_I4_1); break; case 2: fIl.Emit(OpCodes.Ldc_I4_2); break; case 3: fIl.Emit(OpCodes.Ldc_I4_3); break; case 4: fIl.Emit(OpCodes.Ldc_I4_4); break; case 5: fIl.Emit(OpCodes.Ldc_I4_5); break; case 6: fIl.Emit(OpCodes.Ldc_I4_6); break; case 7: fIl.Emit(OpCodes.Ldc_I4_7); break; case 8: fIl.Emit(OpCodes.Ldc_I4_8); break; default: fIl.Emit(OpCodes.Ldc_I4, n); break; } } /// /// Generate an assignment x = y. /// /// target of the assignment /// value to assign public void Assign(Item x, Item y) { // make sure y is loaded (if already on stack, nothing will happen) Load(y); switch (x.Kind) { case ItemKind.Arg: fIl.Emit(OpCodes.Starg, x.Adr); break; case ItemKind.Local: StoreLocal(x.Adr); break; default: fError.SemErr( "Left-hand side of an assignment must be a variable"); break; } } /// /// Store the top stack element into a local variable. /// /// the address of the target local variable private void StoreLocal(int adr) { switch (adr) { case 0: fIl.Emit(OpCodes.Stloc_0); break; case 1: fIl.Emit(OpCodes.Stloc_1); break; case 2: fIl.Emit(OpCodes.Stloc_2); break; case 3: fIl.Emit(OpCodes.Stloc_3); break; default: fIl.Emit(OpCodes.Stloc, adr); break; } } /// /// Generate an unconditional jump to the given label /// /// target label public void Jump(Label label) { fIl.Emit(OpCodes.Br, label); } /// /// True Jump. Generates conditional branch instruction. /// /// condition item public void TJump(Item x) { if (x.Kind != ItemKind.Cond) { throw new ArgumentException("item must be of kind condition"); } fIl.Emit(x.Relop, x.Label); } /// /// Emits the given OpCode to the currently open method. /// /// OpCode to emit public void Emit(OpCode opcode) { fIl.Emit(opcode); } /// /// Emits a call to the given method. /// /// Method to call public void EmitCall(MethodInfo meth) { fIl.EmitCall(OpCodes.Call, meth, null); } /// /// Emits a call to the given symbol. /// /// /// The function to call, the symbol must be /// of kind . /// public void EmitCall(Symbol function) { if (function.Kind != SymbolKind.Func) { fError.SemErr("Illegal function call to: " + function.Name + "(no function)"); return; } EmitCall(function.Meth); } /// /// Create a stack item with the given type /// /// type of the item to create /// the newly created item public Item CreateStackItem(Type type) { Item item = new Item(); item.Kind = ItemKind.Stack; item.Type = type; return item; } /// /// Creates a constant item. /// /// value of the item /// integer type (int or char) /// the newly created item public Item CreateConstItem(int val, Type type) { Item item = new Item(); item.Kind = ItemKind.Const; item.Val = val; item.Type = type; return item; } /// /// Creates an item for the given symbol /// /// the symbol describing to item to create /// the newly created item public Item CreateItem(Symbol sym) { Item item = new Item(); item.Type = sym.Type; item.Sym = sym; switch (sym.Kind) { case SymbolKind.Arg: item.Kind = ItemKind.Arg; item.Adr = sym.Adr; break; case SymbolKind.Local: item.Kind = ItemKind.Local; item.Adr = sym.Adr; break; case SymbolKind.Func: item.Kind = ItemKind.Func; break; default: fError.SemErr( "Cannot create code item for this kind of symbol table object"); break; } return item; } /// /// Creates an relational item. /// /// relational operator to use /// the newly created item public Item CreateRelItem(OpCode relop) { Item item = new Item(); item.Kind = ItemKind.Cond; item.Relop = relop; item.Label = fIl.DefineLabel(); return item; } /// /// Create a new undefined label. /// /// /// the newly created public Label CreateLabel() { return fIl.DefineLabel(); } /// /// Defines the given /// /// the label to define public void MarkLabel(Label label) { fIl.MarkLabel(label); } /// /// Generate an executable .NET-PE-File. /// public void WritePEFile() { fProgram.CreateType(); if (fAssembly.EntryPoint == null) { fError.SemErr("Missing 'main' function"); } fAssembly.Save(fAssembly.GetName().Name + ".exe"); } } /// /// Different item kinds. /// public enum ItemKind { /// /// Constant /// Const, /// /// Function argument /// Arg, /// /// Local variable /// Local, /// /// Stack item /// Stack, /// /// Function /// Func, /// /// Conditional item /// Cond } /// /// Simple data class. /// An item stores the attributes of an operand /// during code generation. /// public class Item { private ItemKind kind; private Type type; private int val; private int adr; private OpCode relop; private Symbol sym; private Label label; /// /// Any item: kind of the item. /// public ItemKind Kind { get { return kind; } set { kind = value; } } /// /// Any item: the system type of the item. /// public Type Type { get { return type; } set { type = value; } } /// /// Const: value /// public int Val { get { return val; } set { val = value; } } /// /// Arg, Local: offset /// public int Adr { get { return adr; } set { adr = value; } } /// /// Cond: OpCode of relational operator /// public OpCode Relop { get { return relop; } set { relop = value; } } /// /// Field, Func: node from symbol table /// public Symbol Sym { get { return sym; } set { sym = value; } } /// /// Cond: target /// public Label Label { get { return label; } set { label = value; } } } }