//////////////////////////////////////////////////////////////////////////////// // // Microsoft .Net IL Target Code Generator for Elisa Compiler // // Author: Neil Johnson, University of Cambridge Computer Laboratory // email: me@njohnson.co.uk // // Notes: This class is derived from the CodeGen abstract class, so make sure // _all_ methods are overridden. // This is/was the first code generator, so there are bound to be some // .Net infuences on the code gen. API. But I don't think it will // take too much effort to port to, say, JVM bytecodes. // // Much of the information in this module came from: // "Inside Microsoft .Net Assembler" // by Serge Lidin (Microsoft Press) // // History: // // 0.1 17-Apr-2004 First version // //////////////////////////////////////////////////////////////////////////////// using System; using System.IO; using System.Collections; public class DotNetCodeGen : CodeGen { static private string TAB = " "; private uint currstack; private uint maxstack; private uint SP; //////////////////////////////////////////////////////////////////////////// // Constructor // Requires information about the src file, then calls the base // constructor. //////////////////////////////////////////////////////////////////////////// public DotNetCodeGen( FileInfo srcFile ) : base(srcFile) { Console.WriteLine( "Target: .Net Target Code Generator" ); } //////////////////////////////////////////////////////////////////////////// // Open // Opens the target file, with extension ".il" // Then emits boilerplate stuff to setup the .Net file //////////////////////////////////////////////////////////////////////////// override public void Open() { cgStream = new StreamWriter( cgFileName + ".il", false ); comment( "Generated by Elisa Experimental Compiler" ); comment( "Source: " + cgFileName + ".el" ); comment( "Run: " + DateTime.Now.ToString() + "\n" ); directive( 0, ".assembly extern mscorlib\n{\n}\n" ); directive( 0, ".assembly EL_" + cgBaseName + "\n{\n}\n\n.module " + cgBaseName + ".exe\n" ); cgBaseName = "EL_" + cgBaseName; directive( 0, ".class private auto ansi beforefieldinit " + cgBaseName + " extends [mscorlib]System.Object" ); directive( 0, "{" ); } //////////////////////////////////////////////////////////////////////////// // Close() // Closes the scope opened by Open(), then flushes and closes the target // file. //////////////////////////////////////////////////////////////////////////// override public void Close() { // Close this target file directive( 0, "}" ); cgStream.Flush(); cgStream.Close(); } //////////////////////////////////////////////////////////////////////////// // Globals // Emits globals and a constructor //////////////////////////////////////////////////////////////////////////// override public void Globals( ArrayList g ) { // Emit all globals for ( int i = 0; i < g.Count; i++ ) { Symbol s = (Symbol)g[i]; if ( s.Type.Ty == Type.T.ARRAY ) directive( 1, ".field private static int32[] " + s.xName ); else directive( 1, ".field private static int32 " + s.xName ); } // Emit default constructor, that also serves to allocate memory // for globals directive( 1, ".method public hidebysig specialname rtspecialname instance void .ctor() cil managed" ); directive( 1, "{" ); maxstack = 1; for ( int i = 0; i < g.Count; i++ ) { Type ty = ((Symbol)g[i]).Type; if ( ty.Ty == Type.T.ARRAY && ty.Size > 0 ) { emit_ldarg( 0 ); emit_iconst( ty.Size ); emitinsn( "newarr [mscorlib]System.Int32" ); emit_stglobal( "int32[] " + cgBaseName + "::" + ((Symbol)g[i]).xName ); maxstack = 2; } } emit_ldarg( 0 ); emit_call( "instance void [mscorlib]System.Object::.ctor", null ); emit_ret(); directive( 2, ".maxstack " + maxstack ); directive( 1, "}" ); } //////////////////////////////////////////////////////////////////////////// // OpenFunction // Opens up a function frame, and optionally sets the entry point if the // function is called "Main". //////////////////////////////////////////////////////////////////////////// override public void openFunction( Symbol func, ArrayList plist ) { // Emit function signature String s = ".method public hidebysig static int32"; if ( func.Type.Of.Ty == Type.T.ARRAY ) s += "[]"; directive( 1, s + " " + func.xName + "(" ); if ( plist != null ) { for ( int pi = 0; pi < plist.Count; ++pi ) { Symbol param = (Symbol)plist[pi]; s = ""; if ( pi > 0 ) s += ", "; s += "int32"; if ( param.Type.Ty == Type.T.ARRAY ) s += "[]"; directive( 3, " " + s ); //+ " " + param.Name ); } } directive( 2, ") cil managed" ); directive( 1, "{" ); // SPECIAL CASE: if the function name is "Main" then mark this // as the entry point if ( func.Name == "Main" ) directive( 2, ".entrypoint" ); // SP0 is used as a temp location for this backend SP = 1; } //////////////////////////////////////////////////////////////////////////// // CloseFunction // Closes the current function frame, emitting the maxstack directive // and any locals for initialisation. //////////////////////////////////////////////////////////////////////////// override public void closeFunction( ArrayList locals ) { directive( 2, ".maxstack " + maxstack ); // If there are any locals, emit a list of init'ed // locals. Give them the name "V_n", where n is // their stack offset. directive( 2, ".locals init ( " ); locals.Reverse(); locals.Add( new Symbol( "__stelemtemp__", new Type() ) ); locals.Reverse(); for( int i = 0; i < locals.Count; i++ ) { String s = "int32"; if ( ((Symbol)locals[i]).Type.Ty == Type.T.ARRAY ) s += "[]"; s += " V_" + ((Symbol)locals[i]).Offset; if ( (i+1) < locals.Count ) s += ","; directive( 3, s ); } directive( 2, ")" ); directive( 1, "}" ); // close function body } //////////////////////////////////////////////////////////////////////////// // BaseName // Accessor to cgBaseName property (read-only) //////////////////////////////////////////////////////////////////////////// override public string BaseName { get { return cgBaseName; } } private string mklabel( uint l ) { return String.Format( "IL_{0:x4}", l ); } // Directives ////////////////////////////////////////////////////////////// override public void emit_label( uint l ) { cgStream.WriteLine( TAB + mklabel( l ) + ":" ); } override public void directive( uint d, string s ) { for ( int i = 0; i < d; ++i ) cgStream.Write( TAB ); cgStream.WriteLine( s ); } override public void comment( string s ) { cgStream.WriteLine( "// " + s ); } //////////////////////////////////////////////////////////////////////////// // // Instructions // //////////////////////////////////////////////////////////////////////////// // Helper methods ////////////////////////////////////////////////////////// private void emitinsn( string insn ) { cgStream.WriteLine( TAB + TAB + insn ); } private void pushstack() { currstack++; maxstack = maxstack > currstack ? maxstack : currstack; } private void popstack() { currstack--; } // Constants /////////////////////////////////////////////////////////////// override public void emit_iconst( int c ) { if ( c == -1 ) emitinsn( "ldc.i4.m1" ); else if ( c >= 0 && c <= 8 ) emitinsn( "ldc.i4." + c ); else emitinsn( "ldc.i4 " + c ); pushstack(); } // Branches //////////////////////////////////////////////////////////////// override public void emit_brnz( uint l ) { emitinsn( "brtrue " + mklabel( l ) ); popstack(); } override public void emit_brz( uint l ) { emitinsn( "brfalse " + mklabel( l ) ); popstack(); } override public void emit_branch( uint l ) { emitinsn( "br " + mklabel( l ) ); } // Relational ////////////////////////////////////////////////////////////// override public void emit_beq( uint l ) { emitinsn( "beq " + mklabel( l ) ); popstack(); popstack(); } override public void emit_bne( uint l ) { emitinsn( "bne.un " + mklabel( l ) ); popstack(); popstack(); } override public void emit_bgt( uint l ) { emitinsn( "bgt " + mklabel( l ) ); popstack(); popstack(); } override public void emit_bge( uint l ) { emitinsn( "bge " + mklabel( l ) ); popstack(); popstack(); } override public void emit_blt( uint l ) { emitinsn( "blt " + mklabel( l ) ); popstack(); popstack(); } override public void emit_ble( uint l ) { emitinsn( "ble " + mklabel( l ) ); popstack(); popstack(); } // Bitwise ///////////////////////////////////////////////////////////////// override public void emit_bor() { emitinsn( "or" ); popstack(); } override public void emit_bxor() { emitinsn( "xor" ); popstack(); } override public void emit_band() { emitinsn( "and" ); popstack(); } override public void emit_not() { emitinsn( "not" ); } // Arithmetic ////////////////////////////////////////////////////////////// override public void emit_add() { emitinsn( "add" ); popstack(); } override public void emit_sub() { emitinsn( "sub" ); popstack(); } override public void emit_mul() { emitinsn( "mul" ); popstack(); } override public void emit_div() { emitinsn( "div" ); popstack(); } override public void emit_mod() { emitinsn( "rem" ); popstack(); } override public void emit_neg() { emitinsn( "neg" ); } // Shifts ////////////////////////////////////////////////////////////////// override public void emit_shl() { emitinsn( "shl" ); popstack(); } override public void emit_shr() { emitinsn( "shr" ); popstack(); } // Loads and stores //////////////////////////////////////////////////////// override public void emit_pop() { emitinsn( "pop" ); popstack(); } override public void emit_dup() { emitinsn( "dup" ); pushstack(); } override public void emit_ldglobal( string s ) { emitinsn( "ldsfld " + s ); pushstack(); } override public void emit_stglobal( string s ) { emitinsn( "stsfld " + s ); popstack(); } override public void emit_ldarg( uint i ) { if ( i < 4 ) emitinsn( "ldarg." + i ); else emitinsn( "ldarg " + i ); pushstack(); } override public void emit_starg( uint i ) { emitinsn( "starg " + i ); popstack(); } override public void emit_stloc( uint i ) { if ( i < 4 ) emitinsn( "stloc." + i ); else emitinsn( "stloc " + i ); popstack(); } override public void emit_ldloc( uint i ) { emitinsn( "ldloc " + i ); pushstack(); } override public void emit_ldelem() { emitinsn( "ldelem.i4" ); pushstack(); } override public void emit_stelem() { emit_stloc( 0 ); emitinsn( "stelem.i4" ); emit_ldloc( 0 ); popstack(); } // Call and return ///////////////////////////////////////////////////////// override public void emit_call( string t, ArrayList args ) { String s = "call " + t + "("; if ( args != null ) { for( int argi = 0; argi < args.Count; argi++ ) { Type ty = (Type)args[argi]; s += "int32"; if ( ty.Ty == Type.T.ARRAY ) s += "[]"; if ( argi < args.Count - 1 ) s += ","; s += " "; popstack(); // callee pops args from stack } } s += ")"; emitinsn( s ); pushstack(); // callee pushes return value on stack } override public void emit_ret() { emitinsn( "ret" ); } // Announce locals to the back end ///////////////////////////////////////// override public void announceLocal( Symbol s ) { s.Offset = SP++; Type ty = s.Type; if ( ty.Ty == Type.T.ARRAY && ty.Size > 0 ) { emit_iconst( ty.Size ); emitinsn( "newarr [mscorlib]System.Int32" ); emit_stloc( s.Offset ); } } // Custom API calls :-) //////////////////////////////////////////////////// override public void PUT() { emitinsn( "call void [mscorlib]System.Console::Write(char)" ); emit_iconst( 0 ); } override public void GET() { emitinsn( "call int32 [mscorlib]System.Console::Read()" ); pushstack(); } /////////////////////////////////////////////////////////////////////////// }