From b90516fe443d50cacfe6635d8c1be595d41b8a90 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Tue, 29 Nov 2016 18:57:18 -0800 Subject: [PATCH] Fix x86 encoder to use 64-bit type to accumulate opcode/prefix bits The encoder was using size_t, a 32-bit type on x86, to accumulate opcode and prefix bits to emit. AVX support uses 3 bytes for prefixes that are higher than the 32-bit type can handle. So, change all code byte related types from size_t to a new code_t, defined as "unsigned __int64" on RyuJIT x86 (there is precedence for this type on the ARM architectures). Fixes #8331 --- src/jit/emit.cpp | 60 +++++++++++- src/jit/emit.h | 12 +++ src/jit/emitxarch.cpp | 222 +++++++++++++++++++++--------------------- src/jit/emitxarch.h | 88 ++++++++++------- 4 files changed, 233 insertions(+), 149 deletions(-) diff --git a/src/jit/emit.cpp b/src/jit/emit.cpp index d93f505d9647..0929b7392ef2 100644 --- a/src/jit/emit.cpp +++ b/src/jit/emit.cpp @@ -6038,7 +6038,7 @@ unsigned char emitter::emitOutputLong(BYTE* dst, ssize_t val) #ifdef DEBUG if (emitComp->opts.dspEmit) { - printf("; emit_long 0%08XH\n", val); + printf("; emit_long 0%08XH\n", (int)val); } #ifdef _TARGET_AMD64_ // if we're emitting code bytes, ensure that we've already emitted the rex prefix! @@ -6062,9 +6062,9 @@ unsigned char emitter::emitOutputSizeT(BYTE* dst, ssize_t val) if (emitComp->opts.dspEmit) { #ifdef _TARGET_AMD64_ - printf("; emit_size_t 0%016llXH\n", (size_t)val); + printf("; emit_size_t 0%016llXH\n", val); #else // _TARGET_AMD64_ - printf("; emit_size_t 0%08XH\n", (size_t)val); + printf("; emit_size_t 0%08XH\n", val); #endif // _TARGET_AMD64_ } #endif // DEBUG @@ -6072,6 +6072,60 @@ unsigned char emitter::emitOutputSizeT(BYTE* dst, ssize_t val) return sizeof(size_t); } +//------------------------------------------------------------------------ +// Wrappers to emitOutputByte, emitOutputWord, emitOutputLong, emitOutputSizeT +// that take unsigned __int64 or size_t type instead of ssize_t. Used on RyuJIT/x86. +// +// Arguments: +// dst - passed through +// val - passed through +// +// Return Value: +// Same as wrapped function. +// + +#if !defined(LEGACY_BACKEND) && defined(_TARGET_X86_) +unsigned char emitter::emitOutputByte(BYTE* dst, size_t val) +{ + return emitOutputByte(dst, (ssize_t)val); +} + +unsigned char emitter::emitOutputWord(BYTE* dst, size_t val) +{ + return emitOutputWord(dst, (ssize_t)val); +} + +unsigned char emitter::emitOutputLong(BYTE* dst, size_t val) +{ + return emitOutputLong(dst, (ssize_t)val); +} + +unsigned char emitter::emitOutputSizeT(BYTE* dst, size_t val) +{ + return emitOutputSizeT(dst, (ssize_t)val); +} + +unsigned char emitter::emitOutputByte(BYTE* dst, unsigned __int64 val) +{ + return emitOutputByte(dst, (ssize_t)val); +} + +unsigned char emitter::emitOutputWord(BYTE* dst, unsigned __int64 val) +{ + return emitOutputWord(dst, (ssize_t)val); +} + +unsigned char emitter::emitOutputLong(BYTE* dst, unsigned __int64 val) +{ + return emitOutputLong(dst, (ssize_t)val); +} + +unsigned char emitter::emitOutputSizeT(BYTE* dst, unsigned __int64 val) +{ + return emitOutputSizeT(dst, (ssize_t)val); +} +#endif // !defined(LEGACY_BACKEND) && defined(_TARGET_X86_) + /***************************************************************************** * * Given a block cookie and a code position, return the actual code offset; diff --git a/src/jit/emit.h b/src/jit/emit.h index b3716b81bf43..5b1a39537910 100644 --- a/src/jit/emit.h +++ b/src/jit/emit.h @@ -1664,6 +1664,18 @@ class emitter unsigned char emitOutputLong(BYTE* dst, ssize_t val); unsigned char emitOutputSizeT(BYTE* dst, ssize_t val); +#if !defined(LEGACY_BACKEND) && defined(_TARGET_X86_) + unsigned char emitOutputByte(BYTE* dst, size_t val); + unsigned char emitOutputWord(BYTE* dst, size_t val); + unsigned char emitOutputLong(BYTE* dst, size_t val); + unsigned char emitOutputSizeT(BYTE* dst, size_t val); + + unsigned char emitOutputByte(BYTE* dst, unsigned __int64 val); + unsigned char emitOutputWord(BYTE* dst, unsigned __int64 val); + unsigned char emitOutputLong(BYTE* dst, unsigned __int64 val); + unsigned char emitOutputSizeT(BYTE* dst, unsigned __int64 val); +#endif // !defined(LEGACY_BACKEND) && defined(_TARGET_X86_) + size_t emitIssue1Instr(insGroup* ig, instrDesc* id, BYTE** dp); size_t emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp); diff --git a/src/jit/emitxarch.cpp b/src/jit/emitxarch.cpp index fdfe5d4fa9af..1a15175681f7 100644 --- a/src/jit/emitxarch.cpp +++ b/src/jit/emitxarch.cpp @@ -57,7 +57,9 @@ bool emitter::IsAVXInstruction(instruction ins) #endif } +#ifdef _TARGET_AMD64_ #define REX_PREFIX_MASK 0xFF00000000LL +#endif // _TARGET_AMD64_ #ifdef FEATURE_AVX_SUPPORT // Returns true if the AVX instruction is a binary operator that requires 3 operands. @@ -180,8 +182,9 @@ bool emitter::TakesVexPrefix(instruction ins) // prefix. Based on 'attr' param we could add 2-byte VEX prefix in case of scalar // and AVX-128 bit operations. #define DEFAULT_3BYTE_VEX_PREFIX 0xC4E07800000000ULL -#define LBIT_IN_3BYTE_VEX_PREFIX 0X00000400000000ULL -size_t emitter::AddVexPrefix(instruction ins, size_t code, emitAttr attr) +#define DEFAULT_3BYTE_VEX_PREFIX_MASK 0xFFFFFF00000000ULL +#define LBIT_IN_3BYTE_VEX_PREFIX 0x00000400000000ULL +emitter::code_t emitter::AddVexPrefix(instruction ins, code_t code, emitAttr attr) { // Only AVX instructions require VEX prefix assert(IsAVXInstruction(ins)); @@ -190,6 +193,7 @@ size_t emitter::AddVexPrefix(instruction ins, size_t code, emitAttr attr) assert(!hasVexPrefix(code)); // Set L bit to 1 in case of instructions that operate on 256-bits. + assert((code & DEFAULT_3BYTE_VEX_PREFIX_MASK) == 0); code |= DEFAULT_3BYTE_VEX_PREFIX; if (attr == EA_32BYTE) { @@ -326,25 +330,25 @@ bool IsXMMReg(regNumber reg) } // Returns bits to be encoded in instruction for the given register. -regNumber RegEncoding(regNumber reg) +unsigned RegEncoding(regNumber reg) { #ifndef LEGACY_BACKEND // XMM registers do not share the same reg numbers as integer registers. // But register encoding of integer and XMM registers is the same. // Therefore, subtract XMMBASE from regNumber to get the register encoding // in case of XMM registers. - return (regNumber)((IsXMMReg(reg) ? reg - XMMBASE : reg) & 0x7); + return (unsigned)((IsXMMReg(reg) ? reg - XMMBASE : reg) & 0x7); #else // LEGACY_BACKEND // Legacy X86: XMM registers share the same reg numbers as integer registers and // hence nothing to do to get reg encoding. - return (regNumber)(reg & 0x7); + return (unsigned)(reg & 0x7); #endif // LEGACY_BACKEND } // Utility routines that abstract the logic of adding REX.W, REX.R, REX.X, REX.B and REX prefixes // SSE2: separate 1-byte prefix gets added before opcode. // AVX: specific bits within VEX prefix need to be set in bit-inverted form. -size_t emitter::AddRexWPrefix(instruction ins, size_t code) +emitter::code_t emitter::AddRexWPrefix(instruction ins, code_t code) { #ifdef _TARGET_AMD64_ if (UseAVX() && IsAVXInstruction(ins)) @@ -365,7 +369,7 @@ size_t emitter::AddRexWPrefix(instruction ins, size_t code) #ifdef _TARGET_AMD64_ -size_t emitter::AddRexRPrefix(instruction ins, size_t code) +emitter::code_t emitter::AddRexRPrefix(instruction ins, code_t code) { if (UseAVX() && IsAVXInstruction(ins)) { @@ -379,7 +383,7 @@ size_t emitter::AddRexRPrefix(instruction ins, size_t code) return code | 0x4400000000ULL; } -size_t emitter::AddRexXPrefix(instruction ins, size_t code) +emitter::code_t emitter::AddRexXPrefix(instruction ins, code_t code) { if (UseAVX() && IsAVXInstruction(ins)) { @@ -393,7 +397,7 @@ size_t emitter::AddRexXPrefix(instruction ins, size_t code) return code | 0x4200000000ULL; } -size_t emitter::AddRexBPrefix(instruction ins, size_t code) +emitter::code_t emitter::AddRexBPrefix(instruction ins, code_t code) { if (UseAVX() && IsAVXInstruction(ins)) { @@ -408,7 +412,7 @@ size_t emitter::AddRexBPrefix(instruction ins, size_t code) } // Adds REX prefix (0x40) without W, R, X or B bits set -size_t emitter::AddRexPrefix(instruction ins, size_t code) +emitter::code_t emitter::AddRexPrefix(instruction ins, code_t code) { assert(!UseAVX() || !IsAVXInstruction(ins)); return code | 0x4000000000ULL; @@ -434,14 +438,14 @@ bool isPrefix(BYTE b) } // Outputs VEX prefix (in case of AVX instructions) and REX.R/X/W/B otherwise. -unsigned emitter::emitOutputRexOrVexPrefixIfNeeded(instruction ins, BYTE* dst, size_t& code) +unsigned emitter::emitOutputRexOrVexPrefixIfNeeded(instruction ins, BYTE* dst, code_t& code) { -#ifdef _TARGET_AMD64_ // TODO-x86: This needs to be enabled for AVX support on x86. +#ifdef FEATURE_AVX_SUPPORT if (hasVexPrefix(code)) { // Only AVX instructions should have a VEX prefix assert(UseAVX() && IsAVXInstruction(ins)); - size_t vexPrefix = (code >> 32) & 0x00FFFFFF; + code_t vexPrefix = (code >> 32) & 0x00FFFFFF; code &= 0x00000000FFFFFFFFLL; WORD leadingBytes = 0; @@ -534,7 +538,10 @@ unsigned emitter::emitOutputRexOrVexPrefixIfNeeded(instruction ins, BYTE* dst, s emitOutputByte(dst + 2, vexPrefix & 0xFF); return 3; } - else if (code > 0x00FFFFFFFFLL) +#endif // FEATURE_AVX_SUPPORT + +#ifdef _TARGET_AMD64_ + if (code > 0x00FFFFFFFFLL) { BYTE prefix = (code >> 32) & 0xFF; noway_assert(prefix >= 0x40 && prefix <= 0x4F); @@ -573,13 +580,13 @@ unsigned emitter::emitOutputRexOrVexPrefixIfNeeded(instruction ins, BYTE* dst, s { // 3 prefixes were rex = rr, check = c1, check2 = c2 encoded as 0xrrc1c2XXXX // Change to c2rrc1XXXX, and emit check2 now - code = (((size_t)prefix << 24) | ((size_t)check << 16) | (code & 0x0000FFFFLL)); + code = (((code_t)prefix << 24) | ((code_t)check << 16) | (code & 0x0000FFFFLL)); } else { // 2 prefixes were rex = rr, check2 = c2 encoded as 0xrrXXc2XXXX, (check is part of the opcode) // Change to c2XXrrXXXX, and emit check2 now - code = (((size_t)check << 24) | ((size_t)prefix << 16) | (code & 0x0000FFFFLL)); + code = (((code_t)check << 24) | ((code_t)prefix << 16) | (code & 0x0000FFFFLL)); } return emitOutputByte(dst, check2); } @@ -623,7 +630,6 @@ void emitter::emitOutputPreEpilogNOP() // Size of rex prefix in bytes unsigned emitter::emitGetRexPrefixSize(instruction ins) { - // In case of AVX instructions, REX prefixes are part of VEX prefix. // And hence requires no additional byte to encode REX prefixes. if (IsAVXInstruction(ins)) @@ -660,7 +666,7 @@ unsigned emitter::emitGetVexPrefixSize(instruction ins, emitAttr attr) //=(opcodeSize - ExtrabytesSize) + vexPrefixSize //=opcodeSize + (vexPrefixSize - ExtrabytesSize) //=opcodeSize + vexPrefixAdjustedSize -unsigned emitter::emitGetVexPrefixAdjustedSize(instruction ins, emitAttr attr, size_t code) +unsigned emitter::emitGetVexPrefixAdjustedSize(instruction ins, emitAttr attr, code_t code) { #ifdef FEATURE_AVX_SUPPORT if (IsAVXInstruction(ins)) @@ -704,19 +710,19 @@ unsigned emitter::emitGetVexPrefixAdjustedSize(instruction ins, emitAttr attr, s } // Get size of rex or vex prefix emitted in code -unsigned emitter::emitGetPrefixSize(size_t code) +unsigned emitter::emitGetPrefixSize(code_t code) { -#ifdef FEATURE_AVX_SUPPORT - if (code & VEX_PREFIX_MASK_3BYTE) + if (hasVexPrefix(code)) { return 3; } - else -#endif - if (code & REX_PREFIX_MASK) + +#ifdef _TARGET_AMD64_ + if (code & REX_PREFIX_MASK) { return 1; } +#endif // _TARGET_AMD64_ return 0; } @@ -1088,7 +1094,7 @@ size_t insCodesMR[] = // clang-format on // Returns true iff the give CPU instruction has an MR encoding. -inline size_t hasCodeMR(instruction ins) +inline bool hasCodeMR(instruction ins) { assert((unsigned)ins < sizeof(insCodesMR) / sizeof(insCodesMR[0])); return ((insCodesMR[ins] != BAD_CODE)); @@ -1113,7 +1119,7 @@ inline size_t insCodeMR(instruction ins) * part of an opcode. */ -inline unsigned emitter::insEncodeReg012(instruction ins, regNumber reg, emitAttr size, size_t* code) +inline unsigned emitter::insEncodeReg012(instruction ins, regNumber reg, emitAttr size, code_t* code) { assert(reg < REG_STK); @@ -1136,16 +1142,16 @@ inline unsigned emitter::insEncodeReg012(instruction ins, regNumber reg, emitAtt } #endif // _TARGET_AMD64_ - reg = RegEncoding(reg); - assert(reg < 8); - return reg; + unsigned regBits = RegEncoding(reg); #else // LEGACY_BACKEND - assert(reg < 8); - return reg; + unsigned regBits = reg; #endif // LEGACY_BACKEND + + assert(regBits < 8); + return regBits; } /***************************************************************************** @@ -1154,7 +1160,7 @@ inline unsigned emitter::insEncodeReg012(instruction ins, regNumber reg, emitAtt * part of an opcode. */ -inline unsigned emitter::insEncodeReg345(instruction ins, regNumber reg, emitAttr size, size_t* code) +inline unsigned emitter::insEncodeReg345(instruction ins, regNumber reg, emitAttr size, code_t* code) { assert(reg < REG_STK); @@ -1177,14 +1183,16 @@ inline unsigned emitter::insEncodeReg345(instruction ins, regNumber reg, emitAtt } #endif // _TARGET_AMD64_ - reg = RegEncoding(reg); - assert(reg < 8); - return (reg << 3); + unsigned regBits = RegEncoding(reg); + +#else // LEGACY_BACKEND + + unsigned regBits = reg; -#else // LEGACY_BACKEND - assert(reg < 8); - return (reg << 3); #endif // LEGACY_BACKEND + + assert(regBits < 8); + return (regBits << 3); } /*********************************************************************************** @@ -1192,7 +1200,7 @@ inline unsigned emitter::insEncodeReg345(instruction ins, regNumber reg, emitAtt * Returns modified AVX opcode with the specified register encoded in bits 3-6 of * byte 2 of VEX prefix. */ -inline size_t emitter::insEncodeReg3456(instruction ins, regNumber reg, emitAttr size, size_t code) +inline emitter::code_t emitter::insEncodeReg3456(instruction ins, regNumber reg, emitAttr size, code_t code) { #ifdef FEATURE_AVX_SUPPORT assert(reg < REG_STK); @@ -1202,7 +1210,7 @@ inline size_t emitter::insEncodeReg3456(instruction ins, regNumber reg, emitAttr // Get 4-bit register encoding // RegEncoding() gives lower 3 bits // IsExtendedReg() gives MSB. - size_t regBits = RegEncoding(reg); + code_t regBits = RegEncoding(reg); if (IsExtendedReg(reg)) { regBits |= 0x08; @@ -1226,7 +1234,7 @@ inline size_t emitter::insEncodeReg3456(instruction ins, regNumber reg, emitAttr * Used exclusively to generate the REX.X bit and truncate the register. */ -inline unsigned emitter::insEncodeRegSIB(instruction ins, regNumber reg, size_t* code) +inline unsigned emitter::insEncodeRegSIB(instruction ins, regNumber reg, code_t* code) { assert(reg < REG_STK); @@ -1240,11 +1248,13 @@ inline unsigned emitter::insEncodeRegSIB(instruction ins, regNumber reg, size_t* { *code = AddRexXPrefix(ins, *code); // REX.X } - reg = RegEncoding(reg); -#endif + unsigned regBits = RegEncoding(reg); +#else // !_TARGET_AMD64_ + unsigned regBits = reg; +#endif // !_TARGET_AMD64_ - assert(reg < 8); - return reg; + assert(regBits < 8); + return regBits; } /***************************************************************************** @@ -1252,7 +1262,7 @@ inline unsigned emitter::insEncodeRegSIB(instruction ins, regNumber reg, size_t* * Returns the "[r/m]" opcode with the mod/RM field set to register. */ -inline size_t emitter::insEncodeMRreg(instruction ins, size_t code) +inline emitter::code_t emitter::insEncodeMRreg(instruction ins, code_t code) { // If Byte 4 (which is 0xFF00) is 0, that's where the RM encoding goes. // Otherwise, it will be placed after the 4 byte encoding. @@ -1265,24 +1275,12 @@ inline size_t emitter::insEncodeMRreg(instruction ins, size_t code) return code; } -/***************************************************************************** - * - * Returns the "[r/m], icon" opcode with the mod/RM field set to register. - */ - -inline size_t insEncodeMIreg(instruction ins, size_t code) -{ - assert((code & 0xC000) == 0); - code |= 0xC000; - return code; -} - /***************************************************************************** * * Returns the given "[r/m]" opcode with the mod/RM field set to register. */ -inline size_t insEncodeRMreg(instruction ins, size_t code) +inline emitter::code_t emitter::insEncodeRMreg(instruction ins, code_t code) { // If Byte 4 (which is 0xFF00) is 0, that's where the RM encoding goes. // Otherwise, it will be placed after the 4 byte encoding. @@ -1300,7 +1298,7 @@ inline size_t insEncodeRMreg(instruction ins, size_t code) * the given register. */ -inline size_t emitter::insEncodeMRreg(instruction ins, regNumber reg, emitAttr size, size_t code) +inline emitter::code_t emitter::insEncodeMRreg(instruction ins, regNumber reg, emitAttr size, code_t code) { assert((code & 0xC000) == 0); code |= 0xC000; @@ -1315,7 +1313,7 @@ inline size_t emitter::insEncodeMRreg(instruction ins, regNumber reg, emitAttr s * the given register. */ -inline size_t emitter::insEncodeMIreg(instruction ins, regNumber reg, emitAttr size, size_t code) +inline emitter::code_t emitter::insEncodeMIreg(instruction ins, regNumber reg, emitAttr size, code_t code) { assert((code & 0xC000) == 0); code |= 0xC000; @@ -1340,12 +1338,12 @@ inline bool insNeedsRRIb(instruction ins) * Returns the "reg,reg,imm8" opcode with both the reg's set to the * the given register. */ -inline size_t emitter::insEncodeRRIb(instruction ins, regNumber reg, emitAttr size) +inline emitter::code_t emitter::insEncodeRRIb(instruction ins, regNumber reg, emitAttr size) { assert(size == EA_4BYTE); // All we handle for now. assert(insNeedsRRIb(ins)); // If this list gets longer, use a switch, or a table lookup. - size_t code = 0x69c0; + code_t code = 0x69c0; unsigned regcode = insEncodeReg012(ins, reg, size, &code); // We use the same register as source and destination. (Could have another version that does both regs...) code |= regcode; @@ -1359,9 +1357,9 @@ inline size_t emitter::insEncodeRRIb(instruction ins, regNumber reg, emitAttr si * nibble of the opcode */ -inline size_t emitter::insEncodeOpreg(instruction ins, regNumber reg, emitAttr size) +inline emitter::code_t emitter::insEncodeOpreg(instruction ins, regNumber reg, emitAttr size) { - size_t code = insCodeRR(ins); + code_t code = insCodeRR(ins); unsigned regcode = insEncodeReg012(ins, reg, size, &code); code |= regcode; return code; @@ -1372,7 +1370,7 @@ inline size_t emitter::insEncodeOpreg(instruction ins, regNumber reg, emitAttr s * Return the 'SS' field value for the given index scale factor. */ -inline unsigned insSSval(unsigned scale) +inline unsigned emitter::insSSval(unsigned scale) { assert(scale == 1 || scale == 2 || scale == 4 || scale == 8); @@ -1477,7 +1475,7 @@ bool emitter::emitVerifyEncodable(instruction ins, emitAttr size, regNumber reg1 * Estimate the size (in bytes of generated code) of the given instruction. */ -inline UNATIVE_OFFSET emitter::emitInsSize(size_t code) +inline UNATIVE_OFFSET emitter::emitInsSize(code_t code) { UNATIVE_OFFSET size = (code & 0xFF000000) ? 4 : (code & 0x00FF0000) ? 3 : 2; #ifdef _TARGET_AMD64_ @@ -1507,7 +1505,7 @@ inline UNATIVE_OFFSET emitter::emitInsSizeRR(instruction ins, regNumber reg1, re else #endif // _TARGET_AMD64_ { - size_t code = insCodeRM(ins); + code_t code = insCodeRM(ins); sz = emitInsSize(insEncodeRMreg(ins, code)); } @@ -1532,7 +1530,7 @@ inline UNATIVE_OFFSET emitter::emitInsSizeRR(instruction ins, regNumber reg1, re /*****************************************************************************/ -inline UNATIVE_OFFSET emitter::emitInsSizeSV(size_t code, int var, int dsp) +inline UNATIVE_OFFSET emitter::emitInsSizeSV(code_t code, int var, int dsp) { UNATIVE_OFFSET size = emitInsSize(code); UNATIVE_OFFSET offs; @@ -1807,7 +1805,7 @@ static bool baseRegisterRequiresDisplacement(regNumber base) #endif } -UNATIVE_OFFSET emitter::emitInsSizeAM(instrDesc* id, size_t code) +UNATIVE_OFFSET emitter::emitInsSizeAM(instrDesc* id, code_t code) { emitAttr attrSize = id->idOpSize(); instruction ins = id->idIns(); @@ -2024,7 +2022,7 @@ UNATIVE_OFFSET emitter::emitInsSizeAM(instrDesc* id, size_t code) return size; } -inline UNATIVE_OFFSET emitter::emitInsSizeAM(instrDesc* id, size_t code, int val) +inline UNATIVE_OFFSET emitter::emitInsSizeAM(instrDesc* id, code_t code, int val) { instruction ins = id->idIns(); UNATIVE_OFFSET valSize = EA_SIZE_IN_BYTES(id->idOpSize()); @@ -2057,7 +2055,7 @@ inline UNATIVE_OFFSET emitter::emitInsSizeAM(instrDesc* id, size_t code, int val return valSize + emitInsSizeAM(id, code); } -inline UNATIVE_OFFSET emitter::emitInsSizeCV(instrDesc* id, size_t code) +inline UNATIVE_OFFSET emitter::emitInsSizeCV(instrDesc* id, code_t code) { instruction ins = id->idIns(); @@ -2077,7 +2075,7 @@ inline UNATIVE_OFFSET emitter::emitInsSizeCV(instrDesc* id, size_t code) return size + emitInsSize(code); } -inline UNATIVE_OFFSET emitter::emitInsSizeCV(instrDesc* id, size_t code, int val) +inline UNATIVE_OFFSET emitter::emitInsSizeCV(instrDesc* id, code_t code, int val) { instruction ins = id->idIns(); UNATIVE_OFFSET valSize = EA_SIZE_IN_BYTES(id->idOpSize()); @@ -2282,7 +2280,7 @@ void emitter::emitIns(instruction ins) { UNATIVE_OFFSET sz; instrDesc* id = emitNewInstr(); - size_t code = insCodeMR(ins); + code_t code = insCodeMR(ins); #ifdef DEBUG #if FEATURE_STACK_FP_X87 @@ -2358,7 +2356,7 @@ void emitter::emitIns(instruction ins, emitAttr attr) { UNATIVE_OFFSET sz; instrDesc* id = emitNewInstr(attr); - size_t code = insCodeMR(ins); + code_t code = insCodeMR(ins); assert(ins == INS_cdq); assert((code & 0xFFFFFF00) == 0); sz = 1; @@ -3997,7 +3995,7 @@ void emitter::emitIns_C_I(instruction ins, emitAttr attr, CORINFO_FIELD_HANDLE f id->idIns(ins); id->idInsFmt(fmt); - size_t code = insCodeMI(ins); + code_t code = insCodeMI(ins); UNATIVE_OFFSET sz = emitInsSizeCV(id, code, val); #ifdef _TARGET_AMD64_ @@ -7429,7 +7427,7 @@ static BYTE* emitOutputNOP(BYTE* dst, size_t nBytes) * Output an instruction involving an address mode. */ -BYTE* emitter::emitOutputAM(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc) +BYTE* emitter::emitOutputAM(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) { regNumber reg; regNumber rgx; @@ -7451,7 +7449,7 @@ BYTE* emitter::emitOutputAM(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc) // Special case: call via a register if (id->idIsCallRegPtr()) { - size_t opcode = insEncodeMRreg(INS_call, reg, EA_PTRSIZE, insCodeMR(INS_call)); + code_t opcode = insEncodeMRreg(INS_call, reg, EA_PTRSIZE, insCodeMR(INS_call)); dst += emitOutputRexOrVexPrefixIfNeeded(ins, dst, opcode); dst += emitOutputWord(dst, opcode); @@ -7467,13 +7465,15 @@ BYTE* emitter::emitOutputAM(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc) if (IsExtendedReg(reg, EA_PTRSIZE)) { insEncodeReg012(ins, reg, EA_PTRSIZE, &code); - reg = RegEncoding(reg); + // TODO-Cleanup: stop casting RegEncoding() back to a regNumber. + reg = (regNumber)RegEncoding(reg); } if (IsExtendedReg(rgx, EA_PTRSIZE)) { insEncodeRegSIB(ins, rgx, &code); - rgx = RegEncoding(rgx); + // TODO-Cleanup: stop casting RegEncoding() back to a regNumber. + rgx = (regNumber)RegEncoding(rgx); } // And emit the REX prefix @@ -7513,7 +7513,7 @@ BYTE* emitter::emitOutputAM(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc) // For this format, moves do not support a third operand, so we only need to handle the binary ops. if (IsThreeOperandBinaryAVXInstruction(ins)) { - // Encode source operand reg in 'vvvv' bits in 1's compliement form + // Encode source operand reg in 'vvvv' bits in 1's complement form // The order of operands are reversed, therefore use reg2 as the source. code = insEncodeReg3456(ins, id->idReg1(), size, code); } @@ -7527,13 +7527,15 @@ BYTE* emitter::emitOutputAM(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc) if (IsExtendedReg(reg, EA_PTRSIZE)) { insEncodeReg012(ins, reg, EA_PTRSIZE, &code); - reg = RegEncoding(reg); + // TODO-Cleanup: stop casting RegEncoding() back to a regNumber. + reg = (regNumber)RegEncoding(reg); } if (IsExtendedReg(rgx, EA_PTRSIZE)) { insEncodeRegSIB(ins, rgx, &code); - rgx = RegEncoding(rgx); + // TODO-Cleanup: stop casting RegEncoding() back to a regNumber. + rgx = (regNumber)RegEncoding(rgx); } // Is this a 'big' opcode? @@ -8093,7 +8095,7 @@ BYTE* emitter::emitOutputAM(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc) * Output an instruction involving a stack frame value. */ -BYTE* emitter::emitOutputSV(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc) +BYTE* emitter::emitOutputSV(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) { int adr; int dsp; @@ -8142,7 +8144,7 @@ BYTE* emitter::emitOutputSV(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc) // Special case emitting AVX instructions if (Is4ByteAVXInstruction(ins)) { - size_t regcode = insEncodeReg345(ins, id->idReg1(), size, &code); + unsigned regcode = insEncodeReg345(ins, id->idReg1(), size, &code); dst += emitOutputRexOrVexPrefixIfNeeded(ins, dst, code); // Emit last opcode byte @@ -8489,7 +8491,7 @@ BYTE* emitter::emitOutputSV(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc) * Output an instruction with a static data member (class variable). */ -BYTE* emitter::emitOutputCV(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc) +BYTE* emitter::emitOutputCV(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) { BYTE* addr; CORINFO_FIELD_HANDLE fldh; @@ -8554,20 +8556,18 @@ BYTE* emitter::emitOutputCV(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc) { case IF_RWR_MRD: - assert((unsigned)code == - (insCodeRM(ins) | (insEncodeReg345(ins, REG_EAX, EA_PTRSIZE, NULL) << 8) | 0x0500)); + assert(code == (insCodeRM(ins) | (insEncodeReg345(ins, REG_EAX, EA_PTRSIZE, NULL) << 8) | 0x0500)); - code &= ~((size_t)0xFFFFFFFF); + code &= ~((code_t)0xFFFFFFFF); code |= 0xA0; isMoffset = true; break; case IF_MWR_RRD: - assert((unsigned)code == - (insCodeMR(ins) | (insEncodeReg345(ins, REG_EAX, EA_PTRSIZE, NULL) << 8) | 0x0500)); + assert(code == (insCodeMR(ins) | (insEncodeReg345(ins, REG_EAX, EA_PTRSIZE, NULL) << 8) | 0x0500)); - code &= ~((size_t)0xFFFFFFFF); + code &= ~((code_t)0xFFFFFFFF); code |= 0xA2; isMoffset = true; break; @@ -8582,7 +8582,7 @@ BYTE* emitter::emitOutputCV(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc) // Special case emitting AVX instructions if (Is4ByteAVXInstruction(ins)) { - size_t regcode = insEncodeReg345(ins, id->idReg1(), size, &code); + unsigned regcode = insEncodeReg345(ins, id->idReg1(), size, &code); dst += emitOutputRexOrVexPrefixIfNeeded(ins, dst, code); // Emit last opcode byte @@ -8925,7 +8925,7 @@ BYTE* emitter::emitOutputCV(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc) BYTE* emitter::emitOutputR(BYTE* dst, instrDesc* id) { - size_t code; + code_t code; instruction ins = id->idIns(); regNumber reg = id->idReg1(); @@ -9136,7 +9136,7 @@ BYTE* emitter::emitOutputR(BYTE* dst, instrDesc* id) BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) { - size_t code; + code_t code; instruction ins = id->idIns(); regNumber reg1 = id->idReg1(); @@ -9475,7 +9475,7 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) #ifdef FEATURE_AVX_SUPPORT BYTE* emitter::emitOutputRRR(BYTE* dst, instrDesc* id) { - size_t code; + code_t code; instruction ins = id->idIns(); assert(IsAVXInstruction(ins)); @@ -9557,7 +9557,7 @@ BYTE* emitter::emitOutputRRR(BYTE* dst, instrDesc* id) BYTE* emitter::emitOutputRI(BYTE* dst, instrDesc* id) { - size_t code; + code_t code; emitAttr size = id->idOpSize(); instruction ins = id->idIns(); regNumber reg = id->idReg1(); @@ -9919,7 +9919,7 @@ BYTE* emitter::emitOutputRI(BYTE* dst, instrDesc* id) BYTE* emitter::emitOutputIV(BYTE* dst, instrDesc* id) { - size_t code; + code_t code; instruction ins = id->idIns(); emitAttr size = id->idOpSize(); ssize_t val = emitGetInsSC(id); @@ -10201,27 +10201,29 @@ BYTE* emitter::emitOutputLJ(BYTE* dst, instrDesc* i) } else { - size_t code; + code_t code; // Long jump if (jmp) { + // clang-format off assert(INS_jmp + (INS_l_jmp - INS_jmp) == INS_l_jmp); - assert(INS_jo + (INS_l_jmp - INS_jmp) == INS_l_jo); - assert(INS_jb + (INS_l_jmp - INS_jmp) == INS_l_jb); + assert(INS_jo + (INS_l_jmp - INS_jmp) == INS_l_jo); + assert(INS_jb + (INS_l_jmp - INS_jmp) == INS_l_jb); assert(INS_jae + (INS_l_jmp - INS_jmp) == INS_l_jae); - assert(INS_je + (INS_l_jmp - INS_jmp) == INS_l_je); + assert(INS_je + (INS_l_jmp - INS_jmp) == INS_l_je); assert(INS_jne + (INS_l_jmp - INS_jmp) == INS_l_jne); assert(INS_jbe + (INS_l_jmp - INS_jmp) == INS_l_jbe); - assert(INS_ja + (INS_l_jmp - INS_jmp) == INS_l_ja); - assert(INS_js + (INS_l_jmp - INS_jmp) == INS_l_js); + assert(INS_ja + (INS_l_jmp - INS_jmp) == INS_l_ja); + assert(INS_js + (INS_l_jmp - INS_jmp) == INS_l_js); assert(INS_jns + (INS_l_jmp - INS_jmp) == INS_l_jns); assert(INS_jpe + (INS_l_jmp - INS_jmp) == INS_l_jpe); assert(INS_jpo + (INS_l_jmp - INS_jmp) == INS_l_jpo); - assert(INS_jl + (INS_l_jmp - INS_jmp) == INS_l_jl); + assert(INS_jl + (INS_l_jmp - INS_jmp) == INS_l_jl); assert(INS_jge + (INS_l_jmp - INS_jmp) == INS_l_jge); assert(INS_jle + (INS_l_jmp - INS_jmp) == INS_l_jle); - assert(INS_jg + (INS_l_jmp - INS_jmp) == INS_l_jg); + assert(INS_jg + (INS_l_jmp - INS_jmp) == INS_l_jg); + // clang-format on code = insCode((instruction)(ins + (INS_l_jmp - INS_jmp))); } @@ -10367,10 +10369,10 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) // What instruction format have we got? switch (id->idInsFmt()) { - size_t code; - size_t regcode; - int args; - CnsVal cnsVal; + code_t code; + unsigned regcode; + int args; + CnsVal cnsVal; BYTE* addr; bool recCall; diff --git a/src/jit/emitxarch.h b/src/jit/emitxarch.h index 164e15c8a359..98256cdaa707 100644 --- a/src/jit/emitxarch.h +++ b/src/jit/emitxarch.h @@ -28,6 +28,15 @@ inline static bool isDoubleReg(regNumber reg) /* Routines that compute the size of / encode instructions */ /************************************************************************/ +// code_t is a type used to accumulate bits of opcode + prefixes. On amd64, it must be 64 bits +// to support the REX prefixes. On both x86 and amd64, it must be 64 bits to support AVX, with +// its 3-byte VEX prefix. For legacy backend (which doesn't support AVX), leave it as size_t. +#if defined(LEGACY_BACKEND) +typedef size_t code_t; +#else // !defined(LEGACY_BACKEND) +typedef unsigned __int64 code_t; +#endif // !defined(LEGACY_BACKEND) + struct CnsVal { ssize_t cnsVal; @@ -36,19 +45,19 @@ struct CnsVal #endif }; -UNATIVE_OFFSET emitInsSize(size_t code); +UNATIVE_OFFSET emitInsSize(code_t code); UNATIVE_OFFSET emitInsSizeRM(instruction ins); -UNATIVE_OFFSET emitInsSizeSV(size_t code, int var, int dsp); +UNATIVE_OFFSET emitInsSizeSV(code_t code, int var, int dsp); UNATIVE_OFFSET emitInsSizeSV(instrDesc* id, int var, int dsp, int val); UNATIVE_OFFSET emitInsSizeRR(instruction ins, regNumber reg1, regNumber reg2, emitAttr attr); -UNATIVE_OFFSET emitInsSizeAM(instrDesc* id, size_t code); -UNATIVE_OFFSET emitInsSizeAM(instrDesc* id, size_t code, int val); -UNATIVE_OFFSET emitInsSizeCV(instrDesc* id, size_t code); -UNATIVE_OFFSET emitInsSizeCV(instrDesc* id, size_t code, int val); +UNATIVE_OFFSET emitInsSizeAM(instrDesc* id, code_t code); +UNATIVE_OFFSET emitInsSizeAM(instrDesc* id, code_t code, int val); +UNATIVE_OFFSET emitInsSizeCV(instrDesc* id, code_t code); +UNATIVE_OFFSET emitInsSizeCV(instrDesc* id, code_t code, int val); -BYTE* emitOutputAM(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc = nullptr); -BYTE* emitOutputSV(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc = nullptr); -BYTE* emitOutputCV(BYTE* dst, instrDesc* id, size_t code, CnsVal* addc = nullptr); +BYTE* emitOutputAM(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc = nullptr); +BYTE* emitOutputSV(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc = nullptr); +BYTE* emitOutputCV(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc = nullptr); BYTE* emitOutputR(BYTE* dst, instrDesc* id); BYTE* emitOutputRI(BYTE* dst, instrDesc* id); @@ -61,30 +70,33 @@ BYTE* emitOutputRRR(BYTE* dst, instrDesc* id); BYTE* emitOutputLJ(BYTE* dst, instrDesc* id); -unsigned emitOutputRexOrVexPrefixIfNeeded(instruction ins, BYTE* dst, size_t& code); +unsigned emitOutputRexOrVexPrefixIfNeeded(instruction ins, BYTE* dst, code_t& code); unsigned emitGetRexPrefixSize(instruction ins); unsigned emitGetVexPrefixSize(instruction ins, emitAttr attr); -unsigned emitGetPrefixSize(size_t code); -unsigned emitGetVexPrefixAdjustedSize(instruction ins, emitAttr attr, size_t code); +unsigned emitGetPrefixSize(code_t code); +unsigned emitGetVexPrefixAdjustedSize(instruction ins, emitAttr attr, code_t code); + +unsigned insEncodeReg012(instruction ins, regNumber reg, emitAttr size, code_t* code); +unsigned insEncodeReg345(instruction ins, regNumber reg, emitAttr size, code_t* code); +code_t insEncodeReg3456(instruction ins, regNumber reg, emitAttr size, code_t code); +unsigned insEncodeRegSIB(instruction ins, regNumber reg, code_t* code); -unsigned insEncodeReg345(instruction ins, regNumber reg, emitAttr size, size_t* code); -unsigned insEncodeReg012(instruction ins, regNumber reg, emitAttr size, size_t* code); -size_t insEncodeReg3456(instruction ins, regNumber reg, emitAttr size, size_t code); -unsigned insEncodeRegSIB(instruction ins, regNumber reg, size_t* code); +code_t insEncodeMRreg(instruction ins, code_t code); +code_t insEncodeRMreg(instruction ins, code_t code); +code_t insEncodeMRreg(instruction ins, regNumber reg, emitAttr size, code_t code); +code_t insEncodeRRIb(instruction ins, regNumber reg, emitAttr size); +code_t insEncodeOpreg(instruction ins, regNumber reg, emitAttr size); -size_t insEncodeMRreg(instruction ins, size_t code); -size_t insEncodeMRreg(instruction ins, regNumber reg, emitAttr size, size_t code); -size_t insEncodeRRIb(instruction ins, regNumber reg, emitAttr size); -size_t insEncodeOpreg(instruction ins, regNumber reg, emitAttr size); +unsigned insSSval(unsigned scale); bool IsAVXInstruction(instruction ins); -size_t insEncodeMIreg(instruction ins, regNumber reg, emitAttr size, size_t code); +code_t insEncodeMIreg(instruction ins, regNumber reg, emitAttr size, code_t code); -size_t AddRexWPrefix(instruction ins, size_t code); -size_t AddRexRPrefix(instruction ins, size_t code); -size_t AddRexXPrefix(instruction ins, size_t code); -size_t AddRexBPrefix(instruction ins, size_t code); -size_t AddRexPrefix(instruction ins, size_t code); +code_t AddRexWPrefix(instruction ins, code_t code); +code_t AddRexRPrefix(instruction ins, code_t code); +code_t AddRexXPrefix(instruction ins, code_t code); +code_t AddRexBPrefix(instruction ins, code_t code); +code_t AddRexPrefix(instruction ins, code_t code); bool useSSE3_4Encodings; bool UseSSE3_4() @@ -98,16 +110,20 @@ void SetUseSSE3_4(bool value) bool Is4ByteSSE4Instruction(instruction ins); #ifdef FEATURE_AVX_SUPPORT + // 3-byte VEX prefix starts with byte 0xC4 -#define VEX_PREFIX_MASK_3BYTE 0xC4000000000000LL +#define VEX_PREFIX_MASK_3BYTE 0xFF000000000000ULL +#define VEX_PREFIX_CODE_3BYTE 0xC4000000000000ULL + bool TakesVexPrefix(instruction ins); + // Returns true if the instruction encoding already contains VEX prefix -bool hasVexPrefix(size_t code) +bool hasVexPrefix(code_t code) { - return (code & VEX_PREFIX_MASK_3BYTE) != 0; + return (code & VEX_PREFIX_MASK_3BYTE) == VEX_PREFIX_CODE_3BYTE; } -size_t AddVexPrefix(instruction ins, size_t code, emitAttr attr); -size_t AddVexPrefixIfNeeded(instruction ins, size_t code, emitAttr size) +code_t AddVexPrefix(instruction ins, code_t code, emitAttr attr); +code_t AddVexPrefixIfNeeded(instruction ins, code_t code, emitAttr size) { if (TakesVexPrefix(ins)) { @@ -115,7 +131,7 @@ size_t AddVexPrefixIfNeeded(instruction ins, size_t code, emitAttr size) } return code; } -size_t AddVexPrefixIfNeededAndNotPresent(instruction ins, size_t code, emitAttr size) +code_t AddVexPrefixIfNeededAndNotPresent(instruction ins, code_t code, emitAttr size) { if (TakesVexPrefix(ins) && !hasVexPrefix(code)) { @@ -142,11 +158,11 @@ bool IsThreeOperandAVXInstruction(instruction ins) } bool Is4ByteAVXInstruction(instruction ins); #else // !FEATURE_AVX_SUPPORT -bool UseAVX() +bool UseAVX() { return false; } -bool hasVexPrefix(size_t code) +bool hasVexPrefix(code_t code) { return false; } @@ -170,11 +186,11 @@ bool TakesVexPrefix(instruction ins) { return false; } -size_t AddVexPrefixIfNeeded(instruction ins, size_t code, emitAttr attr) +code_t AddVexPrefixIfNeeded(instruction ins, code_t code, emitAttr attr) { return code; } -size_t AddVexPrefixIfNeededAndNotPresent(instruction ins, size_t code, emitAttr size) +code_t AddVexPrefixIfNeededAndNotPresent(instruction ins, code_t code, emitAttr size) { return code; }