diff --git a/src/coreclr/vm/gc_unwind_x86.inl b/src/coreclr/vm/gc_unwind_x86.inl index fa5b88b32be20b..5aaa8fa7f805e6 100644 --- a/src/coreclr/vm/gc_unwind_x86.inl +++ b/src/coreclr/vm/gc_unwind_x86.inl @@ -7,21 +7,10 @@ #define RETURN_ADDR_OFFS 1 // in DWORDS -#define X86_INSTR_TEST_ESP_SIB 0x24 -#define X86_INSTR_PUSH_0 0x6A // push 00, entire instruction is 0x6A00 -#define X86_INSTR_PUSH_IMM 0x68 // push NNNN, -#define X86_INSTR_W_PUSH_IND_IMM 0x35FF // push [NNNN] #define X86_INSTR_CALL_REL32 0xE8 // call rel32 #define X86_INSTR_W_CALL_IND_IMM 0x15FF // call [addr32] #define X86_INSTR_NOP 0x90 // nop -#define X86_INSTR_NOP2 0x9090 // 2-byte nop -#define X86_INSTR_NOP3_1 0x9090 // 1st word of 3-byte nop -#define X86_INSTR_NOP3_3 0x90 // 3rd byte of 3-byte nop -#define X86_INSTR_NOP4 0x90909090 // 4-byte nop -#define X86_INSTR_NOP5_1 0x90909090 // 1st dword of 5-byte nop -#define X86_INSTR_NOP5_5 0x90 // 5th byte of 5-byte nop #define X86_INSTR_INT3 0xCC // int3 -#define X86_INSTR_HLT 0xF4 // hlt #define X86_INSTR_PUSH_EAX 0x50 // push eax #define X86_INSTR_PUSH_EBP 0x55 // push ebp #define X86_INSTR_W_MOV_EBP_ESP 0xEC8B // mov ebp, esp diff --git a/src/coreclr/vm/i386/stublinkerx86.cpp b/src/coreclr/vm/i386/stublinkerx86.cpp index ad519187e118a2..a6238f46e797fc 100644 --- a/src/coreclr/vm/i386/stublinkerx86.cpp +++ b/src/coreclr/vm/i386/stublinkerx86.cpp @@ -35,647 +35,74 @@ #ifndef DACCESS_COMPILE - -#ifdef TARGET_AMD64 - -BOOL IsPreservedReg (X86Reg reg) -{ - UINT16 PreservedRegMask = - (1 << kRBX) - | (1 << kRBP) - | (1 << kRSI) - | (1 << kRDI) - | (1 << kR12) - | (1 << kR13) - | (1 << kR14) - | (1 << kR15); - return PreservedRegMask & (1 << reg); -} - -#endif // TARGET_AMD64 - -#ifdef TARGET_AMD64 -//----------------------------------------------------------------------- -// InstructionFormat for near Jump and short Jump -//----------------------------------------------------------------------- - -//X64EmitTailcallWithRSPAdjust -class X64NearJumpSetup : public InstructionFormat -{ - public: - X64NearJumpSetup() : InstructionFormat( InstructionFormat::k8|InstructionFormat::k32 - | InstructionFormat::k64Small | InstructionFormat::k64 - ) - { - LIMITED_METHOD_CONTRACT; - } - - virtual UINT GetSizeOfInstruction(UINT refsize, UINT variationCode) - { - LIMITED_METHOD_CONTRACT - switch (refsize) - { - case k8: - return 0; - - case k32: - return 0; - - case k64Small: - return 5; - - case k64: - return 10; - - default: - _ASSERTE(!"unexpected refsize"); - return 0; - - } - } - - virtual VOID EmitInstruction(UINT refsize, int64_t fixedUpReference, BYTE *pOutBufferRX, BYTE *pOutBufferRW, UINT variationCode, BYTE *pDataBuffer) - { - LIMITED_METHOD_CONTRACT - if (k8 == refsize) - { - // do nothing, X64NearJump will take care of this - } - else if (k32 == refsize) - { - // do nothing, X64NearJump will take care of this - } - else if (k64Small == refsize) - { - UINT64 TargetAddress = (INT64)pOutBufferRX + fixedUpReference + GetSizeOfInstruction(refsize, variationCode); - _ASSERTE(FitsInU4(TargetAddress)); - - // mov eax, imm32 ; zero-extended - pOutBufferRW[0] = 0xB8; - *((UINT32*)&pOutBufferRW[1]) = (UINT32)TargetAddress; - } - else if (k64 == refsize) - { - // mov rax, imm64 - pOutBufferRW[0] = REX_PREFIX_BASE | REX_OPERAND_SIZE_64BIT; - pOutBufferRW[1] = 0xB8; - *((UINT64*)&pOutBufferRW[2]) = (UINT64)(((INT64)pOutBufferRX) + fixedUpReference + GetSizeOfInstruction(refsize, variationCode)); - } - else - { - _ASSERTE(!"unreached"); - } - } - - virtual BOOL CanReach(UINT refsize, UINT variationCode, BOOL fExternal, INT_PTR offset) - { - STATIC_CONTRACT_NOTHROW; - STATIC_CONTRACT_GC_NOTRIGGER; - STATIC_CONTRACT_FORBID_FAULT; - - - if (fExternal) - { - switch (refsize) - { - case InstructionFormat::k8: - // For external, we don't have enough info to predict - // the offset. - return FALSE; - - case InstructionFormat::k32: - return sizeof(PVOID) <= sizeof(UINT32); - - case InstructionFormat::k64Small: - return FitsInI4(offset); - - case InstructionFormat::k64: - // intentional fallthru - case InstructionFormat::kAllowAlways: - return TRUE; - - default: - _ASSERTE(0); - return FALSE; - } - } - else - { - switch (refsize) - { - case InstructionFormat::k8: - return FitsInI1(offset); - - case InstructionFormat::k32: - return FitsInI4(offset); - - case InstructionFormat::k64Small: - // EmitInstruction emits a non-relative jmp for - // k64Small. We don't have enough info to predict the - // target address. (Even if we did, this would only - // handle the set of unsigned offsets with bit 31 set - // and no higher bits set, too uncommon/hard to test.) - return FALSE; - - case InstructionFormat::k64: - // intentional fallthru - case InstructionFormat::kAllowAlways: - return TRUE; - default: - _ASSERTE(0); - return FALSE; - } - } - } -}; - -class X64NearJumpExecute : public InstructionFormat -{ - public: - X64NearJumpExecute() : InstructionFormat( InstructionFormat::k8|InstructionFormat::k32 - | InstructionFormat::k64Small | InstructionFormat::k64 - ) - { - LIMITED_METHOD_CONTRACT; - } - - virtual UINT GetSizeOfInstruction(UINT refsize, UINT variationCode) - { - LIMITED_METHOD_CONTRACT - switch (refsize) - { - case k8: - return 2; - - case k32: - return 5; - - case k64Small: - return 3; - - case k64: - return 3; - - default: - _ASSERTE(!"unexpected refsize"); - return 0; - - } - } - - virtual VOID EmitInstruction(UINT refsize, int64_t fixedUpReference, BYTE *pOutBufferRX, BYTE *pOutBufferRW, UINT variationCode, BYTE *pDataBuffer) - { - LIMITED_METHOD_CONTRACT - if (k8 == refsize) - { - pOutBufferRW[0] = 0xeb; - *((int8_t*)(pOutBufferRW+1)) = (int8_t)fixedUpReference; - } - else if (k32 == refsize) - { - pOutBufferRW[0] = 0xe9; - *((int32_t*)(pOutBufferRW+1)) = (int32_t)fixedUpReference; - } - else if (k64Small == refsize) - { - // REX.W jmp rax - pOutBufferRW[0] = REX_PREFIX_BASE | REX_OPERAND_SIZE_64BIT; - pOutBufferRW[1] = 0xFF; - pOutBufferRW[2] = 0xE0; - } - else if (k64 == refsize) - { - // REX.W jmp rax - pOutBufferRW[0] = REX_PREFIX_BASE | REX_OPERAND_SIZE_64BIT; - pOutBufferRW[1] = 0xFF; - pOutBufferRW[2] = 0xE0; - } - else - { - _ASSERTE(!"unreached"); - } - } - - virtual BOOL CanReach(UINT refsize, UINT variationCode, BOOL fExternal, INT_PTR offset) - { - STATIC_CONTRACT_NOTHROW; - STATIC_CONTRACT_GC_NOTRIGGER; - STATIC_CONTRACT_FORBID_FAULT; - - - if (fExternal) - { - switch (refsize) - { - case InstructionFormat::k8: - // For external, we don't have enough info to predict - // the offset. - return FALSE; - - case InstructionFormat::k32: - return sizeof(PVOID) <= sizeof(UINT32); - - case InstructionFormat::k64Small: - return FitsInI4(offset); - - case InstructionFormat::k64: - // intentional fallthru - case InstructionFormat::kAllowAlways: - return TRUE; - - default: - _ASSERTE(0); - return FALSE; - } - } - else - { - switch (refsize) - { - case InstructionFormat::k8: - return FitsInI1(offset); - - case InstructionFormat::k32: - return FitsInI4(offset); - - case InstructionFormat::k64Small: - // EmitInstruction emits a non-relative jmp for - // k64Small. We don't have enough info to predict the - // target address. (Even if we did, this would only - // handle the set of unsigned offsets with bit 31 set - // and no higher bits set, too uncommon/hard to test.) - return FALSE; - - case InstructionFormat::k64: - // intentional fallthru - case InstructionFormat::kAllowAlways: - return TRUE; - default: - _ASSERTE(0); - return FALSE; - } - } - } -}; - -#endif - +#ifdef TARGET_X86 //----------------------------------------------------------------------- // InstructionFormat for near Jump and short Jump //----------------------------------------------------------------------- -class X86NearJump : public InstructionFormat -{ - public: - X86NearJump() : InstructionFormat( InstructionFormat::k8|InstructionFormat::k32 -#ifdef TARGET_AMD64 - | InstructionFormat::k64Small | InstructionFormat::k64 -#endif // TARGET_AMD64 - ) - { - LIMITED_METHOD_CONTRACT; - } - - virtual UINT GetSizeOfInstruction(UINT refsize, UINT variationCode) - { - LIMITED_METHOD_CONTRACT - switch (refsize) - { - case k8: - return 2; - - case k32: - return 5; -#ifdef TARGET_AMD64 - case k64Small: - return 5 + 2; - - case k64: - return 12; -#endif // TARGET_AMD64 - default: - _ASSERTE(!"unexpected refsize"); - return 0; - - } - } - - virtual VOID EmitInstruction(UINT refsize, int64_t fixedUpReference, BYTE *pOutBufferRX, BYTE *pOutBufferRW, UINT variationCode, BYTE *pDataBuffer) - { - LIMITED_METHOD_CONTRACT - if (k8 == refsize) - { - pOutBufferRW[0] = 0xeb; - *((int8_t*)(pOutBufferRW+1)) = (int8_t)fixedUpReference; - } - else if (k32 == refsize) - { - pOutBufferRW[0] = 0xe9; - *((int32_t*)(pOutBufferRW+1)) = (int32_t)fixedUpReference; - } -#ifdef TARGET_AMD64 - else if (k64Small == refsize) - { - UINT64 TargetAddress = (INT64)pOutBufferRX + fixedUpReference + GetSizeOfInstruction(refsize, variationCode); - _ASSERTE(FitsInU4(TargetAddress)); - - // mov eax, imm32 ; zero-extended - pOutBufferRW[0] = 0xB8; - *((UINT32*)&pOutBufferRW[1]) = (UINT32)TargetAddress; - - // jmp rax - pOutBufferRW[5] = 0xFF; - pOutBufferRW[6] = 0xE0; - } - else if (k64 == refsize) - { - // mov rax, imm64 - pOutBufferRW[0] = REX_PREFIX_BASE | REX_OPERAND_SIZE_64BIT; - pOutBufferRW[1] = 0xB8; - *((UINT64*)&pOutBufferRW[2]) = (UINT64)(((INT64)pOutBufferRX) + fixedUpReference + GetSizeOfInstruction(refsize, variationCode)); - - // jmp rax - pOutBufferRW[10] = 0xFF; - pOutBufferRW[11] = 0xE0; - } -#endif // TARGET_AMD64 - else - { - _ASSERTE(!"unreached"); - } - } - - virtual BOOL CanReach(UINT refsize, UINT variationCode, BOOL fExternal, INT_PTR offset) - { - STATIC_CONTRACT_NOTHROW; - STATIC_CONTRACT_GC_NOTRIGGER; - STATIC_CONTRACT_FORBID_FAULT; - - - if (fExternal) - { - switch (refsize) - { - case InstructionFormat::k8: - // For external, we don't have enough info to predict - // the offset. - return FALSE; - - case InstructionFormat::k32: - return sizeof(PVOID) <= sizeof(UINT32); - -#ifdef TARGET_AMD64 - case InstructionFormat::k64Small: - return FitsInI4(offset); - - case InstructionFormat::k64: - // intentional fallthru -#endif - case InstructionFormat::kAllowAlways: - return TRUE; - - default: - _ASSERTE(0); - return FALSE; - } - } - else - { - switch (refsize) - { - case InstructionFormat::k8: - return FitsInI1(offset); - - case InstructionFormat::k32: -#ifdef TARGET_AMD64 - return FitsInI4(offset); -#else - return TRUE; -#endif - -#ifdef TARGET_AMD64 - case InstructionFormat::k64Small: - // EmitInstruction emits a non-relative jmp for - // k64Small. We don't have enough info to predict the - // target address. (Even if we did, this would only - // handle the set of unsigned offsets with bit 31 set - // and no higher bits set, too uncommon/hard to test.) - return FALSE; - - case InstructionFormat::k64: - // intentional fallthru -#endif - case InstructionFormat::kAllowAlways: - return TRUE; - default: - _ASSERTE(0); - return FALSE; - } - } - } -}; - - -//----------------------------------------------------------------------- -// InstructionFormat for near call. -//----------------------------------------------------------------------- -class X86Call : public InstructionFormat -{ - public: - X86Call () - : InstructionFormat( InstructionFormat::k32 -#ifdef TARGET_AMD64 - | InstructionFormat::k64Small | InstructionFormat::k64 -#endif // TARGET_AMD64 - ) - { - LIMITED_METHOD_CONTRACT; - } - - virtual UINT GetSizeOfInstruction(UINT refsize, UINT variationCode) - { - LIMITED_METHOD_CONTRACT; - - switch (refsize) - { - case k32: - return 5; - -#ifdef TARGET_AMD64 - case k64Small: - return 5 + 2; - - case k64: - return 10 + 2; -#endif // TARGET_AMD64 - - default: - _ASSERTE(!"unexpected refsize"); - return 0; - } - } - - virtual VOID EmitInstruction(UINT refsize, int64_t fixedUpReference, BYTE *pOutBufferRX, BYTE *pOutBufferRW, UINT variationCode, BYTE *pDataBuffer) - { - LIMITED_METHOD_CONTRACT - - switch (refsize) - { - case k32: - pOutBufferRW[0] = 0xE8; - *((int32_t*)(1+pOutBufferRW)) = (int32_t)fixedUpReference; - break; - -#ifdef TARGET_AMD64 - case k64Small: - UINT64 TargetAddress; - - TargetAddress = (INT64)pOutBufferRX + fixedUpReference + GetSizeOfInstruction(refsize, variationCode); - _ASSERTE(FitsInU4(TargetAddress)); - - // mov eax, ; zero-extends - pOutBufferRW[0] = 0xB8; - *((UINT32*)&pOutBufferRW[1]) = (UINT32)TargetAddress; - - // call rax - pOutBufferRW[5] = 0xff; - pOutBufferRW[6] = 0xd0; - break; - - case k64: - // mov rax, - pOutBufferRW[0] = REX_PREFIX_BASE | REX_OPERAND_SIZE_64BIT; - pOutBufferRW[1] = 0xB8; - *((UINT64*)&pOutBufferRW[2]) = (UINT64)(((INT64)pOutBufferRX) + fixedUpReference + GetSizeOfInstruction(refsize, variationCode)); - - // call rax - pOutBufferRW[10] = 0xff; - pOutBufferRW[11] = 0xd0; - break; -#endif // TARGET_AMD64 - - default: - _ASSERTE(!"unreached"); - break; - } - } - -// For x86, the default CanReach implementation will suffice. It only needs -// to handle k32. -#ifdef TARGET_AMD64 - virtual BOOL CanReach(UINT refsize, UINT variationCode, BOOL fExternal, INT_PTR offset) - { - if (fExternal) - { - switch (refsize) - { - case InstructionFormat::k32: - // For external, we don't have enough info to predict - // the offset. - return FALSE; - - case InstructionFormat::k64Small: - return FitsInI4(offset); - - case InstructionFormat::k64: - // intentional fallthru - case InstructionFormat::kAllowAlways: - return TRUE; - - default: - _ASSERTE(0); - return FALSE; - } - } - else - { - switch (refsize) - { - case InstructionFormat::k32: - return FitsInI4(offset); - - case InstructionFormat::k64Small: - // EmitInstruction emits a non-relative jmp for - // k64Small. We don't have enough info to predict the - // target address. (Even if we did, this would only - // handle the set of unsigned offsets with bit 31 set - // and no higher bits set, too uncommon/hard to test.) - return FALSE; - - case InstructionFormat::k64: - // intentional fallthru - case InstructionFormat::kAllowAlways: - return TRUE; - default: - _ASSERTE(0); - return FALSE; - } - } - } -#endif // TARGET_AMD64 -}; - - -//----------------------------------------------------------------------- -// InstructionFormat for push imm32. -//----------------------------------------------------------------------- -class X86PushImm32 : public InstructionFormat +class X86NearJump : public InstructionFormat { public: - X86PushImm32(UINT allowedSizes) : InstructionFormat(allowedSizes) + X86NearJump() : InstructionFormat(InstructionFormat::k8|InstructionFormat::k32) { LIMITED_METHOD_CONTRACT; } virtual UINT GetSizeOfInstruction(UINT refsize, UINT variationCode) { - LIMITED_METHOD_CONTRACT; + LIMITED_METHOD_CONTRACT + switch (refsize) + { + case k8: + return 2; - return 5; - } + case k32: + return 5; - virtual VOID EmitInstruction(UINT refsize, int64_t fixedUpReference, BYTE *pOutBufferRX, BYTE *pOutBufferRW, UINT variationCode, BYTE *pDataBuffer) - { - LIMITED_METHOD_CONTRACT; + default: + _ASSERTE(!"unexpected refsize"); + return 0; - pOutBufferRW[0] = 0x68; - // only support absolute pushimm32 of the label address. The fixedUpReference is - // the offset to the label from the current point, so add to get address - *((int32_t*)(1+pOutBufferRW)) = (int32_t)(fixedUpReference); + } } -}; -#if defined(TARGET_AMD64) -//----------------------------------------------------------------------- -// InstructionFormat for lea reg, [RIP relative]. -//----------------------------------------------------------------------- -class X64LeaRIP : public InstructionFormat -{ - public: - X64LeaRIP() : InstructionFormat(InstructionFormat::k64Small) + virtual VOID EmitInstruction(UINT refsize, int64_t fixedUpReference, BYTE *pOutBufferRX, BYTE *pOutBufferRW, UINT variationCode, BYTE *pDataBuffer) { - LIMITED_METHOD_CONTRACT; + LIMITED_METHOD_CONTRACT + if (k8 == refsize) + { + pOutBufferRW[0] = 0xeb; + *((int8_t*)(pOutBufferRW+1)) = (int8_t)fixedUpReference; + } + else if (k32 == refsize) + { + pOutBufferRW[0] = 0xe9; + *((int32_t*)(pOutBufferRW+1)) = (int32_t)fixedUpReference; + } + else + { + _ASSERTE(!"unreached"); + } } - virtual UINT GetSizeOfInstruction(UINT refsize, UINT variationCode) + virtual BOOL CanReach(UINT refsize, UINT variationCode, BOOL fExternal, INT_PTR offset) { - LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; - return 7; - } - virtual BOOL CanReach(UINT refsize, UINT variationCode, BOOL fExternal, INT_PTR offset) - { if (fExternal) { switch (refsize) { - case InstructionFormat::k64Small: + case InstructionFormat::k8: // For external, we don't have enough info to predict // the offset. return FALSE; - case InstructionFormat::k64: - // intentional fallthru + case InstructionFormat::k32: + return sizeof(PVOID) <= sizeof(UINT32); + case InstructionFormat::kAllowAlways: return TRUE; @@ -688,11 +115,10 @@ class X64LeaRIP : public InstructionFormat { switch (refsize) { - case InstructionFormat::k64Small: - return FitsInI4(offset); + case InstructionFormat::k8: + return FitsInI1(offset); - case InstructionFormat::k64: - // intentional fallthru + case InstructionFormat::k32: case InstructionFormat::kAllowAlways: return TRUE; @@ -702,40 +128,10 @@ class X64LeaRIP : public InstructionFormat } } } - - virtual VOID EmitInstruction(UINT refsize, int64_t fixedUpReference, BYTE *pOutBufferRX, BYTE *pOutBufferRW, UINT variationCode, BYTE *pDataBuffer) - { - LIMITED_METHOD_CONTRACT; - - X86Reg reg = (X86Reg)variationCode; - BYTE rex = REX_PREFIX_BASE | REX_OPERAND_SIZE_64BIT; - - if (reg >= kR8) - { - rex |= REX_MODRM_REG_EXT; - reg = X86RegFromAMD64Reg(reg); - } - - pOutBufferRW[0] = rex; - pOutBufferRW[1] = 0x8D; - pOutBufferRW[2] = (BYTE)(0x05 | (reg << 3)); - // only support absolute pushimm32 of the label address. The fixedUpReference is - // the offset to the label from the current point, so add to get address - *((int32_t*)(3+pOutBufferRW)) = (int32_t)(fixedUpReference); - } }; -#endif // TARGET_AMD64 - -#if defined(TARGET_AMD64) -static BYTE gX64NearJumpSetup[sizeof(X64NearJumpSetup)]; -static BYTE gX64NearJumpExecute[sizeof(X64NearJumpExecute)]; -static BYTE gX64LeaRIP[sizeof(X64LeaRIP)]; -#endif - static BYTE gX86NearJump[sizeof(X86NearJump)]; -static BYTE gX86Call[sizeof(X86Call)]; -static BYTE gX86PushImm32[sizeof(X86PushImm32)]; +#endif // TARGET_X86 /* static */ void StubLinkerCPU::Init() { @@ -746,14 +142,9 @@ static BYTE gX86PushImm32[sizeof(X86PushImm32)]; INJECT_FAULT(COMPlusThrowOM();); } CONTRACTL_END; - new (gX86NearJump) X86NearJump(); - new (gX86Call) X86Call(); - new (gX86PushImm32) X86PushImm32(InstructionFormat::k32); -#if defined(TARGET_AMD64) - new (gX64NearJumpSetup) X64NearJumpSetup(); - new (gX64NearJumpExecute) X64NearJumpExecute(); - new (gX64LeaRIP) X64LeaRIP(); +#ifdef TARGET_X86 + new (gX86NearJump) X86NearJump(); #endif } @@ -834,44 +225,6 @@ VOID StubLinkerCPU::X86EmitPushImmPtr(LPVOID value BIT64_ARG(X86Reg tmpReg /*=kR X86EmitPushImm32((UINT_PTR) value); } -#endif // TARGET_X86 - -//--------------------------------------------------------------- -// Emits: -// XOR , -//--------------------------------------------------------------- -VOID StubLinkerCPU::X86EmitZeroOutReg(X86Reg reg) -{ - STANDARD_VM_CONTRACT; - -#ifdef TARGET_AMD64 - // 32-bit results are zero-extended, so we only need the REX byte if - // it's an extended register. - if (reg >= kR8) - { - Emit8(REX_PREFIX_BASE | REX_MODRM_REG_EXT | REX_MODRM_RM_EXT); - reg = X86RegFromAMD64Reg(reg); - } -#endif - Emit8(0x33); - Emit8(static_cast(0xc0 | (reg << 3) | reg)); -} - -//--------------------------------------------------------------- -// Emits: -// jmp [reg] -//--------------------------------------------------------------- -VOID StubLinkerCPU::X86EmitJumpReg(X86Reg reg) -{ - CONTRACTL - { - STANDARD_VM_CHECK; - } - CONTRACTL_END; - - Emit8(0xff); - Emit8(static_cast(0xe0) | static_cast(reg)); -} //--------------------------------------------------------------- @@ -884,6 +237,7 @@ VOID StubLinkerCPU::X86EmitNearJump(CodeLabel *target) STANDARD_VM_CONTRACT; EmitLabelRef(target, reinterpret_cast(gX86NearJump), 0); } +#endif // TARGET_X86 //--------------------------------------------------------------- @@ -913,10 +267,7 @@ VOID StubLinkerCPU::X86EmitIndexRegStore(X86Reg dstreg, { STANDARD_VM_CONTRACT; - if (dstreg != kESP_Unsafe) - X86EmitOffsetModRM(0x89, srcreg, dstreg, ofs); - else - X86EmitOp(0x89, srcreg, (X86Reg)kESP_Unsafe, ofs); + X86EmitOffsetModRM(0x89, srcreg, dstreg, ofs); } #ifdef TARGET_X86 @@ -928,32 +279,11 @@ VOID StubLinkerCPU::X86EmitIndexPush(X86Reg srcreg, int32_t ofs) { STANDARD_VM_CONTRACT; - if(srcreg != kESP_Unsafe) - X86EmitOffsetModRM(0xff, (X86Reg)0x6, srcreg, ofs); - else - X86EmitOp(0xff,(X86Reg)0x6, srcreg, ofs); - + X86EmitOffsetModRM(0xff, (X86Reg)0x6, srcreg, ofs); Push(sizeof(void*)); } -//--------------------------------------------------------------- -// Emits: -// pop dword ptr [ + ] -//--------------------------------------------------------------- -VOID StubLinkerCPU::X86EmitIndexPop(X86Reg srcreg, int32_t ofs) -{ - STANDARD_VM_CONTRACT; - - if(srcreg != kESP_Unsafe) - X86EmitOffsetModRM(0x8f, (X86Reg)0x0, srcreg, ofs); - else - X86EmitOp(0x8f,(X86Reg)0x0, srcreg, ofs); - - Pop(sizeof(void*)); -} - - //--------------------------------------------------------------- // Emits: // add esp, IMM @@ -1023,32 +353,34 @@ VOID StubLinkerCPU::X86EmitAddReg(X86Reg reg, INT32 imm32) #if defined(TARGET_AMD64) //--------------------------------------------------------------- -// movdqa destXmmreg, srcXmmReg +// movaps destXmmreg, srcXmmReg //--------------------------------------------------------------- VOID StubLinkerCPU::X64EmitMovXmmXmm(X86Reg destXmmreg, X86Reg srcXmmReg) { STANDARD_VM_CONTRACT; + // There are several that could be used to mov xmm registers. MovAps is // what C++ compiler uses so let's use it here too. - X86EmitR2ROp(X86_INSTR_MOVAPS_R_RM, destXmmreg, srcXmmReg, k32BitOp); -} -//--------------------------------------------------------------- -// movdqa XmmN, [baseReg + offset] -//--------------------------------------------------------------- -VOID StubLinkerCPU::X64EmitMovdqaFromMem(X86Reg Xmmreg, X86Reg baseReg, int32_t ofs) -{ - STANDARD_VM_CONTRACT; - X64EmitMovXmmWorker(0x66, 0x6F, Xmmreg, baseReg, ofs); -} + BYTE rex = 0; -//--------------------------------------------------------------- -// movdqa [baseReg + offset], XmmN -//--------------------------------------------------------------- -VOID StubLinkerCPU::X64EmitMovdqaToMem(X86Reg Xmmreg, X86Reg baseReg, int32_t ofs) -{ - STANDARD_VM_CONTRACT; - X64EmitMovXmmWorker(0x66, 0x7F, Xmmreg, baseReg, ofs); + if (srcXmmReg >= kR8) + { + rex |= REX_MODRM_RM_EXT; + srcXmmReg = X86RegFromAMD64Reg(srcXmmReg); + } + + if (destXmmreg >= kR8) + { + rex |= REX_MODRM_REG_EXT; + destXmmreg = X86RegFromAMD64Reg(destXmmreg); + } + + if (rex) + Emit8(REX_PREFIX_BASE | rex); + + Emit16(0x280F); + Emit8(static_cast(0300 | (destXmmreg << 3) | srcXmmReg)); } //--------------------------------------------------------------- @@ -1260,71 +592,11 @@ VOID StubLinkerCPU::X86EmitOffsetModRM(BYTE opcode, X86Reg opcodereg, X86Reg ind } } -//--------------------------------------------------------------- -// Emits a MOD/RM for accessing a dword at [ + * + ofs32] -//--------------------------------------------------------------- -VOID StubLinkerCPU::X86EmitOffsetModRmSIB(BYTE opcode, X86Reg opcodeOrReg, X86Reg baseReg, X86Reg indexReg, int32_t scale, int32_t ofs) -{ - CONTRACTL - { - STANDARD_VM_CHECK; - PRECONDITION(scale == 1 || scale == 2 || scale == 4 || scale == 8); - PRECONDITION(indexReg != kESP_Unsafe); - } - CONTRACTL_END; - - BYTE codeBuffer[8]; - BYTE* code = codeBuffer; - int nBytes = 0; - -#ifdef TARGET_AMD64 - _ASSERTE(!"NYI"); -#endif - code[0] = opcode; - nBytes++; - - BYTE scaleEnc = 0; - switch(scale) - { - case 1: scaleEnc = 0; break; - case 2: scaleEnc = 1; break; - case 4: scaleEnc = 2; break; - case 8: scaleEnc = 3; break; - default: _ASSERTE(!"Unexpected"); - } - - BYTE sib = static_cast((scaleEnc << 6) | (indexReg << 3) | baseReg); - - if (FitsInI1(ofs)) - { - code[1] = static_cast(0x44 | (opcodeOrReg << 3)); - code[2] = sib; - code[3] = (BYTE)ofs; - nBytes += 3; - EmitBytes(codeBuffer, nBytes); - } - else - { - code[1] = static_cast(0x84 | (opcodeOrReg << 3)); - code[2] = sib; - *(int32_t*)(&code[3]) = ofs; - nBytes += 6; - EmitBytes(codeBuffer, nBytes); - } -} - - VOID StubLinkerCPU::X86EmitRegLoad(X86Reg reg, UINT_PTR imm) { STANDARD_VM_CONTRACT; - if (!imm) - { - X86EmitZeroOutReg(reg); - return; - } - UINT cbimm = sizeof(void*); #ifdef TARGET_AMD64 @@ -1512,107 +784,19 @@ VOID StubLinkerCPU::X86EmitOp(WORD opcode, } -// Emits -// -// opcode altreg, modrmreg -// -// or -// -// opcode modrmreg, altreg -// -// (the opcode determines which one comes first) -// -// For single-operand opcodes, "altreg" actually selects -// an operation rather than a register. - -VOID StubLinkerCPU::X86EmitR2ROp (WORD opcode, - X86Reg altreg, - X86Reg modrmreg - AMD64_ARG(X86OperandSize OperandSize /*= k64BitOp*/) - ) -{ - CONTRACTL - { - STANDARD_VM_CHECK; - - // All 2-byte opcodes start with 0x0f. - PRECONDITION(!(opcode >> 8) || (opcode & 0xff) == 0x0f); - - PRECONDITION( ((UINT)altreg) < NumX86Regs ); - PRECONDITION( ((UINT)modrmreg) < NumX86Regs ); - } - CONTRACTL_END; - -#ifdef TARGET_AMD64 - BYTE rex = 0; - - if (modrmreg >= kR8) - { - rex |= REX_MODRM_RM_EXT; - modrmreg = X86RegFromAMD64Reg(modrmreg); - } - - if (altreg >= kR8) - { - rex |= REX_MODRM_REG_EXT; - altreg = X86RegFromAMD64Reg(altreg); - } - - if (k64BitOp == OperandSize) - rex |= REX_OPERAND_SIZE_64BIT; - - if (rex) - Emit8(REX_PREFIX_BASE | rex); -#endif // TARGET_AMD64 - - Emit8((BYTE)opcode); - - if (opcode >> 8) - Emit8(opcode >> 8); - - Emit8(static_cast(0300 | (altreg << 3) | modrmreg)); -} - - +#ifdef TARGET_X86 //--------------------------------------------------------------- // Emits: // op altreg, [esp+ofs] //--------------------------------------------------------------- VOID StubLinkerCPU::X86EmitEspOffset(BYTE opcode, X86Reg altreg, - int32_t ofs - AMD64_ARG(X86OperandSize OperandSize /*= k64BitOp*/) - ) + int32_t ofs) { STANDARD_VM_CONTRACT; - BYTE codeBuffer[8]; + BYTE codeBuffer[7]; BYTE *code = codeBuffer; - int nBytes; - -#ifdef TARGET_AMD64 - BYTE rex = 0; - - if (k64BitOp == OperandSize) - rex |= REX_OPERAND_SIZE_64BIT; - - if (altreg >= kR8) - { - rex |= REX_MODRM_REG_EXT; - altreg = X86RegFromAMD64Reg(altreg); - } - - if (rex) - { - *code = (REX_PREFIX_BASE | rex); - code++; - nBytes = 1; - } - else -#endif // TARGET_AMD64 - { - nBytes = 0; - } code[0] = opcode; BYTE modrm = static_cast((altreg << 3) | 004); @@ -1620,51 +804,24 @@ VOID StubLinkerCPU::X86EmitEspOffset(BYTE opcode, { code[1] = modrm; code[2] = 0044; - EmitBytes(codeBuffer, 3 + nBytes); + EmitBytes(codeBuffer, 3); } else if (FitsInI1(ofs)) { code[1] = 0x40|modrm; code[2] = 0044; code[3] = (BYTE)ofs; - EmitBytes(codeBuffer, 4 + nBytes); + EmitBytes(codeBuffer, 4); } else { code[1] = 0x80|modrm; code[2] = 0044; *((int32_t*)(3+code)) = ofs; - EmitBytes(codeBuffer, 7 + nBytes); + EmitBytes(codeBuffer, 7); } - } - -#ifdef _DEBUG -//--------------------------------------------------------------- -// Emits: -// mov ,0xcccccccc -//--------------------------------------------------------------- -VOID StubLinkerCPU::X86EmitDebugTrashReg(X86Reg reg) -{ - STANDARD_VM_CONTRACT; - -#ifdef TARGET_AMD64 - BYTE rex = REX_PREFIX_BASE | REX_OPERAND_SIZE_64BIT; - - if (reg >= kR8) - { - rex |= REX_OPCODE_REG_EXT; - reg = X86RegFromAMD64Reg(reg); - } - Emit8(rex); - Emit8(0xb8|reg); - Emit64(0xcccccccccccccccc); -#else - Emit8(static_cast(0xb8 | reg)); - Emit32(0xcccccccc); #endif -} -#endif //_DEBUG // Get X86Reg indexes of argument registers based on offset into ArgumentRegister @@ -2047,7 +1204,8 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) X86EmitAddReg(kR11, DelegateObject::GetOffsetOfMethodPtrAux()); // Now jump to real target // jmp r10 - X86EmitR2ROp(0xff, (X86Reg)4, kR10); + static const BYTE bjmpr10[] = { 0x41, 0xff, 0xe2 }; + EmitBytes(bjmpr10, sizeof(bjmpr10)); #else // TARGET_AMD64 diff --git a/src/coreclr/vm/i386/stublinkerx86.h b/src/coreclr/vm/i386/stublinkerx86.h index bc90a14d52c956..9547cab0746e11 100644 --- a/src/coreclr/vm/i386/stublinkerx86.h +++ b/src/coreclr/vm/i386/stublinkerx86.h @@ -22,22 +22,9 @@ extern PCODE GetPreStubEntryPoint(); #define X86_INSTR_MOV_EAX_ECX_IND 0x018b // mov eax, [ecx] #define X86_INSTR_CMP_IND_ECX_IMM32 0x3981 // cmp [ecx], imm32 -#define X86_INSTR_MOV_AL 0xB0 // mov al, imm8 -#define X86_INSTR_JMP_REL8 0xEB // jmp short rel8 - -#define X86_INSTR_NOP 0x90 // nop #define X86_INSTR_NOP3_1 0x9090 // 1st word of 3-byte nop #define X86_INSTR_NOP3_3 0x90 // 3rd byte of 3-byte nop #define X86_INSTR_INT3 0xCC // int 3 -#define X86_INSTR_HLT 0xF4 // hlt - -#define X86_INSTR_MOVAPS_R_RM 0x280F // movaps xmm1, xmm2/mem128 -#define X86_INSTR_MOVAPS_RM_R 0x290F // movaps xmm1/mem128, xmm2 -#define X86_INSTR_MOVLPS_R_RM 0x120F // movlps xmm1, xmm2/mem128 -#define X86_INSTR_MOVLPS_RM_R 0x130F // movlps xmm1/mem128, xmm2 -#define X86_INSTR_MOVUPS_R_RM 0x100F // movups xmm1, xmm2/mem128 -#define X86_INSTR_MOVUPS_RM_R 0x110F // movups xmm1/mem128, xmm2 -#define X86_INSTR_XORPS 0x570F // xorps xmm1, xmm2/mem128 //---------------------------------------------------------------------- // Encodes X86 registers. The numbers are chosen to match Intel's opcode @@ -101,12 +88,6 @@ enum X86Reg : UCHAR }; -// Use this only if you are absolutely sure that the instruction format -// handles it. This is not declared as X86Reg so that users are forced -// to add a cast and think about what exactly they are doing. -const int kESP_Unsafe = 4; - - //---------------------------------------------------------------------- // StubLinker with extensions for generating X86 code. //---------------------------------------------------------------------- @@ -126,15 +107,15 @@ class StubLinkerCPU : public StubLinker VOID X86EmitMovRegReg(X86Reg destReg, X86Reg srcReg); +#ifdef TARGET_X86 VOID X86EmitPushReg(X86Reg reg); VOID X86EmitPopReg(X86Reg reg); VOID X86EmitPushImm32(UINT value); VOID X86EmitPushImmPtr(LPVOID value BIT64_ARG(X86Reg tmpReg = kR10)); +#endif #ifdef TARGET_AMD64 VOID X64EmitMovXmmXmm(X86Reg destXmmreg, X86Reg srcXmmReg); - VOID X64EmitMovdqaFromMem(X86Reg Xmmreg, X86Reg baseReg, int32_t ofs = 0); - VOID X64EmitMovdqaToMem(X86Reg Xmmreg, X86Reg baseReg, int32_t ofs = 0); VOID X64EmitMovSDFromMem(X86Reg Xmmreg, X86Reg baseReg, int32_t ofs = 0); VOID X64EmitMovSDToMem(X86Reg Xmmreg, X86Reg baseReg, int32_t ofs = 0); VOID X64EmitMovSSFromMem(X86Reg Xmmreg, X86Reg baseReg, int32_t ofs = 0); @@ -146,26 +127,23 @@ class StubLinkerCPU : public StubLinker VOID X64EmitMovqWorker(BYTE opcode, X86Reg Xmmreg, X86Reg reg); #endif - VOID X86EmitZeroOutReg(X86Reg reg); - VOID X86EmitJumpReg(X86Reg reg); - VOID X86EmitOffsetModRM(BYTE opcode, X86Reg altreg, X86Reg indexreg, int32_t ofs); - VOID X86EmitOffsetModRmSIB(BYTE opcode, X86Reg opcodeOrReg, X86Reg baseReg, X86Reg indexReg, int32_t scale, int32_t ofs); +#ifdef TARGET_X86 VOID X86EmitNearJump(CodeLabel *pTarget); +#endif VOID X86EmitIndexRegLoad(X86Reg dstreg, X86Reg srcreg, int32_t ofs = 0); VOID X86EmitIndexRegStore(X86Reg dstreg, int32_t ofs, X86Reg srcreg); +#ifdef TARGET_X86 VOID X86EmitIndexPush(X86Reg srcreg, int32_t ofs); - VOID X86EmitIndexPop(X86Reg srcreg, int32_t ofs); VOID X86EmitAddEsp(INT32 imm32); VOID X86EmitEspOffset(BYTE opcode, X86Reg altreg, - int32_t ofs - AMD64_ARG(X86OperandSize OperandSize = k64BitOp) - ); + int32_t ofs); +#endif // Emits the most efficient form of the operation: // @@ -209,25 +187,6 @@ class StubLinkerCPU : public StubLinker } #endif // TARGET_AMD64 - // Emits - // - // opcode altreg, modrmreg - // - // or - // - // opcode modrmreg, altreg - // - // (the opcode determines which one comes first) - // - // For single-operand opcodes, "altreg" actually selects - // an operation rather than a register. - - VOID X86EmitR2ROp(WORD opcode, - X86Reg altreg, - X86Reg modrmreg - AMD64_ARG(X86OperandSize OperandSize = k64BitOp) - ); - VOID X86EmitRegLoad(X86Reg reg, UINT_PTR imm); VOID X86_64BitOperands () @@ -255,10 +214,6 @@ class StubLinkerCPU : public StubLinker // Emits code to adjust for a static delegate target. VOID EmitShuffleThunk(struct ShuffleEntry *pShuffleEntryArray); -#ifdef _DEBUG - VOID X86EmitDebugTrashReg(X86Reg reg); -#endif - public: static void Init();