namespace FlexSwitch3 { using System; using Benchmark; /* Jump between basic block inside cases of flexswitches ----------------------------------------------------- Each basic block in a function and in its "childs" (i.e. the cases generated by flexswitches) is numbered with an unique ID. Arguments across non-local links are passed by passing an instance of the InputArgs class; this class contains enough fields to be able to store every possible set of inputargs needed by the function. At the beginning of the function, InputArgs is instantiated, and the object args is passed around when calling into flexswitches. Before calling a flexswitch case, you need to setup args accordingly to the inputargs expected by the block you are jumping in. The same must be done before returning from a flexswitch case; moreover, the return value of the flexswitch case is an int which specifies the ID of the block we want to jump to. Whenever we want to do a non-local jump (e.g., after the call to a flexswitch case), we set args as the expected inputargs and the jumpto variable to the ID of the target block, then you jump to "dispatch_jump". If the target block happens to be inside a flexswitch case, "dispatch_jump" will take care of locating the right delegate and call it passing the ID of the block as the first argument (XXX: not implemented yet). */ delegate int SwitchCaseFunc(int block, InputArgs args); delegate int SwitchDefaultCase(FlexSwitch obj, object value); // this class will be automatically generated by the JIT backend class InputArgs { public int int0; } class FlexSwitch { struct SwitchCase { public object value; public SwitchCaseFunc func; public SwitchCase(object value, SwitchCaseFunc func) { this.value = value; this.func = func; } } public int numcases = 0; SwitchCase[] cases = new SwitchCase[2]; public SwitchDefaultCase defaultcase; private void grow() { int newsize = cases.Length * 2; SwitchCase[] newarray = new SwitchCase[newsize]; Array.Copy(cases, newarray, cases.Length); cases = newarray; } public void addcase(object v, SwitchCaseFunc c) { if (numcases >= cases.Length) grow(); cases[numcases] = new SwitchCase(v, c); numcases++; } public int execute(object v, InputArgs args) { foreach(SwitchCase c in cases) if (c.value == v) return c.func(-1, args); return defaultcase(this, v); } } class DynamicGeneratedCode { // these functions play the role of the dynamic generated code public static int add_10(int block, InputArgs args) { int x = args.int0; // inputargs: [x] int result = x + 10; args.int0 = result; // outputargs: [accumulator] return Test.NEXT; } public static int mul_2(int block, InputArgs args) { int x = args.int0; // inputargs: [x] int result = x * 2; args.int0 = result; // outputargs: [accumulator] return Test.NEXT; } public static int sub_3(int block, InputArgs args) { int x = args.int0; // inputargs: [x] int result = x - 3; args.int0 = result; // outputargs: [accumulator] return Test.NEXT; } public static int defaultcase(FlexSwitch fs, object v) { ll_continue_compilation(fs, v); return Test.EXEC; } public static void ll_continue_compilation(FlexSwitch fs, object v) { //Console.WriteLine("ll_continue_compilation..."); if (v == OpCodes.v_add_10) fs.addcase(v, new SwitchCaseFunc(add_10)); else if (v == OpCodes.v_mul_2) fs.addcase(v, new SwitchCaseFunc(mul_2)); else if (v == OpCodes.v_sub_3) fs.addcase(v, new SwitchCaseFunc(sub_3)); else throw new Exception("error in ll_continue_compilation"); } } class Test { // block IDs public const int FETCH = 0; public const int EXEC = 1; public const int NEXT = 2; public static int interpret(object[] bytecode) { InputArgs args = new InputArgs(); FlexSwitch fs = new FlexSwitch(); fs.defaultcase = new SwitchDefaultCase(DynamicGeneratedCode.defaultcase); int accumulator = 0; object opcode = null; int i=0; int jumpto = -1; goto fetch; // begin main loop fetch: // inputargs: [] opcode = bytecode[i]; exec: // inputargs: [] jumpto = fs.execute(opcode, args); goto dispatch_jump; next: // inputargs: [accumulator] accumulator = args.int0; //Console.WriteLine("accumulator = {0}", accumulator); i++; if (i >= bytecode.Length) return accumulator; goto fetch; // end main loop dispatch_jump: if (jumpto == FETCH) goto fetch; else if (jumpto == EXEC) goto exec; else if (jumpto == NEXT) goto next; else throw new Exception("error in dispatch_jump"); } } }