// File RTCG6.cs --- runtime code generation for mathematical functions // sestoft@itu.dk * 2002 // Could use this in numeric integration packages or graph drawing // packages etc, where the user would enter a formula interactively // which would then be parsed and RTCGed to CIL code. // The runtime generated code is 3 times faster than the interpreted // abstract syntax, even for the very simple expression f1. using System; using System.Reflection; using System.Reflection.Emit; class RTCG6 { public static void Main(String [] args) { int count = int.Parse(args[0]); // Representing fn x => 7 / x + sin(x) Expr x = new Arg(0); Fun f1 = new Fun(new Cst(7) / x + new Call1("Sin", x)); AssemblyName assemblyName = new AssemblyName(); assemblyName.Name = "myassembly"; // Build: run-only assembly AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); // Build: module mymodule ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("mymodule"); // Build: public class MyClass { ... } TypeBuilder typeBuilder = moduleBuilder.DefineType("MyClass", TypeAttributes.Class | TypeAttributes.Public, typeof(Object)); { // Build: public static double MyEval(double x) { ... } MethodBuilder methodBuilder = typeBuilder.DefineMethod("MyEval", MethodAttributes.Static | MethodAttributes.Public, typeof(double), new Type[] { typeof(double) }); // Obtain an IL generator to build the method body ILGenerator ilg = methodBuilder.GetILGenerator(); f1.Gen(ilg); Console.WriteLine(ilg); } Type ty = typeBuilder.CreateType(); double res = 0.0; Timer t1 = new Timer(); D2D eval = (D2D)Delegate.CreateDelegate(typeof(D2D), ty.GetMethod("MyEval")); for (int i=count; i>0; i--) res = eval(1.0); Console.WriteLine("Delegate call to specialized generated method: " + t1.Check() + " sec"); Console.WriteLine(res); Timer t2 = new Timer(); for (int i=count; i>0; i--) res = f1.Eval(1.0); Console.WriteLine("Interpretation of expression abstract syntax: " + t2.Check() + " sec"); Console.WriteLine(res); } } public delegate double D2D(double x); // Abstract syntax of floating-point expressions class Fun { private readonly Expr body; public Fun(Expr body) { this.body = body; } public double Eval(double x) { return body.Eval(x); } public void Gen(ILGenerator ilg) { body.Gen(ilg); ilg.Emit(OpCodes.Ret); } } abstract class Expr { public abstract void Gen(ILGenerator ilg); public abstract double Eval(double x); public static Expr operator +(Expr e1, Expr e2) { return new Add(e1, e2); } public static Expr operator -(Expr e1, Expr e2) { return new Sub(e1, e2); } public static Expr operator *(Expr e1, Expr e2) { return new Mul(e1, e2); } public static Expr operator /(Expr e1, Expr e2) { return new Div(e1, e2); } } class Cst : Expr { private readonly double d; public Cst(double d) { this.d = d; } public override void Gen(ILGenerator ilg) { ilg.Emit(OpCodes.Ldc_R8, d); } public override double Eval(double x) { return d; } } abstract class Binop : Expr { protected readonly Expr e1, e2; private readonly OpCode opc; public Binop(Expr e1, Expr e2, OpCode opc) { this.e1 = e1; this.e2 = e2; this.opc = opc; } public override void Gen(ILGenerator ilg) { e1.Gen(ilg); e2.Gen(ilg); ilg.Emit(opc); } } class Add : Binop { public Add(Expr e1, Expr e2) : base(e1, e2, OpCodes.Add) { } public override double Eval(double x) { return e1.Eval(x) + e2.Eval(x); } } class Sub : Binop { public Sub(Expr e1, Expr e2) : base(e1, e2, OpCodes.Sub) { } public override double Eval(double x) { return e1.Eval(x) - e2.Eval(x); } } class Mul : Binop { public Mul(Expr e1, Expr e2) : base(e1, e2, OpCodes.Mul) { } public override double Eval(double x) { return e1.Eval(x) * e2.Eval(x); } } class Div : Binop { public Div(Expr e1, Expr e2) : base(e1, e2, OpCodes.Div) { } public override double Eval(double x) { return e1.Eval(x) / e2.Eval(x); } } class Call1 : Expr { private readonly D2D dg; private readonly Expr arg1; public Call1(D2D dg, Expr arg1) { this.dg = dg; this.arg1 = arg1; } public Call1(String s, Expr arg1) { this.dg = (D2D)Delegate.CreateDelegate(typeof(D2D), typeof(Math).GetMethod(s)); this.arg1 = arg1; } public override void Gen(ILGenerator ilg) { arg1.Gen(ilg); ilg.EmitCall(OpCodes.Call, dg.Method, null); } public override double Eval(double x) { return dg(arg1.Eval(x)); } } class Arg : Expr { private readonly int i; public Arg(int i) { this.i = i; } public override void Gen(ILGenerator ilg) { ilg.Emit(OpCodes.Ldarg, i); } public override double Eval(double x) { return x; } } // Crude timing utility ---------------------------------------- public class Timer { private DateTime start; public Timer() { start = DateTime.Now; } public double Check() { TimeSpan dur = DateTime.Now - start; return dur.TotalSeconds; } }