/// <summary> /// Sets the mem address to the given int (word) /// </summary> protected static void SetWordMemAddr(InternalVMData vmData, int address, int value) { vmData.Memory[address + 3] = (byte)(value >> 24); vmData.Memory[address + 2] = (byte)(value >> 16); vmData.Memory[address + 1] = (byte)(value >> 8); vmData.Memory[address] = (byte)(value); }
private readonly IDictionary <OperationCodes, InstructionEmiter> instructionEmiters; //The instruction emiters #endregion #region Constructors /// <summary> /// Creates a new JIT compiler /// </summary> /// <param name="virtualMachine">The virtual machine</param> public BaseJITCompiler(VirtualMachine virtualMachine) { this.virtualMachine = virtualMachine; this.vmData = this.virtualMachine.GetInternalData(); this.registerField = typeof(InternalVMData).GetField("Registers", BindingFlags.Instance | BindingFlags.Public); this.memoryField = typeof(InternalVMData).GetField("Memory", BindingFlags.Instance | BindingFlags.Public); this.convertToInt = typeof(BaseJITCompiler).GetMethod("ConvertToInt", BindingFlags.NonPublic | BindingFlags.Static); this.setWordMemAddr = typeof(BaseJITCompiler).GetMethod("SetWordMemAddr", BindingFlags.NonPublic | BindingFlags.Static); this.instructionEmiters = new Dictionary <OperationCodes, InstructionEmiter>(); #region Emiters #region Generators //Creates R-format emiters Func <OpCode, RFormatInstructionEmiter> rFormatEmiterGenerator = opCode => { return((i, inst, genData) => { var gen = genData.ILGenerator; //Push reg C this.EmitRegRef(gen, inst.RegisterC); //Load the value of reg A this.EmitRegRef(gen, inst.RegisterA); this.EmitLoadRef(gen); //Load the value of reg B this.EmitRegRef(gen, inst.RegisterB); this.EmitLoadRef(gen); //Apply the operation gen.Emit(opCode); //Store in reg C this.EmitSaveRef(gen); }); }; Func <OpCode, IFormatInstructionEmiter> iFormatEmiterGenerator = opCode => { return((i, inst, genData) => { var gen = genData.ILGenerator; //Push reg B this.EmitRegRef(gen, inst.RegisterB); //Load the value of reg A this.EmitRegRef(gen, inst.RegisterA); this.EmitLoadRef(gen); //Push the imm value gen.Emit(OpCodes.Ldc_I4, inst.SignedImmediate); //Apply the operation gen.Emit(opCode); //Store in reg B this.EmitSaveRef(gen); }); }; #endregion #region Arithmetic //The addi instruction this.AddIFormatEmiter(OperationCodes.Addi, iFormatEmiterGenerator(OpCodes.Add)); //The add instruction this.AddRFormatEmiter(OperationCodes.Add, OperationXCodes.Add, rFormatEmiterGenerator(OpCodes.Add)); //The sub instruction this.AddRFormatEmiter(OperationCodes.Sub, OperationXCodes.Sub, rFormatEmiterGenerator(OpCodes.Sub)); #endregion #region Logic //The and instruction this.AddRFormatEmiter(OperationCodes.And, OperationXCodes.And, rFormatEmiterGenerator(OpCodes.And)); //The andi instruction this.AddIFormatEmiter(OperationCodes.Andi, iFormatEmiterGenerator(OpCodes.And)); //The or instruction this.AddRFormatEmiter(OperationCodes.Or, OperationXCodes.Or, rFormatEmiterGenerator(OpCodes.Or)); //The ori instruction this.AddIFormatEmiter(OperationCodes.Ori, iFormatEmiterGenerator(OpCodes.Or)); //The xor instruction this.AddRFormatEmiter(OperationCodes.Xor, OperationXCodes.Xor, rFormatEmiterGenerator(OpCodes.Xor)); //The xori instruction this.AddIFormatEmiter(OperationCodes.Xori, iFormatEmiterGenerator(OpCodes.Xor)); //The nor instruction this.AddRFormatEmiter(OperationCodes.Nor, OperationXCodes.Nor, (i, inst, genData) => { var gen = genData.ILGenerator; //Push reg C this.EmitRegRef(gen, inst.RegisterC); //Load the value of reg A this.EmitRegRef(gen, inst.RegisterA); this.EmitLoadRef(gen); //Load the value of reg B this.EmitRegRef(gen, inst.RegisterB); this.EmitLoadRef(gen); //OR them gen.Emit(OpCodes.Or); //Invert them gen.Emit(OpCodes.Not); //Store in reg C this.EmitSaveRef(gen); }); #endregion #region Shift //The sll instruction this.AddRFormatEmiter(OperationCodes.Sll, OperationXCodes.Sll, rFormatEmiterGenerator(OpCodes.Shl)); //The sll instruction this.AddRFormatEmiter(OperationCodes.Srl, OperationXCodes.Srl, rFormatEmiterGenerator(OpCodes.Shr)); Func <OpCode, RFormatInstructionEmiter> shiftImmGenerator = opCode => { return((i, inst, genData) => { var gen = genData.ILGenerator; //Push reg C this.EmitRegRef(gen, inst.RegisterC); //Load the value of reg A this.EmitRegRef(gen, inst.RegisterA); this.EmitLoadRef(gen); //Push the imm field, stored in the OPX code gen.Emit(OpCodes.Ldc_I4, inst.OpxCode & 0x1F); //Apply the operation gen.Emit(opCode); //Store in reg C this.EmitSaveRef(gen); }); }; //The slli instruction this.AddRFormatEmiter(OperationCodes.Slli, OperationXCodes.Slli, shiftImmGenerator(OpCodes.Shl), ~0x1F); //The srli instruction this.AddRFormatEmiter(OperationCodes.Srli, OperationXCodes.Srli, shiftImmGenerator(OpCodes.Shr), ~0x1F); #endregion #region Compare //The cmpeq instruction this.AddRFormatEmiter(OperationCodes.Cmpeq, OperationXCodes.Cmpeq, rFormatEmiterGenerator(OpCodes.Ceq)); //The cmpne instruction this.AddRFormatEmiter(OperationCodes.Cmpne, OperationXCodes.Cmpne, (i, inst, genData) => { var gen = genData.ILGenerator; //Push reg C this.EmitRegRef(gen, inst.RegisterC); //Load the value of reg A this.EmitRegRef(gen, inst.RegisterA); this.EmitLoadRef(gen); //Load the value of reg B this.EmitRegRef(gen, inst.RegisterB); this.EmitLoadRef(gen); Label end = gen.DefineLabel(); Label setOne = gen.DefineLabel(); //Branch if A == B gen.Emit(OpCodes.Beq, setOne); gen.Emit(OpCodes.Ldc_I4_1); //A != B, push 1 gen.Emit(OpCodes.Br, end); gen.MarkLabel(setOne); gen.Emit(OpCodes.Ldc_I4_0); //A == B, push 0 gen.MarkLabel(end); //Store in reg C this.EmitSaveRef(gen); }); //The cmplt instruction this.AddRFormatEmiter(OperationCodes.Cmplt, OperationXCodes.Cmplt, rFormatEmiterGenerator(OpCodes.Clt)); //The cmpge instruction this.AddRFormatEmiter(OperationCodes.Cmpge, OperationXCodes.Cmpge, (i, inst, genData) => { var gen = genData.ILGenerator; //Push reg C this.EmitRegRef(gen, inst.RegisterC); //Load the value of reg A this.EmitRegRef(gen, inst.RegisterA); this.EmitLoadRef(gen); //Load the value of reg B this.EmitRegRef(gen, inst.RegisterB); this.EmitLoadRef(gen); Label end = gen.DefineLabel(); Label setOne = gen.DefineLabel(); //Branch if A >= B gen.Emit(OpCodes.Bge, setOne); gen.Emit(OpCodes.Ldc_I4_0); //A < B, push 0 gen.Emit(OpCodes.Br, end); gen.MarkLabel(setOne); gen.Emit(OpCodes.Ldc_I4_1); //A >= B, push 1 gen.MarkLabel(end); //Store in reg C this.EmitSaveRef(gen); }); #endregion #region Branch //The br instruction this.AddIFormatEmiter(OperationCodes.Br, (i, inst, genData) => { var gen = genData.ILGenerator; //Emit the jump int label = i + (inst.SignedImmediate / 4) + 1; gen.Emit(OpCodes.Br, genData.GetLabel(label)); }); //Create branch emiters Func <OpCode, IFormatInstructionEmiter> branchEmiterGenerator = opCode => { return((i, inst, genData) => { var gen = genData.ILGenerator; //Load the value of reg A this.EmitRegRef(gen, inst.RegisterA); this.EmitLoadRef(gen); //Load the value of reg B this.EmitRegRef(gen, inst.RegisterB); this.EmitLoadRef(gen); //Emit the jump int label = (i + inst.SignedImmediate / 4) + 1; gen.Emit(opCode, genData.GetLabel(label)); }); }; //The beq instruction this.AddIFormatEmiter(OperationCodes.Beq, branchEmiterGenerator(OpCodes.Beq)); //The bne instruction this.AddIFormatEmiter(OperationCodes.Bne, branchEmiterGenerator(OpCodes.Bne_Un)); //The bge instruction this.AddIFormatEmiter(OperationCodes.Bge, branchEmiterGenerator(OpCodes.Bge)); //The blt instruction this.AddIFormatEmiter(OperationCodes.Blt, branchEmiterGenerator(OpCodes.Blt)); #endregion #region Memory //The ldw instruction this.AddIFormatEmiter(OperationCodes.Ldw, (i, inst, genData) => { var gen = genData.ILGenerator; //Emit reference to reg B this.EmitRegRef(gen, inst.RegisterB); for (int offset = 0; offset < 4; offset++) { //Emit the memory reference this.EmitMemRef(gen); //Load the value of reg A (base address) this.EmitRegRef(gen, inst.RegisterA); this.EmitLoadRef(gen); //Push the imm value (offset) gen.Emit(OpCodes.Ldc_I4, inst.SignedImmediate + offset); //Compute the effective address gen.Emit(OpCodes.Add); //Load from memory this.EmitLoadMem(gen); } //Convert the top 4 bytes to a int gen.EmitCall(OpCodes.Call, this.convertToInt, null); //Store in reg B this.EmitSaveRef(gen); }); //The stw instruction this.AddIFormatEmiter(OperationCodes.Stw, (i, inst, genData) => { var gen = genData.ILGenerator; //Push the vm data this.LoadVMData(gen); //Load the value of reg A (base addr) this.EmitRegRef(gen, inst.RegisterA); this.EmitLoadRef(gen); //Push the imm value (offset) gen.Emit(OpCodes.Ldc_I4, inst.SignedImmediate); //Compute the effective address gen.Emit(OpCodes.Add); //Load the value of reg B (value) this.EmitRegRef(gen, inst.RegisterB); this.EmitLoadRef(gen); //Store in memory gen.EmitCall(OpCodes.Call, this.setWordMemAddr, null); }); //The ldb instruction this.AddIFormatEmiter(OperationCodes.Ldb, (i, inst, genData) => { var gen = genData.ILGenerator; //Emit reference to reg B this.EmitRegRef(gen, inst.RegisterB); //Emit the memory reference this.EmitMemRef(gen); //Load the value of reg A (base address) this.EmitRegRef(gen, inst.RegisterA); this.EmitLoadRef(gen); //Push the imm value (offset) gen.Emit(OpCodes.Ldc_I4, inst.SignedImmediate); //Compute the effective address gen.Emit(OpCodes.Add); //Load from memory this.EmitLoadMem(gen); //Store in reg B this.EmitSaveRef(gen); }); //The stb instruction this.AddIFormatEmiter(OperationCodes.Stb, (i, inst, genData) => { var gen = genData.ILGenerator; //Emit the reference to the memory field gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Ldfld, this.memoryField); //Load the value of reg A (base addr) this.EmitRegRef(gen, inst.RegisterA); this.EmitLoadRef(gen); //Push the imm value (offset) gen.Emit(OpCodes.Ldc_I4, inst.SignedImmediate); //Compute the effective address gen.Emit(OpCodes.Add); //Load the value of reg B (value) this.EmitRegRef(gen, inst.RegisterB); this.EmitLoadRef(gen); //Store in memory gen.Emit(OpCodes.Stelem_I1); }); #endregion #endregion }