private Constant EmitDirectAddress(int reg, MemoryOperand memOp) { Debug.Assert(memOp.Offset.IsValid); if (defaultWordSize == PrimitiveType.Word16) { reg |= 0x6; emitter.EmitByte(reg); return(Constant.Create(PrimitiveType.Word16, memOp.Offset.ToUInt32())); } else { reg |= 0x5; emitter.EmitByte(reg); return(Constant.Word32(memOp.Offset.ToUInt32())); } }
public void DisEdiTimes2() { X86TextAssembler asm = new X86TextAssembler(sc, new X86ArchitectureFlat32("x86-protected-32")); var program = asm.AssembleFragment(Address.SegPtr(0x0B00, 0), @" .i386 mov ebx,[edi*2] "); CreateDisassembler32(program.SegmentMap.Segments.Values.First().MemoryArea); var instr = dasm.First(); MemoryOperand mem = (MemoryOperand)instr.op2; Assert.AreEqual(2, mem.Scale); Assert.AreEqual(RegisterStorage.None, mem.Base); Assert.AreEqual(Registers.edi, mem.Index); }
private void RenderObjdumpMemoryOperand(MemoryOperand mem, StringBuilder sb) { switch (mem.Width.Size) { case 1: sb.Append("BYTE PTR"); break; case 2: sb.Append("WORD PTR"); break; case 4: sb.Append("DWORD PTR"); break; case 8: sb.Append("QWORD PTR"); break; case 10: sb.Append("TBYTE PTR"); break; case 16: sb.Append("XMMWORD PTR"); break; case 32: sb.Append("YMMWORD PTR"); break; default: sb.AppendFormat("[SIZE {0} PTR]", mem.Width.Size); break; } sb.AppendFormat(" {0}[", mem.SegOverride != null && mem.SegOverride != RegisterStorage.None ? $"{mem.SegOverride}:" : ""); if (mem.Base != null && mem.Base != RegisterStorage.None) { sb.Append(mem.Base.Name); if (mem.Index != null && mem.Index != RegisterStorage.None) { sb.Append("+"); sb.Append(mem.Index.Name); if (mem.Scale >= 1) { sb.AppendFormat("*{0}", mem.Scale); } } if (mem.Offset != null && mem.Offset.IsValid) { RenderObjdumpConstant(mem.Offset, true, sb); } } else { sb.Append(mem.Offset); } sb.Append("]"); }
protected virtual Address ResolveFlowInstructionTarget(MemoryOperand operand) { #if false // TODO: handle symbolic target. MemoryOperand opr = (MemoryOperand)instruction.Operands[0]; // Handle static near jump table. We recognize a jump table // heuristically if the instruction looks like the following: // // jmpn word ptr cs:[bx+3782h] // // That is, it meets the requirements that // - the instruction is JMPN // - the jump target is a word-ptr memory location // - the memory location has CS prefix // - a base register (e.g. bx) specifies the entry index // // Note that a malformed executable may create a jump table // not conforming to the above rules, or create a non-jump // table that conforms to the above rules. We do not deal with // these cases for the moment. if (instruction.Operation == Operation.JMP && opr.Size == CpuSize.Use16Bit && opr.Segment == Register.CS && opr.Base != Register.None && opr.Index == Register.None) { #if false return(new XRef( type: XRefType.NearIndexedJump, source: start, target: Pointer.Invalid, dataLocation: new Pointer(start.Segment, (UInt16)opr.Displacement.Value) )); #else return(new XRef( type: XRefType.NearJump, source: start, target: Address.Invalid )); #endif } #endif return(Address.Invalid); }
private ParsedOperand ParseMemoryOperand(RegisterStorage segOver) { MemoryOperand memOp = new MemoryOperand(null); memOp.SegOverride = segOver; this.segOverride = segOver; ParseMemoryFactor(memOp); for (;;) { Token token = lexer.GetToken(); switch (token) { default: OnError("Unexpected token: " + token); return(null); case Token.KET: if (totalInt != 0 || sym != null) { if (addrWidth == null || sym != null) { memOp.Offset = Constant.Create(defaultAddressWidth, totalInt); } else { memOp.Offset = X86Assembler.IntegralConstant(totalInt, addrWidth); } } return(new ParsedOperand(memOp, sym)); case Token.PLUS: break; case Token.MINUS: Expect(Token.INTEGER); totalInt -= lexer.Integer; continue; case Token.ID: break; } ParseMemoryFactor(memOp); } }
public static Operand MemoryOp( OperandType type, Operand baseAddress, Operand index = default, Multiplier scale = Multiplier.x1, int displacement = 0) { Operand result = Make(OperandKind.Memory, type, 0); MemoryOperand memory = result.GetMemory(); memory.BaseAddress = baseAddress; memory.Index = index; memory.Scale = scale; memory.Displacement = displacement; return(result); }
/// <summary> /// /// </summary> /// <param name="ctx"></param> /// <param name="emitter"></param> protected override void Emit(Context ctx, MachineCodeEmitter emitter) { RegisterOperand rop = (RegisterOperand)ctx.Result; MemoryOperand mop = (MemoryOperand)ctx.Operand1; byte[] code; if (mop.Base != null) { code = new byte[] { 0x8D, 0x84, (4 << 3) }; code[1] |= (byte)((rop.Register.RegisterCode & 0x07)); code[2] |= (byte)((mop.Base.RegisterCode & 0x07)); } else { code = new byte[] { 0xB8 }; } emitter.Write(code, 0, code.Length); emitter.EmitImmediate(mop); }
protected override void HandleCallInstance(CompilationData compilationData, Instruction instruction, int index) { var classType = instruction.ClassType; var signature = this.virtualMachine.Binder.MemberFunctionSignature( classType, instruction.StringValue, instruction.Parameters); var funcToCall = this.virtualMachine.Binder.GetFunction(signature); var firstArgOffset = new MemoryOperand( Register.BP, compilationData.OperandStack.GetStackOperandOffset( compilationData.OperandStack.TopIndex - (int)funcToCall.Parameters.Count + 1)); //Add null check compilationData.Assembler.Move(Register.AX, firstArgOffset); this.exceptionHandling.AddNullCheck(compilationData, Register.AX); this.GenerateCallInstruction(compilationData, funcToCall, index); }
/// <summary> /// Moves the return value to 64 bit. /// </summary> /// <param name="resultOperand">The result operand.</param> /// <param name="ctx">The context.</param> private static void MoveReturnValueTo64Bit(Operand resultOperand, Context ctx) { SigType I4 = new SigType(CilElementType.I4); SigType U4 = new SigType(CilElementType.U4); MemoryOperand memoryOperand = resultOperand as MemoryOperand; if (memoryOperand == null) { return; } Operand opL, opH; LongOperandTransformationStage.SplitLongOperand(memoryOperand, out opL, out opH); //MemoryOperand opL = new MemoryOperand(U4, memoryOperand.Base, memoryOperand.Offset); //MemoryOperand opH = new MemoryOperand(I4, memoryOperand.Base, new IntPtr(memoryOperand.Offset.ToInt64() + 4)); RegisterOperand eax = new RegisterOperand(U4, GeneralPurposeRegister.EAX); RegisterOperand edx = new RegisterOperand(I4, GeneralPurposeRegister.EDX); ctx.AppendInstruction(CPUx86.Instruction.MovInstruction, opL, eax); ctx.AppendInstruction(CPUx86.Instruction.MovInstruction, opH, edx); }
/// <summary> /// Emits the given code. /// </summary> /// <param name="code">The opcode bytes.</param> /// <param name="regField">The modR/M regfield.</param> /// <param name="dest">The destination operand.</param> /// <param name="src">The source operand.</param> public void Emit(byte[] code, byte?regField, Operand dest, Operand src) { byte? sib = null, modRM = null; MemoryOperand displacement = null; // Write the opcode _codeStream.Write(code, 0, code.Length); if (null == dest && null == src) { return; } // Write the mod R/M byte modRM = CalculateModRM(regField, dest, src, out sib, out displacement); if (null != modRM) { _codeStream.WriteByte(modRM.Value); if (sib.HasValue) { _codeStream.WriteByte(sib.Value); } } // Add displacement to the code if (null != displacement) { EmitDisplacement(displacement); } // Add immediate bytes if (dest is ConstantOperand) { EmitImmediate(dest); } if (src is ConstantOperand) { EmitImmediate(src); } }
public void ModRm16Memop() { MemoryOperand m; PrimitiveType w = PrimitiveType.Word16; ModRmBuilder mrm = new ModRmBuilder(w, null); m = new MemoryOperand(w, Registers.bx, Registers.si, 1, Constant.Invalid); Assert.AreEqual(0, mrm.Get16AddressingModeMask(m)); m = new MemoryOperand(w, Registers.bx, Registers.di, 1, Constant.Invalid); Assert.AreEqual(1, mrm.Get16AddressingModeMask(m)); m = new MemoryOperand(w, Registers.bp, Registers.si, 1, Constant.Invalid); Assert.AreEqual(2, mrm.Get16AddressingModeMask(m)); m = new MemoryOperand(w, Registers.bp, Registers.di, 1, Constant.Invalid); Assert.AreEqual(3, mrm.Get16AddressingModeMask(m)); m = new MemoryOperand(w, Registers.si, Constant.Invalid); Assert.AreEqual(4, mrm.Get16AddressingModeMask(m)); m = new MemoryOperand(w, Registers.di, Constant.Invalid); Assert.AreEqual(5, mrm.Get16AddressingModeMask(m)); m = new MemoryOperand(w, Registers.bp, Constant.Invalid); Assert.AreEqual(0x46, mrm.Get16AddressingModeMask(m)); m = new MemoryOperand(w, Registers.bx, Constant.Invalid); Assert.AreEqual(7, mrm.Get16AddressingModeMask(m)); }
/// <summary> /// Emits the given code. /// </summary> /// <param name="opCode">The op code.</param> /// <param name="result">The destination operand.</param> /// <param name="leftOperand">The source operand.</param> /// <param name="rightOperand">The third operand.</param> public void Emit(OpCode opCode, Operand result, Operand leftOperand, Operand rightOperand) { byte? sib = null, modRM = null; MemoryOperand displacement = null; // Write the opcode _codeStream.Write(opCode.Code, 0, opCode.Code.Length); if (null == result && null == leftOperand) { return; } // Write the mod R/M byte modRM = CalculateModRM(opCode.RegField, result, leftOperand, out sib, out displacement); if (null != modRM) { _codeStream.WriteByte(modRM.Value); if (sib.HasValue) { _codeStream.WriteByte(sib.Value); } } // Add displacement to the code if (null != displacement) { WriteDisplacement(displacement); } // Add immediate bytes if (rightOperand is ConstantOperand) { WriteImmediate(rightOperand); } }
protected override void HandleStoreElement(CompilationData compilationData, Instruction instruction, int index) { var elementType = this.virtualMachine.TypeProvider.FindType(instruction.StringValue); //Pop the operands compilationData.OperandStack.PopRegister(Register.DX); //The value to store compilationData.OperandStack.PopRegister(Register.R10); //The index of the element compilationData.OperandStack.PopRegister(Register.AX); //The address of the array //Error checks this.exceptionHandling.AddNullCheck(compilationData); this.exceptionHandling.AddArrayBoundsCheck(compilationData); //Compute the address of the element this.GenerateComputeAddress(compilationData, elementType); //Store the element var elementSize = TypeSystem.SizeOf(elementType); var dataSize = this.SizeOf(elementSize); var elementOffset = new MemoryOperand(Register.AX); if (dataSize != DataSize.Size8) { compilationData.Assembler.Move(elementOffset, Register.DX, dataSize); } else { compilationData.Assembler.Move(elementOffset, Register8Bit.DL); } //if (elementType.IsReference()) //{ // addCardMarking(vmState, assembler, Registers::AX); //} }
/// <summary> /// Replaces the instrinsic call site /// </summary> /// <param name="context">The context.</param> /// <param name="typeSystem">The type system.</param> public void ReplaceIntrinsicCall(Context context, ITypeSystem typeSystem) { MemoryOperand operand = new MemoryOperand(new Mosa.Runtime.Metadata.Signatures.SigType(Mosa.Runtime.Metadata.CilElementType.Ptr), GeneralPurposeRegister.EAX, new System.IntPtr(0)); context.SetInstruction(CPUx86.Instruction.MovInstruction, new RegisterOperand(new Mosa.Runtime.Metadata.Signatures.SigType(Mosa.Runtime.Metadata.CilElementType.Ptr), GeneralPurposeRegister.EAX), context.Operand1); context.AppendInstruction(CPUx86.Instruction.LgdtInstruction, null, operand); RegisterOperand ax = new RegisterOperand(new Mosa.Runtime.Metadata.Signatures.SigType(Mosa.Runtime.Metadata.CilElementType.I2), GeneralPurposeRegister.EAX); RegisterOperand ds = new RegisterOperand(new Mosa.Runtime.Metadata.Signatures.SigType(Mosa.Runtime.Metadata.CilElementType.I2), SegmentRegister.DS); RegisterOperand es = new RegisterOperand(new Mosa.Runtime.Metadata.Signatures.SigType(Mosa.Runtime.Metadata.CilElementType.I2), SegmentRegister.ES); RegisterOperand fs = new RegisterOperand(new Mosa.Runtime.Metadata.Signatures.SigType(Mosa.Runtime.Metadata.CilElementType.I2), SegmentRegister.FS); RegisterOperand gs = new RegisterOperand(new Mosa.Runtime.Metadata.Signatures.SigType(Mosa.Runtime.Metadata.CilElementType.I2), SegmentRegister.GS); RegisterOperand ss = new RegisterOperand(new Mosa.Runtime.Metadata.Signatures.SigType(Mosa.Runtime.Metadata.CilElementType.I2), SegmentRegister.SS); context.AppendInstruction(CPUx86.Instruction.MovInstruction, ax, new ConstantOperand(new Mosa.Runtime.Metadata.Signatures.SigType(Mosa.Runtime.Metadata.CilElementType.I4), (int)0x00000010)); context.AppendInstruction(CPUx86.Instruction.MovInstruction, ds, ax); context.AppendInstruction(CPUx86.Instruction.MovInstruction, es, ax); context.AppendInstruction(CPUx86.Instruction.MovInstruction, fs, ax); context.AppendInstruction(CPUx86.Instruction.MovInstruction, gs, ax); context.AppendInstruction(CPUx86.Instruction.MovInstruction, ss, ax); context.AppendInstruction(CPUx86.Instruction.FarJmpInstruction); context.AppendInstruction(CPUx86.Instruction.NopInstruction); context.Previous.SetBranch(context.Offset); }
/// <summary> /// Emits the displacement operand. /// </summary> /// <param name="displacement">The displacement operand.</param> private void EmitDisplacement(MemoryOperand displacement) { byte[] disp; MemberOperand member = displacement as MemberOperand; LabelOperand label = displacement as LabelOperand; if (null != label) { int pos = (int)(_codeStream.Position - _codeStreamBasePosition); disp = LittleEndianBitConverter.GetBytes((uint)_linker.Link(LinkType.AbsoluteAddress | LinkType.I4, _compiler.Method, pos, 0, label.Name, IntPtr.Zero)); } else if (null != member) { int pos = (int)(_codeStream.Position - _codeStreamBasePosition); disp = LittleEndianBitConverter.GetBytes((uint)_linker.Link(LinkType.AbsoluteAddress | LinkType.I4, _compiler.Method, pos, 0, member.Member, member.Offset)); } else { disp = LittleEndianBitConverter.GetBytes(displacement.Offset.ToInt32()); } _codeStream.Write(disp, 0, disp.Length); }
public static void RunPass(ControlFlowGraph cfg) { for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) { Node nextNode; for (Node node = block.Operations.First; node != null; node = nextNode) { nextNode = node.ListNext; if (!(node is Operation operation)) { continue; } // Insert copies for constants that can't fit on a 32-bits immediate. // Doing this early unblocks a few optimizations. if (operation.Instruction == Instruction.Add) { Operand src1 = operation.GetSource(0); Operand src2 = operation.GetSource(1); if (src1.Kind == OperandKind.Constant && (src1.Relocatable || CodeGenCommon.IsLongConst(src1))) { Operand temp = Local(src1.Type); Operation copyOp = Operation(Instruction.Copy, temp, src1); block.Operations.AddBefore(operation, copyOp); operation.SetSource(0, temp); } if (src2.Kind == OperandKind.Constant && (src2.Relocatable || CodeGenCommon.IsLongConst(src2))) { Operand temp = Local(src2.Type); Operation copyOp = Operation(Instruction.Copy, temp, src2); block.Operations.AddBefore(operation, copyOp); operation.SetSource(1, temp); } } // Try to fold something like: // shl rbx, 2 // add rax, rbx // add rax, 0xcafe // mov rax, [rax] // Into: // mov rax, [rax+rbx*4+0xcafe] if (IsMemoryLoadOrStore(operation.Instruction)) { OperandType type; if (operation.Destination != null) { type = operation.Destination.Type; } else { type = operation.GetSource(1).Type; } MemoryOperand memOp = GetMemoryOperandOrNull(operation.GetSource(0), type); if (memOp != null) { operation.SetSource(0, memOp); } } } } Optimizer.RemoveUnusedNodes(cfg); }
/// <summary> /// Emits the ModRM byte (and SIB byte if applicable) /// </summary> /// <param name="reg"></param> /// <param name="memOp"></param> /// <returns>The offset value to be emitted as the last piece of the instruction</returns> public Constant?EmitModRMPrefix(int reg, MemoryOperand memOp) { offset = null; reg <<= 3; if (memOp.Base != RegisterStorage.None || memOp.Index != RegisterStorage.None) { PrimitiveType baseWidth = memOp.Base.DataType; PrimitiveType indexWidth = memOp.Index.DataType; if (memOp.Base != RegisterStorage.None && memOp.Index != RegisterStorage.None) { if (baseWidth != indexWidth) { OnError("mismatched base and index registers"); return(null); } } // Add the 'mod' bits if (memOp.Offset != null) { Debug.Assert(memOp.Offset.IsValid); if (memOp.Offset.DataType == PrimitiveType.SByte) { reg |= 0x40; offset = memOp.Offset; } else { reg |= 0x80; offset = Constant.Create(defaultWordSize, memOp.Offset.ToInt32()); } } bool fNeedsSib = false; int sib = 0; if (baseWidth == PrimitiveType.Word16) { reg |= Get16AddressingModeMask(memOp); } else if (baseWidth == PrimitiveType.Word32 || indexWidth == PrimitiveType.Word32) { if (memOp.Index == RegisterStorage.None) { if (memOp.Base != Registers.esp) { reg |= X86Assembler.RegisterEncoding(memOp.Base); if (memOp.Offset == null && memOp.Base == Registers.ebp) { reg |= 0x40; offset = Constant.Byte(0); } } else { reg |= 0x04; fNeedsSib = true; sib = 0x24; } } else { reg |= 0x04; fNeedsSib = true; switch (memOp.Scale) { case 1: sib = 0; break; case 2: sib = 0x40; break; case 4: sib = 0x80; break; case 8: sib = 0xC0; break; default: OnError("Scale factor must be 1, 2, 4, or 8"); return(Constant.Invalid); } if (memOp.Base == RegisterStorage.None) { sib |= 0x05; reg &= ~0xC0; // clear mod part of modRM. if (memOp.Offset == null) { offset = Constant.Word32(0); } } else { sib |= X86Assembler.RegisterEncoding(memOp.Base); } if (memOp.Index != Registers.esp) { sib |= X86Assembler.RegisterEncoding(memOp.Index) << 3; } else { throw new ApplicationException("ESP register can't be used as an index register"); } } } else { throw new ApplicationException("unexpected address width"); } emitter.EmitByte(reg); if (fNeedsSib) { emitter.EmitByte(sib); } return(offset); } else { return(EmitDirectAddress(reg, memOp)); } }
void Verify_MemoryOperand_ctors() { { var op = new MemoryOperand(Register.RCX, Register.RSI, 4, 0x12345678, 8, true, Register.FS); Assert.Equal(Register.RCX, op.Base); Assert.Equal(Register.RSI, op.Index); Assert.Equal(4, op.Scale); Assert.Equal(0x12345678, op.Displacement); Assert.Equal(8, op.DisplSize); Assert.True(op.IsBroadcast); Assert.Equal(Register.FS, op.SegmentPrefix); } { var op = new MemoryOperand(Register.RCX, Register.RSI, 4, true, Register.FS); Assert.Equal(Register.RCX, op.Base); Assert.Equal(Register.RSI, op.Index); Assert.Equal(4, op.Scale); Assert.Equal(0, op.Displacement); Assert.Equal(0, op.DisplSize); Assert.True(op.IsBroadcast); Assert.Equal(Register.FS, op.SegmentPrefix); } { var op = new MemoryOperand(Register.RCX, 0x12345678, 8, true, Register.FS); Assert.Equal(Register.RCX, op.Base); Assert.Equal(Register.None, op.Index); Assert.Equal(1, op.Scale); Assert.Equal(0x12345678, op.Displacement); Assert.Equal(8, op.DisplSize); Assert.True(op.IsBroadcast); Assert.Equal(Register.FS, op.SegmentPrefix); } { var op = new MemoryOperand(Register.RSI, 4, 0x12345678, 8, true, Register.FS); Assert.Equal(Register.None, op.Base); Assert.Equal(Register.RSI, op.Index); Assert.Equal(4, op.Scale); Assert.Equal(0x12345678, op.Displacement); Assert.Equal(8, op.DisplSize); Assert.True(op.IsBroadcast); Assert.Equal(Register.FS, op.SegmentPrefix); } { var op = new MemoryOperand(Register.RCX, 0x12345678, true, Register.FS); Assert.Equal(Register.RCX, op.Base); Assert.Equal(Register.None, op.Index); Assert.Equal(1, op.Scale); Assert.Equal(0x12345678, op.Displacement); Assert.Equal(1, op.DisplSize); Assert.True(op.IsBroadcast); Assert.Equal(Register.FS, op.SegmentPrefix); } { var op = new MemoryOperand(Register.RCX, Register.RSI, 4, 0x12345678, 8); Assert.Equal(Register.RCX, op.Base); Assert.Equal(Register.RSI, op.Index); Assert.Equal(4, op.Scale); Assert.Equal(0x12345678, op.Displacement); Assert.Equal(8, op.DisplSize); Assert.False(op.IsBroadcast); Assert.Equal(Register.None, op.SegmentPrefix); } { var op = new MemoryOperand(Register.RCX, Register.RSI, 4); Assert.Equal(Register.RCX, op.Base); Assert.Equal(Register.RSI, op.Index); Assert.Equal(4, op.Scale); Assert.Equal(0, op.Displacement); Assert.Equal(0, op.DisplSize); Assert.False(op.IsBroadcast); Assert.Equal(Register.None, op.SegmentPrefix); } { var op = new MemoryOperand(Register.RCX, Register.RSI); Assert.Equal(Register.RCX, op.Base); Assert.Equal(Register.RSI, op.Index); Assert.Equal(1, op.Scale); Assert.Equal(0, op.Displacement); Assert.Equal(0, op.DisplSize); Assert.False(op.IsBroadcast); Assert.Equal(Register.None, op.SegmentPrefix); } { var op = new MemoryOperand(Register.RCX, 0x12345678, 8); Assert.Equal(Register.RCX, op.Base); Assert.Equal(Register.None, op.Index); Assert.Equal(1, op.Scale); Assert.Equal(0x12345678, op.Displacement); Assert.Equal(8, op.DisplSize); Assert.False(op.IsBroadcast); Assert.Equal(Register.None, op.SegmentPrefix); } { var op = new MemoryOperand(Register.RSI, 4, 0x12345678, 8); Assert.Equal(Register.None, op.Base); Assert.Equal(Register.RSI, op.Index); Assert.Equal(4, op.Scale); Assert.Equal(0x12345678, op.Displacement); Assert.Equal(8, op.DisplSize); Assert.False(op.IsBroadcast); Assert.Equal(Register.None, op.SegmentPrefix); } { var op = new MemoryOperand(Register.RCX, 0x12345678); Assert.Equal(Register.RCX, op.Base); Assert.Equal(Register.None, op.Index); Assert.Equal(1, op.Scale); Assert.Equal(0x12345678, op.Displacement); Assert.Equal(1, op.DisplSize); Assert.False(op.IsBroadcast); Assert.Equal(Register.None, op.SegmentPrefix); } { var op = new MemoryOperand(Register.RCX); Assert.Equal(Register.RCX, op.Base); Assert.Equal(Register.None, op.Index); Assert.Equal(1, op.Scale); Assert.Equal(0, op.Displacement); Assert.Equal(0, op.DisplSize); Assert.False(op.IsBroadcast); Assert.Equal(Register.None, op.SegmentPrefix); } }
public static IEnumerable <object[]> ParseMovFromMemoryOffsetTestCases() { var registers = new[] { "rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "rsp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", }; var values = new[] { -122232425, -71819202, -1314151, -101112, -56789, -1234, -300, -255, -128, -127, -126, -1, 0, 1, 126, 127, 128, 255, 300, 1234, 56789, 101112, 1314151, 71819202, 122232425, }; foreach (var value in values) { var imm = new Imm32Operand(value); var memoryOperand = new MemoryOperand(imm); foreach (var reg1 in registers) { var register64Operand = new Register64Operand(reg1); // mov [offset], reg2 yield return(new object[] { new Instruction(64, "mov", new Operand[] { memoryOperand, register64Operand }), $"mov [{value}], {reg1}" }); // mov reg1, [offset] yield return(new object[] { new Instruction(64, "mov", new Operand[] { register64Operand, memoryOperand }), $"mov {reg1}, [{value}]" }); } } }
/// <summary> /// Generates code for an one virtual register operand instruction with an int value /// </summary> /// <param name="destinationRegister">The destination register</param> /// <param name="value">The value</param> public void GenerateOneRegisterWithValueInstruction(VirtualRegister destinationRegister, int value, Action<IList<byte>, IntRegister, int> inst1, Action<IList<byte>, MemoryOperand, int> inst2) { var generatedCode = compilationData.Function.GeneratedCode; var regAlloc = compilationData.RegisterAllocation; var opStack = regAlloc.GetStackIndex(destinationRegister); if (!opStack.HasValue) { var opReg = this.GetIntRegisterForVirtual(destinationRegister).Value; inst1(generatedCode, opReg, value); } else { var stackOp = new MemoryOperand( Register.BP, CalculateStackOffset(opStack.Value)); inst2(generatedCode, stackOp, value); } }
public AllocationResult RunPass( ControlFlowGraph cfg, StackAllocator stackAlloc, RegisterMasks regMasks) { int intUsedRegisters = 0; int vecUsedRegisters = 0; int intFreeRegisters = regMasks.IntAvailableRegisters; int vecFreeRegisters = regMasks.VecAvailableRegisters; var blockInfo = new BlockInfo[cfg.Blocks.Count]; var locInfo = new List <LocalInfo>(); var locVisited = new HashSet <Operand>(); for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--) { BasicBlock block = cfg.PostOrderBlocks[index]; int intFixedRegisters = 0; int vecFixedRegisters = 0; bool hasCall = false; for (Node node = block.Operations.First; node != null; node = node.ListNext) { if (node is Operation operation && operation.Instruction == Instruction.Call) { hasCall = true; } for (int srcIndex = 0; srcIndex < node.SourcesCount; srcIndex++) { Operand source = node.GetSource(srcIndex); if (source.Kind == OperandKind.LocalVariable) { locInfo[source.GetLocalNumber() - 1].SetBlockIndex(block.Index); } else if (source.Kind == OperandKind.Memory) { MemoryOperand memOp = (MemoryOperand)source; if (memOp.BaseAddress != null) { locInfo[memOp.BaseAddress.GetLocalNumber() - 1].SetBlockIndex(block.Index); } if (memOp.Index != null) { locInfo[memOp.Index.GetLocalNumber() - 1].SetBlockIndex(block.Index); } } } for (int dstIndex = 0; dstIndex < node.DestinationsCount; dstIndex++) { Operand dest = node.GetDestination(dstIndex); if (dest.Kind == OperandKind.LocalVariable) { LocalInfo info; if (!locVisited.Add(dest)) { info = locInfo[dest.GetLocalNumber() - 1]; } else { dest.NumberLocal(locInfo.Count + 1); info = new LocalInfo(dest.Type, UsesCount(dest)); locInfo.Add(info); } info.SetBlockIndex(block.Index); } else if (dest.Kind == OperandKind.Register) { if (dest.Type.IsInteger()) { intFixedRegisters |= 1 << dest.GetRegister().Index; } else { vecFixedRegisters |= 1 << dest.GetRegister().Index; } } } } blockInfo[block.Index] = new BlockInfo(hasCall, intFixedRegisters, vecFixedRegisters); } int sequence = 0; for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--) { BasicBlock block = cfg.PostOrderBlocks[index]; BlockInfo blkInfo = blockInfo[block.Index]; int intLocalFreeRegisters = intFreeRegisters & ~blkInfo.IntFixedRegisters; int vecLocalFreeRegisters = vecFreeRegisters & ~blkInfo.VecFixedRegisters; int intCallerSavedRegisters = blkInfo.HasCall ? regMasks.IntCallerSavedRegisters : 0; int vecCallerSavedRegisters = blkInfo.HasCall ? regMasks.VecCallerSavedRegisters : 0; int intSpillTempRegisters = SelectSpillTemps( intCallerSavedRegisters & ~blkInfo.IntFixedRegisters, intLocalFreeRegisters); int vecSpillTempRegisters = SelectSpillTemps( vecCallerSavedRegisters & ~blkInfo.VecFixedRegisters, vecLocalFreeRegisters); intLocalFreeRegisters &= ~(intSpillTempRegisters | intCallerSavedRegisters); vecLocalFreeRegisters &= ~(vecSpillTempRegisters | vecCallerSavedRegisters); for (Node node = block.Operations.First; node != null; node = node.ListNext) { int intLocalUse = 0; int vecLocalUse = 0; void AllocateRegister(Operand source, MemoryOperand memOp, int srcIndex) { LocalInfo info = locInfo[source.GetLocalNumber() - 1]; info.UseCount++; Debug.Assert(info.UseCount <= info.Uses); if (info.Register != -1) { Operand reg = Register(info.Register, source.Type.ToRegisterType(), source.Type); if (memOp != null) { if (srcIndex == 0) { memOp.BaseAddress = reg; } else /* if (srcIndex == 1) */ { memOp.Index = reg; } } else { node.SetSource(srcIndex, reg); } if (info.UseCount == info.Uses && !info.PreAllocated) { if (source.Type.IsInteger()) { intLocalFreeRegisters |= 1 << info.Register; } else { vecLocalFreeRegisters |= 1 << info.Register; } } } else if (node is Operation operation && operation.Instruction == Instruction.Copy) { Operation fillOp = Operation(Instruction.Fill, node.Destination, Const(info.SpillOffset)); block.Operations.AddBefore(node, fillOp); block.Operations.Remove(node); node = fillOp; }
/// <summary> /// Generates code for an instruction with virtual register destination and memory source /// </summary> /// <param name="destinationRegister">The destination</param> /// <param name="source">The source</param> /// <param name="memoryRewrite">Determines how an instruction with two memory operands will be rewritten into one memory operand.</param> public void GenerateOneRegisterMemorySourceInstruction(VirtualRegister destinationRegister, MemoryOperand source, Action<IList<byte>, IntRegister, MemoryOperand> inst1, Action<IList<byte>, MemoryOperand, IntRegister> inst2, MemoryRewrite memoryRewrite = MemoryRewrite.MemoryOnLeft) { var generatedCode = compilationData.Function.GeneratedCode; var regAlloc = compilationData.RegisterAllocation; int? opStack = compilationData.RegisterAllocation.GetStackIndex(destinationRegister); if (!opStack.HasValue) { var destinationReg = this.GetIntRegisterForVirtual(destinationRegister).Value; inst1(generatedCode, destinationReg, source); } else { var opStackOffset = CalculateStackOffset(opStack.Value); RewriteMemory(memoryRewrite, new MemoryOperand(Register.BP, opStackOffset), source, inst1, inst2); } }
public AllocationResult RunPass( ControlFlowGraph cfg, StackAllocator stackAlloc, RegisterMasks regMasks) { int intUsedRegisters = 0; int vecUsedRegisters = 0; int intFreeRegisters = regMasks.IntAvailableRegisters; int vecFreeRegisters = regMasks.VecAvailableRegisters; BlockInfo[] blockInfo = new BlockInfo[cfg.Blocks.Count]; List <LocalInfo> locInfo = new List <LocalInfo>(); for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--) { BasicBlock block = cfg.PostOrderBlocks[index]; int intFixedRegisters = 0; int vecFixedRegisters = 0; bool hasCall = false; for (Node node = block.Operations.First; node != null; node = node.ListNext) { if (node is Operation operation && operation.Instruction == Instruction.Call) { hasCall = true; } for (int srcIndex = 0; srcIndex < node.SourcesCount; srcIndex++) { Operand source = node.GetSource(srcIndex); if (source.Kind == OperandKind.LocalVariable) { locInfo[source.AsInt32() - 1].SetBlockIndex(block.Index); } else if (source.Kind == OperandKind.Memory) { MemoryOperand memOp = (MemoryOperand)source; if (memOp.BaseAddress != null) { locInfo[memOp.BaseAddress.AsInt32() - 1].SetBlockIndex(block.Index); } if (memOp.Index != null) { locInfo[memOp.Index.AsInt32() - 1].SetBlockIndex(block.Index); } } } for (int dstIndex = 0; dstIndex < node.DestinationsCount; dstIndex++) { Operand dest = node.GetDestination(dstIndex); if (dest.Kind == OperandKind.LocalVariable) { LocalInfo info; if (dest.Value != 0) { info = locInfo[dest.AsInt32() - 1]; } else { dest.NumberLocal(locInfo.Count + 1); info = new LocalInfo(dest.Type, UsesCount(dest)); locInfo.Add(info); } info.SetBlockIndex(block.Index); } else if (dest.Kind == OperandKind.Register) { if (dest.Type.IsInteger()) { intFixedRegisters |= 1 << dest.GetRegister().Index; } else { vecFixedRegisters |= 1 << dest.GetRegister().Index; } } } } blockInfo[block.Index] = new BlockInfo(hasCall, intFixedRegisters, vecFixedRegisters); } int sequence = 0; for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--) { BasicBlock block = cfg.PostOrderBlocks[index]; BlockInfo blkInfo = blockInfo[block.Index]; int intLocalFreeRegisters = intFreeRegisters & ~blkInfo.IntFixedRegisters; int vecLocalFreeRegisters = vecFreeRegisters & ~blkInfo.VecFixedRegisters; int intCallerSavedRegisters = blkInfo.HasCall ? regMasks.IntCallerSavedRegisters : 0; int vecCallerSavedRegisters = blkInfo.HasCall ? regMasks.VecCallerSavedRegisters : 0; int intSpillTempRegisters = SelectSpillTemps( intCallerSavedRegisters & ~blkInfo.IntFixedRegisters, intLocalFreeRegisters); int vecSpillTempRegisters = SelectSpillTemps( vecCallerSavedRegisters & ~blkInfo.VecFixedRegisters, vecLocalFreeRegisters); intLocalFreeRegisters &= ~(intSpillTempRegisters | intCallerSavedRegisters); vecLocalFreeRegisters &= ~(vecSpillTempRegisters | vecCallerSavedRegisters); for (Node node = block.Operations.First; node != null; node = node.ListNext) { int intLocalUse = 0; int vecLocalUse = 0; void AllocateRegister(Operand source, MemoryOperand memOp, int srcIndex) { LocalInfo info = locInfo[source.AsInt32() - 1]; info.UseCount++; Debug.Assert(info.UseCount <= info.Uses); if (info.Register != -1) { Operand reg = Register(info.Register, source.Type.ToRegisterType(), source.Type); if (memOp != null) { if (srcIndex == 0) { memOp.BaseAddress = reg; } else /* if (srcIndex == 1) */ { memOp.Index = reg; } } else { node.SetSource(srcIndex, reg); } if (info.UseCount == info.Uses && !info.PreAllocated) { if (source.Type.IsInteger()) { intLocalFreeRegisters |= 1 << info.Register; } else { vecLocalFreeRegisters |= 1 << info.Register; } } } else { Operand temp = info.Temp; if (temp == null || info.Sequence != sequence) { temp = source.Type.IsInteger() ? GetSpillTemp(source, intSpillTempRegisters, ref intLocalUse) : GetSpillTemp(source, vecSpillTempRegisters, ref vecLocalUse); info.Sequence = sequence; info.Temp = temp; } if (memOp != null) { if (srcIndex == 0) { memOp.BaseAddress = temp; } else /* if (srcIndex == 1) */ { memOp.Index = temp; } } else { node.SetSource(srcIndex, temp); } Operation fillOp = new Operation(Instruction.Fill, temp, Const(info.SpillOffset)); block.Operations.AddBefore(node, fillOp); } } for (int srcIndex = 0; srcIndex < node.SourcesCount; srcIndex++) { Operand source = node.GetSource(srcIndex); if (source.Kind == OperandKind.LocalVariable) { AllocateRegister(source, null, srcIndex); } else if (source.Kind == OperandKind.Memory) { MemoryOperand memOp = (MemoryOperand)source; if (memOp.BaseAddress != null) { AllocateRegister(memOp.BaseAddress, memOp, 0); } if (memOp.Index != null) { AllocateRegister(memOp.Index, memOp, 1); } } } int intLocalAsg = 0; int vecLocalAsg = 0; for (int dstIndex = 0; dstIndex < node.DestinationsCount; dstIndex++) { Operand dest = node.GetDestination(dstIndex); if (dest.Kind != OperandKind.LocalVariable) { continue; } LocalInfo info = locInfo[dest.AsInt32() - 1]; if (info.UseCount == 0 && !info.PreAllocated) { int mask = dest.Type.IsInteger() ? intLocalFreeRegisters : vecLocalFreeRegisters; if (info.IsBlockLocal && mask != 0) { int selectedReg = BitUtils.LowestBitSet(mask); info.Register = selectedReg; if (dest.Type.IsInteger()) { intLocalFreeRegisters &= ~(1 << selectedReg); intUsedRegisters |= 1 << selectedReg; } else { vecLocalFreeRegisters &= ~(1 << selectedReg); vecUsedRegisters |= 1 << selectedReg; } } else { info.Register = -1; info.SpillOffset = stackAlloc.Allocate(dest.Type.GetSizeInBytes()); } } info.UseCount++; Debug.Assert(info.UseCount <= info.Uses); if (info.Register != -1) { node.SetDestination(dstIndex, Register(info.Register, dest.Type.ToRegisterType(), dest.Type)); } else { Operand temp = info.Temp; if (temp == null || info.Sequence != sequence) { temp = dest.Type.IsInteger() ? GetSpillTemp(dest, intSpillTempRegisters, ref intLocalAsg) : GetSpillTemp(dest, vecSpillTempRegisters, ref vecLocalAsg); info.Sequence = sequence; info.Temp = temp; } node.SetDestination(dstIndex, temp); Operation spillOp = new Operation(Instruction.Spill, null, Const(info.SpillOffset), temp); block.Operations.AddAfter(node, spillOp); node = spillOp; } } sequence++; intUsedRegisters |= intLocalAsg | intLocalUse; vecUsedRegisters |= vecLocalAsg | vecLocalUse; } } return(new AllocationResult(intUsedRegisters, vecUsedRegisters, stackAlloc.TotalSize)); }
/// <summary> /// Analyzes an instruction and returns a xref if the instruction is /// one of the branch/call/jump instructions. Note that the 'no-jump' /// branch of a conditional jump instruction is not returned. The /// caller must manually create such a xref if needed. /// </summary> /// <param name="instruction">The instruction to analyze.</param> /// <returns>XRef if the instruction is a b/c/j instruction; /// null otherwise.</returns> /// TBD: address wrapping if IP is above 0xFFFF is not handled. It should be. private XRef AnalyzeFlowInstruction(Pointer start, Instruction instruction) { Operation op = instruction.Operation; // Find the type of branch/call/jump instruction being processed. // // Note: If the instruction is a conditional jump, we assume that // the condition may be true or false, so that both "jump" and // "no jump" is a reachable branch. If the code is malformed such // that either branch will never be executed, the analysis may not // work correctly. // // Note: If the instruction is a function call, we assume that the // subroutine being called will return. If the subroutine never // returns the analysis may not work correctly. XRefType bcjType; switch (op) { case Operation.JO: case Operation.JNO: case Operation.JB: case Operation.JAE: case Operation.JE: case Operation.JNE: case Operation.JBE: case Operation.JA: case Operation.JS: case Operation.JNS: case Operation.JP: case Operation.JNP: case Operation.JL: case Operation.JGE: case Operation.JLE: case Operation.JG: case Operation.JCXZ: case Operation.LOOP: case Operation.LOOPZ: case Operation.LOOPNZ: bcjType = XRefType.ConditionalJump; break; case Operation.JMP: bcjType = XRefType.NearJump; break; case Operation.JMPF: bcjType = XRefType.FarJump; break; case Operation.CALL: bcjType = XRefType.NearCall; break; case Operation.CALLF: bcjType = XRefType.FarCall; break; default: // Not a b/c/j instruction; do nothing. return(null); } // Create a cross-reference depending on the type of operand. if (instruction.Operands[0] is RelativeOperand) // near jump/call to relative address { RelativeOperand opr = (RelativeOperand)instruction.Operands[0]; return(new XRef( type: bcjType, source: start, target: start.IncrementWithWrapping(instruction.EncodedLength + opr.Offset.Value) )); } if (instruction.Operands[0] is PointerOperand) // far jump/call to absolute address { PointerOperand opr = (PointerOperand)instruction.Operands[0]; return(new XRef( type: bcjType, source: start, target: new Pointer(opr.Segment.Value, (UInt16)opr.Offset.Value) )); } if (instruction.Operands[0] is MemoryOperand) // indirect jump/call { MemoryOperand opr = (MemoryOperand)instruction.Operands[0]; // Handle static near jump table. We recognize a jump table // heuristically if the instruction looks like the following: // // jmpn word ptr cs:[bx+3782h] // // That is, it meets the requirements that // - the instruction is JMPN // - the jump target is a word-ptr memory location // - the memory location has CS prefix // - a base register (e.g. bx) specifies the entry index // // Note that a malformed executable may create a jump table // not conforming to the above rules, or create a non-jump // table that conforms to the above rules. We do not deal with // these cases for the moment. if (op == Operation.JMP && opr.Size == CpuSize.Use16Bit && opr.Segment == Register.CS && opr.Base != Register.None && opr.Index == Register.None) { return(new XRef( type: XRefType.NearIndexedJump, source: start, target: Pointer.Invalid, dataLocation: new Pointer(start.Segment, (UInt16)opr.Displacement.Value) )); } } // Other jump/call targets that we cannot recognize. AddError(start, ErrorCategory.Message, "Cannot determine target of {0} instruction.", op); return(new XRef( type: bcjType, source: start, target: Pointer.Invalid )); }
/// <summary> /// Emits an immediate operand. /// </summary> /// <param name="op">The immediate operand to emit.</param> public void EmitImmediate(Operand op) { byte[] imm = null; if (op is LocalVariableOperand) { // Add the displacement StackOperand so = (StackOperand)op; imm = LittleEndianBitConverter.GetBytes(so.Offset.ToInt32()); } else if (op is LabelOperand) { _literals.Add(new Patch((op as LabelOperand).Label, _codeStream.Position)); imm = new byte[4]; } else if (op is MemoryOperand) { // Add the displacement MemoryOperand mo = (MemoryOperand)op; imm = LittleEndianBitConverter.GetBytes(mo.Offset.ToInt32()); } else if (op is ConstantOperand) { // Add the immediate ConstantOperand co = (ConstantOperand)op; switch (op.Type.Type) { case CilElementType.I: try { imm = LittleEndianBitConverter.GetBytes(Convert.ToInt32(co.Value)); } catch (OverflowException) { imm = LittleEndianBitConverter.GetBytes(Convert.ToUInt32(co.Value)); } break; case CilElementType.I1: //imm = LittleEndianBitConverter.GetBytes(Convert.ToSByte(co.Value)); imm = new byte[1] { Convert.ToByte(co.Value) }; break; case CilElementType.I2: imm = LittleEndianBitConverter.GetBytes(Convert.ToInt16(co.Value)); break; case CilElementType.I4: goto case CilElementType.I; case CilElementType.U1: //imm = LittleEndianBitConverter.GetBytes(Convert.ToByte(co.Value)); imm = new byte[1] { Convert.ToByte(co.Value) }; break; case CilElementType.Char: goto case CilElementType.U2; case CilElementType.U2: imm = LittleEndianBitConverter.GetBytes(Convert.ToUInt16(co.Value)); break; case CilElementType.U4: imm = LittleEndianBitConverter.GetBytes(Convert.ToUInt32(co.Value)); break; case CilElementType.I8: imm = LittleEndianBitConverter.GetBytes(Convert.ToInt64(co.Value)); break; case CilElementType.U8: imm = LittleEndianBitConverter.GetBytes(Convert.ToUInt64(co.Value)); break; case CilElementType.R4: imm = LittleEndianBitConverter.GetBytes(Convert.ToSingle(co.Value)); break; case CilElementType.R8: goto default; default: throw new NotSupportedException(); } } else if (op is RegisterOperand) { // Nothing to do... } else { throw new NotImplementedException(); } // Emit the immediate constant to the code if (null != imm) { _codeStream.Write(imm, 0, imm.Length); } }
/// <summary> /// Handles the given function call argument /// </summary> /// <param name="compilationData">The compilation data</param> /// <param name="argumentIndex">The index of the argument</param> /// <param name="argumentType">The type of the argument</param> /// <param name="argumentRegisters">The virtual registers for the arguments</param> /// <param name="aliveRegistersStack">The alive registers stack</param> /// <param name="toCall">The function to call</param> public void CallFunctionArgument( CompilationData compilationData, int argumentIndex, VMType argumentType, IReadOnlyList<VirtualRegister> argumentRegisters, IDictionary<HardwareRegister, int> aliveRegistersStack, FunctionDefinition toCall) { var virtualAssembler = compilationData.VirtualAssembler; var regAlloc = compilationData.RegisterAllocation; var generatedCode = compilationData.Function.GeneratedCode; int thisNumArgs = compilationData.Function.Definition.Parameters.Count; int numArgs = toCall.Parameters.Count; int argsStart = 1 + compilationData.StackSize / Assembler.RegisterSize; if (virtualAssembler.NeedSpillRegister) { argsStart += 1; } int alignment = this.CalculateStackAlignment(compilationData, toCall.Parameters, aliveRegistersStack.Count); var virtualReg = argumentRegisters[numArgs - 1 - argumentIndex]; var virtualRegStack = regAlloc.GetStackIndex(virtualReg); //Check if to pass argument by via the stack if (argumentIndex >= numRegisterArguments) { //Move arguments to the stack HardwareRegister spillReg; int stackOffset = 0; if (argumentType.IsPrimitiveType(PrimitiveTypes.Float)) { spillReg = virtualAssembler.GetFloatSpillRegister(); if (!virtualRegStack.HasValue) { stackOffset = aliveRegistersStack[virtualAssembler.GetFloatRegisterForVirtual(virtualReg).Value]; } } else { spillReg = virtualAssembler.GetIntSpillRegister(); if (!virtualRegStack.HasValue) { stackOffset = aliveRegistersStack[virtualAssembler.GetIntRegisterForVirtual(virtualReg).Value]; } } var argMemory = new MemoryOperand(); if (virtualRegStack.HasValue) { argMemory = new MemoryOperand( Register.BP, virtualAssembler.CalculateStackOffset(virtualRegStack.Value)); } else { argMemory = new MemoryOperand( Register.BP, -(argsStart + stackOffset) * Assembler.RegisterSize); } Assembler.Move(generatedCode, spillReg, argMemory); Assembler.Push(generatedCode, spillReg); } else { HardwareRegister argReg; int stackOffset = 0; if (argumentType.IsPrimitiveType(PrimitiveTypes.Float)) { argReg = floatArgumentRegisters[argumentIndex]; if (!virtualRegStack.HasValue) { stackOffset = aliveRegistersStack[virtualAssembler.GetFloatRegisterForVirtual(virtualReg).Value]; } } else { argReg = intArgumentRegisters[argumentIndex]; if (!virtualRegStack.HasValue) { stackOffset = aliveRegistersStack[virtualAssembler.GetIntRegisterForVirtual(virtualReg).Value]; } } var argMemory = new MemoryOperand(); if (virtualRegStack.HasValue) { argMemory = new MemoryOperand( Register.BP, virtualAssembler.CalculateStackOffset(virtualRegStack.Value)); } else { argMemory = new MemoryOperand( Register.BP, -(argsStart + stackOffset) * Assembler.RegisterSize); } Assembler.Move(generatedCode, argReg, argMemory); } }
/// <summary> /// Calculates the value of the modR/M byte and SIB bytes. /// </summary> /// <param name="regField">The modR/M regfield value.</param> /// <param name="op1">The destination operand.</param> /// <param name="op2">The source operand.</param> /// <param name="sib">A potential SIB byte to emit.</param> /// <param name="displacement">An immediate displacement to emit.</param> /// <returns>The value of the modR/M byte.</returns> private static byte?CalculateModRM(byte?regField, Operand op1, Operand op2, out byte?sib, out Operand displacement) { byte?modRM = null; displacement = null; // FIXME: Handle the SIB byte sib = null; RegisterOperand rop1 = op1 as RegisterOperand, rop2 = op2 as RegisterOperand; MemoryOperand mop1 = op1 as MemoryOperand, mop2 = op2 as MemoryOperand; // Normalize the operand order if (rop1 == null && rop2 != null) { // Swap the memory operands rop1 = rop2; rop2 = null; mop2 = mop1; mop1 = null; } if (regField != null) { modRM = (byte)(regField.Value << 3); } if (rop1 != null && rop2 != null) { // mod = 11b, reg = rop1, r/m = rop2 modRM = (byte)((3 << 6) | (rop1.Register.RegisterCode << 3) | rop2.Register.RegisterCode); } // Check for register/memory combinations else if (mop2 != null && mop2.Base != null) { // mod = 10b, reg = rop1, r/m = mop2 modRM = (byte)(modRM.GetValueOrDefault() | (2 << 6) | (byte)mop2.Base.RegisterCode); if (rop1 != null) { modRM |= (byte)(rop1.Register.RegisterCode << 3); } displacement = mop2; if (mop2.Base.RegisterCode == 4) { sib = 0xA4; } } else if (mop2 != null) { // mod = 10b, r/m = mop1, reg = rop2 modRM = (byte)(modRM.GetValueOrDefault() | 5); if (rop1 != null) { modRM |= (byte)(rop1.Register.RegisterCode << 3); } displacement = mop2; } else if (mop1 != null && mop1.Base != null) { // mod = 10b, r/m = mop1, reg = rop2 modRM = (byte)(modRM.GetValueOrDefault() | (2 << 6) | mop1.Base.RegisterCode); if (rop2 != null) { modRM |= (byte)(rop2.Register.RegisterCode << 3); } displacement = mop1; if (mop1.Base.RegisterCode == 4) { sib = 0xA4; } } else if (mop1 != null) { // mod = 10b, r/m = mop1, reg = rop2 modRM = (byte)(modRM.GetValueOrDefault() | 5); if (rop2 != null) { modRM |= (byte)(rop2.Register.RegisterCode << 3); } displacement = mop1; } else if (rop1 != null) { modRM = (byte)(modRM.GetValueOrDefault() | (3 << 6) | rop1.Register.RegisterCode); //if (op2 is SymbolOperand) // displacement = op2; } return(modRM); }
private MemoryAccess RewriteMemoryAccess(MemoryOperand mem, PrimitiveType dataWidth, Address addrInstr) { Expression ea; if (mem.Base == Registers.pc) { ea = addrInstr + mem.Offset.ToInt32(); } else { var bReg = frame.EnsureRegister(mem.Base); ea = bReg; if (mem.Offset != null) { ea = m.IAdd(bReg, Constant.Int32(mem.Offset.ToInt32())); } } return m.Load(dataWidth, ea); }
/// <summary> /// Pushes the specified instructions. /// </summary> /// <param name="ctx">The context.</param> /// <param name="op">The op.</param> /// <param name="stackSize">Size of the stack.</param> private void Push(Context ctx, Operand op, int stackSize) { if (op is MemoryOperand) { RegisterOperand rop; switch (op.StackType) { case StackTypeCode.O: goto case StackTypeCode.N; case StackTypeCode.Ptr: goto case StackTypeCode.N; case StackTypeCode.Int32: goto case StackTypeCode.N; case StackTypeCode.N: rop = new RegisterOperand(op.Type, GeneralPurposeRegister.EAX); break; case StackTypeCode.F: rop = new RegisterOperand(op.Type, SSE2Register.XMM0); break; case StackTypeCode.Int64: { SigType I4 = new SigType(CilElementType.I4); MemoryOperand mop = op as MemoryOperand; Debug.Assert(null != mop, "I8/U8 arg is not in a memory operand."); RegisterOperand eax = new RegisterOperand(I4, GeneralPurposeRegister.EAX); Operand opL, opH; LongOperandTransformationStage.SplitLongOperand(mop, out opL, out opH); //MemoryOperand opL = new MemoryOperand(I4, mop.Base, mop.Offset); //MemoryOperand opH = new MemoryOperand(I4, mop.Base, new IntPtr(mop.Offset.ToInt64() + 4)); ctx.AppendInstruction(CPUx86.Instruction.MovInstruction, eax, opL); ctx.AppendInstruction(CPUx86.Instruction.MovInstruction, new MemoryOperand(op.Type, GeneralPurposeRegister.EDX, new IntPtr(stackSize)), eax); ctx.AppendInstruction(CPUx86.Instruction.MovInstruction, eax, opH); ctx.AppendInstruction(CPUx86.Instruction.MovInstruction, new MemoryOperand(op.Type, GeneralPurposeRegister.EDX, new IntPtr(stackSize + 4)), eax); } return; default: throw new NotSupportedException(); } ctx.AppendInstruction(CPUx86.Instruction.MovInstruction, rop, op); op = rop; } else if (op is ConstantOperand && op.StackType == StackTypeCode.Int64) { Operand opL, opH; SigType I4 = new SigType(CilElementType.I4); RegisterOperand eax = new RegisterOperand(I4, GeneralPurposeRegister.EAX); LongOperandTransformationStage.SplitLongOperand(op, out opL, out opH); ctx.AppendInstruction(CPUx86.Instruction.MovInstruction, eax, opL); ctx.AppendInstruction(CPUx86.Instruction.MovInstruction, new MemoryOperand(I4, GeneralPurposeRegister.EDX, new IntPtr(stackSize)), eax); ctx.AppendInstruction(CPUx86.Instruction.MovInstruction, eax, opH); ctx.AppendInstruction(CPUx86.Instruction.MovInstruction, new MemoryOperand(I4, GeneralPurposeRegister.EDX, new IntPtr(stackSize + 4)), eax); return; } ctx.AppendInstruction(CPUx86.Instruction.MovInstruction, new MemoryOperand(op.Type, GeneralPurposeRegister.EDX, new IntPtr(stackSize)), op); }
protected virtual Address ResolveFlowInstructionTarget(MemoryOperand operand) { #if false // TODO: handle symbolic target. MemoryOperand opr = (MemoryOperand)instruction.Operands[0]; // Handle static near jump table. We recognize a jump table // heuristically if the instruction looks like the following: // // jmpn word ptr cs:[bx+3782h] // // That is, it meets the requirements that // - the instruction is JMPN // - the jump target is a word-ptr memory location // - the memory location has CS prefix // - a base register (e.g. bx) specifies the entry index // // Note that a malformed executable may create a jump table // not conforming to the above rules, or create a non-jump // table that conforms to the above rules. We do not deal with // these cases for the moment. if (instruction.Operation == Operation.JMP && opr.Size == CpuSize.Use16Bit && opr.Segment == Register.CS && opr.Base != Register.None && opr.Index == Register.None) { #if false return new XRef( type: XRefType.NearIndexedJump, source: start, target: Pointer.Invalid, dataLocation: new Pointer(start.Segment, (UInt16)opr.Displacement.Value) ); #else return new XRef( type: XRefType.NearJump, source: start, target: Address.Invalid ); #endif } #endif return Address.Invalid; }
/// <summary> /// Moves an argument to the stack /// </summary> /// <param name="compilationData">The compilation data</param> /// <param name="argumentIndex">The argument index</param> /// <param name="argumentType">The type of the argument</param> private void MoveArgumentToStack(CompilationData compilationData, int argumentIndex, VMType argumentType) { var generatedCode = compilationData.Function.GeneratedCode; int argStackOffset = -(1 + argumentIndex) * Assembler.RegisterSize; if (argumentIndex >= numRegisterArguments) { int stackArgumentIndex = this.GetStackArgumentIndex(compilationData, argumentIndex); var argStackSource = new MemoryOperand( Register.BP, Assembler.RegisterSize * (6 + stackArgumentIndex)); HardwareRegister tmpReg; if (argumentType.IsPrimitiveType(PrimitiveTypes.Float)) { tmpReg = compilationData.VirtualAssembler.GetFloatSpillRegister(); } else { tmpReg = new IntRegister(Register.AX); } Assembler.Move(generatedCode, tmpReg, argStackSource); Assembler.Move(generatedCode, new MemoryOperand(Register.BP, argStackOffset), tmpReg); } else { //var argReg = floatArgumentRegisters[argumentIndex]; HardwareRegister argReg; if (argumentType.IsPrimitiveType(PrimitiveTypes.Float)) { argReg = floatArgumentRegisters[argumentIndex]; } else { argReg = intArgumentRegisters[argumentIndex]; } Assembler.Move(generatedCode, new MemoryOperand(Register.BP, argStackOffset), argReg); } }
public SymbolicMemoryOperand(MemoryOperand opr, SymbolicTarget target) { base.Base = opr.Base; base.Displacement = opr.Displacement; base.Index = opr.Index; base.Scaling = opr.Scaling; base.Segment = opr.Segment; base.Size = opr.Size; this.Target = target; }
private void ParseMemoryFactor(MemoryOperand memOp) { Token token = lexer.GetToken(); RegisterStorage reg = RegisterStorage.None; switch (token) { default: OnError("unexpected token: " + token); return; case Token.INTEGER: totalInt += lexer.Integer; break; case Token.REGISTER: { reg = lexer.Register !; PrimitiveType width = reg.DataType; if (addrWidth == null) { addrWidth = width; } else if (addrWidth != width) { throw new ApplicationException("Conflicting address widths"); } break; } case Token.ID: { if (symtab.Equates.TryGetValue(lexer.StringLiteral.ToLower(), out int v)) { totalInt += v; } else { sym = symtab.CreateSymbol(lexer.StringLiteral); totalInt += unchecked ((int)addrBase.Offset); } break; } } if (lexer.PeekToken() == Token.TIMES) { if (reg == RegisterStorage.None) { throw new ApplicationException("Scale factor must be preceded by a register"); } lexer.GetToken(); if (memOp.Index != RegisterStorage.None) { throw new ApplicationException("Scale can only be used once in an addressing form"); } Expect(Token.INTEGER, "Expected an integer scale"); if (lexer.Integer != 1 && lexer.Integer != 2 && lexer.Integer != 4 && lexer.Integer != 8) { throw new ApplicationException("Only scales 1, 2, 4, and 8 are supported"); } memOp.Scale = (byte)lexer.Integer; memOp.Index = reg; } else if (reg != RegisterStorage.None) { if (memOp.Base == RegisterStorage.None) { memOp.Base = reg; } else if (memOp.Index == RegisterStorage.None) { memOp.Index = reg; memOp.Scale = 1; } else { throw new ApplicationException("Can't have more than two registers in an addressing form"); } } }
/// <summary> /// Rewrites two memory operand instructions /// </summary> /// <param name="memoryRewrite">The memory rewrite rule</param> /// <param name="destination">The destination</param> /// <param name="source">The source</param> private void RewriteMemory(MemoryRewrite memoryRewrite, MemoryOperand destination, MemoryOperand source, Action<IList<byte>, FloatRegister, MemoryOperand> inst1, Action<IList<byte>, MemoryOperand, FloatRegister> inst2) { var generatedCode = compilationData.Function.GeneratedCode; var spillReg = this.GetFloatSpillRegister(); if (memoryRewrite == MemoryRewrite.MemoryOnLeft) { Assembler.Move(generatedCode, spillReg, source); inst2(generatedCode, destination, spillReg); } else { Assembler.Move(generatedCode, spillReg, destination); inst1(generatedCode, spillReg, source); Assembler.Move(generatedCode, destination, spillReg); } }
public virtual string FormatOperand(MemoryOperand operand) { CpuSize size = operand.Size; string prefix = (size == CpuSize.Use8Bit) ? "byte" : (size == CpuSize.Use16Bit) ? "word" : (size == CpuSize.Use32Bit) ? "dword" : (size == CpuSize.Use64Bit) ? "qword" : (size == CpuSize.Use128Bit) ? "dqword" : ""; StringBuilder sb = new StringBuilder(); if (prefix != "") { sb.Append(prefix); sb.Append(" ptr "); } if (operand.Segment != Register.None) { FormatRegister(sb, operand.Segment); sb.Append(':'); } string strDisplacement = FormatFixableLocation(operand); sb.Append('['); if (operand.Base == Register.None) // only displacement { if (strDisplacement != null) sb.Append(strDisplacement); else FormatNumber(sb, (UInt16)operand.Displacement.Value); } else // base+index*scale+displacement { FormatRegister(sb, operand.Base); if (operand.Index != Register.None) { sb.Append('+'); FormatRegister(sb, operand.Index); if (operand.Scaling != 1) { sb.Append('*'); sb.Append(operand.Scaling); } } if (strDisplacement != null) { sb.Append('+'); sb.Append(strDisplacement); } else { int displacement = operand.Displacement.Value; if (displacement > 0) // e.g. [BX+1] { sb.Append('+'); FormatNumber(sb, (uint)displacement); } else if (displacement < 0) { sb.Append('-'); FormatNumber(sb, (uint)-displacement); } } } sb.Append(']'); return sb.ToString(); }
// Note: we need to take into account OperandSizeOverride and // AddressSizeOverride!!!!!! /// <summary> /// Decodes a memory operand encoded by the ModRM byte, SIB byte, and /// Displacement, or a register operand if MOD=3 and registerType is /// specified. /// </summary> /// <param name="reader">Instruction reader.</param> /// <param name="registerType">Type of the register to return if the /// Mod field of the ModR/M byte is 3. If this parameter is set to /// RegisterType.None, an exception is thrown if Mod=3.</param> /// <param name="operandSize">Size of the returned operand.</param> /// <param name="context">Decoding context.</param> /// <returns>The decoded memory or register operand.</returns> /// <exception cref="InvalidInstructionException">If registerType is /// set to None but the ModR/M byte encodes a register.</exception> static Operand DecodeMemoryOperand( InstructionReader reader, RegisterType registerType, CpuSize operandSize, DecoderContext context) { if (context.CpuMode == CpuMode.X64Mode) throw new NotSupportedException(); //if (operandSize == CpuSize.Default) // throw new ArgumentException("operandSize is not specified."); if (context.AddressSize != CpuSize.Use16Bit) throw new NotSupportedException("32-bit addressing mode is not supported."); ModRM modrm = reader.GetModRM(); int rm = modrm.RM; int mod = modrm.MOD; // Decode a register if MOD = (11). if (mod == 3) { // If the instruction expects a memory operand, throw an exception. if (registerType == RegisterType.None) { throw new InvalidInstructionException( "The instruction expects a memory operand, but the ModR/M byte encodes a register."); } // Treat AH-DH specially. if (registerType == RegisterType.General && operandSize == CpuSize.Use8Bit && rm >= 4) { return new RegisterOperand(new Register( RegisterType.HighByte, rm - 4, CpuSize.Use8Bit)); } else { return new RegisterOperand(new Register(registerType, rm, operandSize)); } } // Take into account segment override prefix if present. Register segment = context.SegmentOverride; // Special treatment for MOD = (00) and RM = (110). // This encodes a 16-bit sign-extended displacement. if (mod == 0 && rm == 6) { return new MemoryOperand { Size = operandSize, Segment = segment, Displacement = reader.ReadImmediate(CpuSize.Use16Bit) }; } /* Decode an indirect memory address XX[+YY][+disp]. */ MemoryOperand mem = new MemoryOperand(); mem.Size = operandSize; mem.Segment = segment; switch (rm) { case 0: /* [BX+SI] */ mem.Base = Register.BX; mem.Index = Register.SI; break; case 1: /* [BX+DI] */ mem.Base = Register.BX; mem.Index = Register.DI; break; case 2: /* [BP+SI] */ mem.Base = Register.BP; mem.Index = Register.SI; break; case 3: /* [BP+DI] */ mem.Base = Register.BP; mem.Index = Register.DI; break; case 4: /* [SI] */ mem.Base = Register.SI; break; case 5: /* [DI] */ mem.Base = Register.DI; break; case 6: /* [BP] */ mem.Base = Register.BP; break; case 7: /* [BX] */ mem.Base = Register.BX; break; } if (mod == 1) /* disp8, sign-extended */ { mem.Displacement = reader.ReadImmediate(CpuSize.Use8Bit); } else if (mod == 2) /* disp16, sign-extended */ { mem.Displacement = reader.ReadImmediate(CpuSize.Use16Bit); } return mem; }