diff options
author | Kyungwoo Lee <kyulee@microsoft.com> | 2016-05-11 21:37:47 -0700 |
---|---|---|
committer | Kyungwoo Lee <kyulee@microsoft.com> | 2016-05-11 21:37:47 -0700 |
commit | 3e986665bf96e9acf7f07c3efd171cf98c9d0ca2 (patch) | |
tree | 142d56019770701e220f3e854937ef17e5768667 | |
parent | 1c2d8a6f958907e7ba9ac1198124d6611494001e (diff) | |
parent | 61fe4641665e84089dcceeabbea3e5faa0f693ce (diff) | |
download | coreclr-3e986665bf96e9acf7f07c3efd171cf98c9d0ca2.tar.gz coreclr-3e986665bf96e9acf7f07c3efd171cf98c9d0ca2.tar.bz2 coreclr-3e986665bf96e9acf7f07c3efd171cf98c9d0ca2.zip |
Merge pull request #4896 from kyulee1/longjmp
ARM64: Enable Long Address
-rw-r--r-- | src/jit/codegenarm64.cpp | 17 | ||||
-rw-r--r-- | src/jit/compiler.cpp | 6 | ||||
-rw-r--r-- | src/jit/compiler.h | 2 | ||||
-rw-r--r-- | src/jit/emit.cpp | 71 | ||||
-rw-r--r-- | src/jit/emit.h | 29 | ||||
-rw-r--r-- | src/jit/emitarm.cpp | 3 | ||||
-rw-r--r-- | src/jit/emitarm64.cpp | 605 | ||||
-rw-r--r-- | src/jit/emitarm64.h | 21 | ||||
-rw-r--r-- | src/jit/emitfmtsarm64.h | 4 | ||||
-rw-r--r-- | src/jit/flowgraph.cpp | 13 | ||||
-rw-r--r-- | src/jit/jitconfigvalues.h | 2 | ||||
-rw-r--r-- | src/jit/lowerarm64.cpp | 14 | ||||
-rw-r--r-- | src/jit/target.h | 14 | ||||
-rw-r--r-- | tests/arm64/Tests.lst | 2 |
14 files changed, 622 insertions, 181 deletions
diff --git a/src/jit/codegenarm64.cpp b/src/jit/codegenarm64.cpp index 8093d96ef3..dc25607836 100644 --- a/src/jit/codegenarm64.cpp +++ b/src/jit/codegenarm64.cpp @@ -2065,7 +2065,9 @@ void CodeGen::genCodeForBBlist() break; case BBJ_EHCATCHRET: - getEmitter()->emitIns_R_L(INS_adr, EA_4BYTE_DSP_RELOC, block->bbJumpDest, REG_INTRET); + // For long address (default): `adrp + add` will be emitted. + // For short address (proven later): `adr` will be emitted. + getEmitter()->emitIns_R_L(INS_adr, EA_PTRSIZE, block->bbJumpDest, REG_INTRET); __fallthrough; @@ -2248,10 +2250,17 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types tar } else { + // Get a temp integer register to compute long address. + regMaskTP addrRegMask = tree->gtRsvdRegs; + regNumber addrReg = genRegNumFromMask(addrRegMask); + noway_assert(addrReg != REG_NA); + // We must load the FP constant from the constant pool // Emit a data section constant for the float or double constant. CORINFO_FIELD_HANDLE hnd = emit->emitFltOrDblConst(dblConst); - emit->emitIns_R_C(INS_ldr, size, targetReg, hnd, 0); + // For long address (default): `adrp + ldr + fmov` will be emitted. + // For short address (proven later), `ldr` will be emitted. + emit->emitIns_R_C(INS_ldr, size, targetReg, addrReg, hnd, 0); } } break; @@ -3271,6 +3280,9 @@ CodeGen::genCodeForTreeNode(GenTreePtr treeNode) case GT_LABEL: genPendingCallLabel = genCreateTempLabel(); treeNode->gtLabel.gtLabBB = genPendingCallLabel; + + // For long address (default): `adrp + add` will be emitted. + // For short address (proven later): `adr` will be emitted. emit->emitIns_R_L(INS_adr, EA_PTRSIZE, genPendingCallLabel, targetReg); break; @@ -4091,6 +4103,7 @@ CodeGen::genJumpTable(GenTree* treeNode) getEmitter()->emitIns_R_C(INS_lea, emitTypeSize(TYP_I_IMPL), treeNode->gtRegNum, + REG_NA, compiler->eeFindJitDataOffs(jmpTabBase), 0); genProduceReg(treeNode); diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp index 3b2c3d6b3d..15dba0d6a0 100644 --- a/src/jit/compiler.cpp +++ b/src/jit/compiler.cpp @@ -2465,7 +2465,7 @@ void Compiler::compInitOptions(CORJIT_FLAGS* jitFlags) opts.disAsm2 = false; opts.dspUnwind = false; s_dspMemStats = false; - opts.compLargeBranches = false; + opts.compLongAddress = false; opts.compJitELTHookEnabled = false; #ifdef LATE_DISASM @@ -2534,8 +2534,8 @@ void Compiler::compInitOptions(CORJIT_FLAGS* jitFlags) if (JitConfig.DisplayMemStats() != 0) s_dspMemStats = true; - if (JitConfig.JitLargeBranches() != 0) - opts.compLargeBranches = true; + if (JitConfig.JitLongAddress() != 0) + opts.compLongAddress = true; } if (verboseDump) diff --git a/src/jit/compiler.h b/src/jit/compiler.h index 95995c3622..d757b68db5 100644 --- a/src/jit/compiler.h +++ b/src/jit/compiler.h @@ -7554,7 +7554,7 @@ public : bool dspOrder; // Display names of each of the methods that we ngen/jit bool dspUnwind; // Display the unwind info output bool dspDiffable; // Makes the Jit Dump 'diff-able' (currently uses same COMPlus_* flag as disDiffable) - bool compLargeBranches; // Force using large conditional branches + bool compLongAddress;// Force using large pseudo instructions for long address (IF_LARGEJMP/IF_LARGEADR/IF_LARGLDC) bool dspGCtbls; // Display the GC tables #endif diff --git a/src/jit/emit.cpp b/src/jit/emit.cpp index 9b8aadde60..cea1509433 100644 --- a/src/jit/emit.cpp +++ b/src/jit/emit.cpp @@ -3721,6 +3721,8 @@ AGAIN: else if (emitIsUncondJump(jmp)) { // Nothing to do; we don't shrink these. + assert(jmp->idjShort); + ssz = JMP_SIZE_SMALL; } else if (emitIsCmpJump(jmp)) { @@ -3732,6 +3734,12 @@ AGAIN: nsd = LBL_DIST_SMALL_MAX_NEG; psd = LBL_DIST_SMALL_MAX_POS; } + else if (emitIsLoadConstant(jmp)) + { + ssz = LDC_SIZE_SMALL; + nsd = LDC_DIST_SMALL_MAX_NEG; + psd = LDC_DIST_SMALL_MAX_POS; + } else { assert(!"Unknown jump instruction"); @@ -3793,6 +3801,42 @@ AGAIN: // If this is a jump via register, the instruction size does not change, so we are done. +#if defined(_TARGET_ARM64_) + // JIT code and data will be allocated together for arm64 so the relative offset to JIT data is known. + // In case such offset can be encodeable for `ldr` (+-1MB), shorten it. + if (emitIsLoadConstant(jmp)) + { + // Reference to JIT data + assert(jmp->idAddr()->iiaIsJitDataOffset()); + assert(jmp->idIsBound()); + UNATIVE_OFFSET srcOffs = jmpIG->igOffs + jmp->idjOffs; + + int doff = jmp->idAddr()->iiaGetJitDataOffset(); + assert(doff >= 0); + ssize_t imm = emitGetInsSC(jmp); + assert((imm >= 0) && (imm < 0x1000)); // 0x1000 is arbitrary, currently 'imm' is always 0 + + unsigned dataOffs = (unsigned)(doff + imm); + assert(dataOffs < emitDataSize()); + + // Conservately assume JIT data starts after the entire code size. + // TODO-ARM64: we might consider only hot code size which will be computed later in emitComputeCodeSizes(). + assert(emitTotalCodeSize > 0); + UNATIVE_OFFSET maxDstOffs = emitTotalCodeSize + dataOffs; + + // Check if the distance is within the encoding length. + UNATIVE_OFFSET jmpDist = maxDstOffs - srcOffs; + extra = jmpDist - psd; + if (extra <= 0) + { + goto SHORT_JMP; + } + + // Keep the large form. + continue; + } +#endif + /* Have we bound this jump's target already? */ if (jmp->idIsBound()) @@ -3981,10 +4025,6 @@ AGAIN: } } - // TODO-ARM64-NYI: we couldn't shorten the branch/label load into a size that fits a single b.cond or adr instruction. - // We need to implement multiple-instruction sequences to handle large ranges, e.g. b.!cond / b, and adrp/adr. - NYI_ARM64("Implement pseudo-instructions for large conditional branch and large load label address"); - /* We arrive here if the jump couldn't be made short, at least for now */ /* We had better not have eagerly marked the jump as short @@ -4109,7 +4149,9 @@ AGAIN: // insSize isz = emitInsSize(jmp->idInsFmt()); // jmp->idInsSize(isz); #elif defined(_TARGET_ARM64_) - // TODO-ARM64-NYI: Support large conditional pseudo-op branches + // The size of IF_LARGEJMP/IF_LARGEADR/IF_LARGELDC are 8 or 12. + // All other code size is 4. + assert((sizeDif == 4) || (sizeDif == 8)); #else #error Unsupported or unset target architecture #endif @@ -4505,14 +4547,21 @@ unsigned emitter::emitEndCodeGen(Compiler *comp, NYI_ARM64("Need to handle fix-up to data from cold code."); } - emitCmpHandle->allocMem(emitTotalHotCodeSize + emitConsDsc.dsdOffs, emitTotalColdCodeSize, + UNATIVE_OFFSET roDataAlignmentDelta = 0; + if (emitConsDsc.dsdOffs) + { + UNATIVE_OFFSET roDataAlignment = sizeof(void*); // 8 Byte align by default. + roDataAlignmentDelta = (UNATIVE_OFFSET)ALIGN_UP(emitTotalHotCodeSize, roDataAlignment) - emitTotalHotCodeSize; + assert((roDataAlignmentDelta == 0) || (roDataAlignmentDelta == 4)); + } + emitCmpHandle->allocMem(emitTotalHotCodeSize + roDataAlignmentDelta + emitConsDsc.dsdOffs, emitTotalColdCodeSize, 0, xcptnsCount, allocMemFlag, (void**)&codeBlock, (void**)&coldCodeBlock, (void**)&consBlock); - consBlock = codeBlock + emitTotalHotCodeSize; + consBlock = codeBlock + emitTotalHotCodeSize + roDataAlignmentDelta; #else emitCmpHandle->allocMem( emitTotalHotCodeSize, emitTotalColdCodeSize, @@ -4973,7 +5022,8 @@ unsigned emitter::emitEndCodeGen(Compiler *comp, // Presumably we could also just call "emitOutputLJ(NULL, adr, jmp)", like for long jumps? *(short int *)adr -= (short)adj; #elif defined(_TARGET_ARM64_) - NYI_ARM64("Fill this in for Arm64"); + assert(!jmp->idAddr()->iiaHasInstrCount()); + emitOutputLJ(NULL, adr, jmp); #else #error Unsupported or unset target architecture #endif @@ -4983,12 +5033,9 @@ unsigned emitter::emitEndCodeGen(Compiler *comp, // Patch Forward non-Short Jump #if defined(_TARGET_XARCH_) *(int *)adr -= adj; -#elif defined(_TARGET_ARM_) +#elif defined(_TARGET_ARMARCH_) assert(!jmp->idAddr()->iiaHasInstrCount()); - // This handles both medium and long jumps emitOutputLJ(NULL, adr, jmp); -#elif defined(_TARGET_ARM64_) - NYI_ARM64("Fill this in for Arm64"); #else #error Unsupported or unset target architecture #endif diff --git a/src/jit/emit.h b/src/jit/emit.h index 67adcdf731..41fc46ca70 100644 --- a/src/jit/emit.h +++ b/src/jit/emit.h @@ -983,8 +983,32 @@ protected: void idCodeSize(unsigned sz) { _idCodeSize = sz; assert(sz == _idCodeSize); } #elif defined(_TARGET_ARM64_) - - unsigned idCodeSize() const { return 4; } + unsigned idCodeSize() const { + int size = 4; + switch (idInsFmt()) + { + case IF_LARGEADR: + // adrp + add + case IF_LARGEJMP: + // b<cond> + b<uncond> + size = 8; + break; + case IF_LARGELDC: + if (isVectorRegister(idReg1())) + { + // adrp + ldr + fmov + size = 12; + } + else + { + // adrp + ldr + size = 8; + } + break; + } + + return size; + } #elif defined(_TARGET_ARM_) @@ -1797,6 +1821,7 @@ public: void emitSetFrameRangeArgs(int offsLo, int offsHi); static instruction emitJumpKindToIns(emitJumpKind jumpKind); + static emitJumpKind emitInsToJumpKind(instruction ins); static emitJumpKind emitReverseJumpKind(emitJumpKind jumpKind); #ifdef _TARGET_ARM_ diff --git a/src/jit/emitarm.cpp b/src/jit/emitarm.cpp index 99049e4b0e..e2daab4a34 100644 --- a/src/jit/emitarm.cpp +++ b/src/jit/emitarm.cpp @@ -10,7 +10,6 @@ XX XX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ - #include "jitpch.h" #ifdef _MSC_VER #pragma hdrstop @@ -4280,7 +4279,7 @@ void emitter::emitIns_J(instruction ins, id->idjKeepLong = emitComp->fgInDifferentRegions(emitComp->compCurBB, dst); #ifdef DEBUG - if (emitComp->opts.compLargeBranches) // Force long branches + if (emitComp->opts.compLongAddress) // Force long branches id->idjKeepLong = 1; #endif // DEBUG } diff --git a/src/jit/emitarm64.cpp b/src/jit/emitarm64.cpp index 33843d37d6..3eec0eaade 100644 --- a/src/jit/emitarm64.cpp +++ b/src/jit/emitarm64.cpp @@ -56,6 +56,25 @@ const emitJumpKind emitReverseJumpKinds[] = } /***************************************************************************** +* Look up the jump kind for an instruction. It better be a conditional +* branch instruction with a jump kind! +*/ + +/*static*/ emitJumpKind emitter::emitInsToJumpKind(instruction ins) +{ + for (unsigned i = 0; i < ArrLen(emitJumpKindInstructions); i++) + { + if (ins == emitJumpKindInstructions[i]) + { + emitJumpKind ret = (emitJumpKind)i; + assert(EJ_NONE < ret && ret < EJ_COUNT); + return ret; + } + } + unreached(); +} + +/***************************************************************************** * Reverse the conditional jump */ @@ -157,6 +176,11 @@ void emitter::emitInsSanityCheck(instrDesc *id) case IF_BI_0B: // BI_0B ......iiiiiiiiii iiiiiiiiiiii.... simm19:00 break; + case IF_LARGEJMP: + case IF_LARGEADR: + case IF_LARGELDC: + break; + case IF_BI_0C: // BI_0C ......iiiiiiiiii iiiiiiiiiiiiiiii simm26:00 break; @@ -6321,6 +6345,7 @@ void emitter::emitIns_S_I (instruction ins, void emitter::emitIns_R_C (instruction ins, emitAttr attr, regNumber reg, + regNumber addrReg, CORINFO_FIELD_HANDLE fldHnd, int offs) { @@ -6332,24 +6357,27 @@ void emitter::emitIns_R_C (instruction ins, int disp = 0; instrDescJmp* id = emitNewInstrJmp(); - // TODO-ARM64-CQ: use unscaled loads? - /* Figure out the encoding format of the instruction */ switch (ins) { case INS_ldr: + fmt = IF_LARGELDC; if (isVectorRegister(reg)) { assert(isValidScalarDatasize(size)); + // For vector (float/double) register, we should have an integer address reg to + // compute long address which consists of page address and page offset. + // For integer constant, this is not needed since the dest reg can be used to + // compute address as well as contain the final contents. + assert(isGeneralRegister(reg) || (addrReg != REG_NA)); } else { assert(isGeneralRegister(reg)); assert(isValidGeneralDatasize(size)); } - fmt = IF_LS_1A; break; default: - break; + unreached(); } assert(fmt != IF_NONE); @@ -6360,9 +6388,36 @@ void emitter::emitIns_R_C (instruction ins, id->idSmallCns(offs); id->idOpSize(size); id->idAddr()->iiaFieldHnd = fldHnd; - id->idSetIsBound(); + id->idSetIsBound(); // We won't patch address since we will know the exact distance once JIT code and data are allocated together. - id->idReg1(reg); + id->idReg1(reg); // destination register that will get the constant value. + id->idReg2(addrReg); // integer register to compute long address (used for vector dest when we end up with long address) + id->idjShort = false; // Assume loading constant from long address + + // Keep it long if it's in cold code. + id->idjKeepLong = emitComp->fgIsBlockCold(emitComp->compCurBB); + +#ifdef DEBUG + if (emitComp->opts.compLongAddress) + id->idjKeepLong = 1; +#endif // DEBUG + + // If it's possible to be shortened, then put it in jump list + // to be revisited by emitJumpDistBind. + if (!id->idjKeepLong) + { + /* Record the jump's IG and offset within it */ + id->idjIG = emitCurIG; + id->idjOffs = emitCurIGsize; + + /* Append this jump to this IG's jump list */ + id->idjNext = emitCurIGjmpList; + emitCurIGjmpList = id; + +#if EMITTER_STATS + emitTotalIGjmps++; +#endif + } dispIns(id); appendToCurIG(id); @@ -6515,8 +6570,27 @@ void emitter::emitIns_R_ARX (instruction ins, */ void emitter::emitSetShortJump(instrDescJmp * id) { - // All jumps are the same size. - // NYI: support large conditional branches via pseudo-op. + if (id->idjKeepLong) + return; + + insFormat fmt = IF_NONE; + if (emitIsCondJump(id)) + { + fmt = IF_BI_0B; + } + else if (emitIsLoadLabel(id)) + { + fmt = IF_DI_1E; + } + else if (emitIsLoadConstant(id)) + { + fmt = IF_LS_1A; + } + else { + unreached(); + } + + id->idInsFmt(fmt); id->idjShort = true; } @@ -6534,23 +6608,20 @@ void emitter::emitIns_R_L (instruction ins, insFormat fmt = IF_NONE; - instrDescJmp *id; switch (ins) { case INS_adr: - case INS_adrp: - fmt = IF_DI_1E; + fmt = IF_LARGEADR; break; default: - // TODO-Cleanup: add unreached() here - break; + unreached(); } - assert(fmt == IF_DI_1E); - id = emitNewInstrJmp(); + instrDescJmp* id = emitNewInstrJmp(); id->idIns(ins); id->idInsFmt(fmt); + id->idjShort = false; id->idAddr()->iiaBBlabel = dst; id->idReg1(reg); id->idOpSize(EA_PTRSIZE); @@ -6563,11 +6634,13 @@ void emitter::emitIns_R_L (instruction ins, } #endif // DEBUG - /* Assume the label reference will be long */ - - id->idjShort = 0; id->idjKeepLong = emitComp->fgInDifferentRegions(emitComp->compCurBB, dst); - + +#ifdef DEBUG + if (emitComp->opts.compLongAddress) + id->idjKeepLong = 1; +#endif // DEBUG + /* Record the jump's IG and offset within it */ id->idjIG = emitCurIG; @@ -6582,13 +6655,6 @@ void emitter::emitIns_R_L (instruction ins, emitTotalIGjmps++; #endif - /* Figure out the max. size of the instruction */ - - if (!id->idjKeepLong) - { - // TODO-ARM64-NYI: handle large labels: larger than adr can do (+/-1MB) - } - dispIns(id); appendToCurIG(id); } @@ -6631,10 +6697,14 @@ void emitter::emitIns_J(instruction ins, } /* Figure out the encoding format of the instruction */ + + bool idjShort = false; switch (ins) { case INS_bl_local: case INS_b: + // Unconditional jump is a single form. + idjShort = true; fmt = IF_BI_0A; break; @@ -6652,20 +6722,20 @@ void emitter::emitIns_J(instruction ins, case INS_blt: case INS_bgt: case INS_ble: - // TODO-ARM64-CQ: fmt = IF_LARGEJMP; /* Assume the jump will be long */ - fmt = IF_BI_0B; + // Assume conditional jump is long. + fmt = IF_LARGEJMP; break; + default: - // TODO-Cleanup: add unreached() here + unreached(); break; } - assert((fmt == IF_BI_0A) || - (fmt == IF_BI_0B)); instrDescJmp* id = emitNewInstrJmp(); id->idIns(ins); id->idInsFmt(fmt); + id->idjShort = idjShort; #ifdef DEBUG // Mark the finally call @@ -6675,19 +6745,22 @@ void emitter::emitIns_J(instruction ins, } #endif // DEBUG - /* Assume the jump will be long */ - - // TODO-ARM64-Cleanup: there is only one size jump on ARM64. Clean this up. - id->idjShort = 1; if (dst != nullptr) { id->idAddr()->iiaBBlabel = dst; - id->idjKeepLong = emitComp->fgInDifferentRegions(emitComp->compCurBB, dst); + + // Skip unconditional jump that has a single form. + // TODO-ARM64-NYI: enable hot/cold splittingNYI. + // The target needs to be relocated. + if (!idjShort) + { + id->idjKeepLong = emitComp->fgInDifferentRegions(emitComp->compCurBB, dst); #ifdef DEBUG - if (emitComp->opts.compLargeBranches) // Force long branches - id->idjKeepLong = 1; + if (emitComp->opts.compLongAddress) // Force long branches + id->idjKeepLong = 1; #endif // DEBUG + } } else { @@ -6712,13 +6785,6 @@ void emitter::emitIns_J(instruction ins, emitTotalIGjmps++; #endif - /* Figure out the max. size of the jump/call instruction */ - - if (!id->idjKeepLong) - { - // TODO-ARM64-NYI: handle large conditional branches: larger than b.cond can do (+/-1MB) - } - dispIns(id); appendToCurIG(id); } @@ -7832,12 +7898,13 @@ BYTE* emitter::emitOutputLJ(insGroup *ig, BYTE *dst, instrDesc *i ssize_t distVal; ssize_t loBits; + // Set default ins/fmt from id. instruction ins = id->idIns(); insFormat fmt = id->idInsFmt(); - code_t code = emitInsCode(ins, fmt); // Basic instruction encoding bool loadLabel = false; bool isJump = false; + bool loadConstant = false; switch (ins) { @@ -7855,7 +7922,16 @@ BYTE* emitter::emitOutputLJ(insGroup *ig, BYTE *dst, instrDesc *i case INS_ldr: case INS_ldrsw: case INS_adr: - loadLabel = true; + case INS_adrp: + // Any reference to JIT data is assumed to load constant. + if (id->idAddr()->iiaIsJitDataOffset()) + { + loadConstant = true; + } + else + { + loadLabel = true; + } break; } @@ -7864,6 +7940,78 @@ BYTE* emitter::emitOutputLJ(insGroup *ig, BYTE *dst, instrDesc *i srcOffs = emitCurCodeOffs(dst); srcAddr = emitOffsetToPtr(srcOffs); + if (loadConstant) + { + /* This is actually a reference to the JIT data section */ + int doff = id->idAddr()->iiaGetJitDataOffset(); + assert(doff >= 0); + ssize_t imm = emitGetInsSC(id); + assert((imm >= 0) && (imm < 0x1000)); // 0x1000 is arbitrary, currently 'imm' is always 0 + + unsigned dataOffs = (unsigned)(doff + imm); + assert(dataOffs < emitDataSize()); + dstAddr = emitDataOffsetToPtr(dataOffs); + + regNumber dstReg = id->idReg1(); + regNumber addrReg = dstReg; // an integer register to compute long address. + emitAttr opSize = id->idOpSize(); + + if (id->idjShort) + { + // ldr x/v, [rel addr] -- load constant from current addr(ip) + rel addr. + assert(ins == INS_ldr); + assert(fmt == IF_LS_1A); + distVal = (ssize_t)(dstAddr - srcAddr); + dst = emitOutputShortConstant(dst, ins, fmt, distVal, dstReg, opSize); + } + else + { + // adrp x, [rel page addr] -- compute page address: current page addr + rel page addr + assert(fmt == IF_LARGELDC); + ssize_t relPageAddr = (((ssize_t)dstAddr & 0xFFFFFFFFFFFFF000LL) - ((ssize_t)srcAddr & 0xFFFFFFFFFFFFF000LL)) >> 12; + if (isVectorRegister(dstReg)) + { + // Update addrReg with the reserved integer register + // since we cannot use dstReg (vector) to load constant directly from memory. + addrReg = id->idReg2(); + assert(isGeneralRegister(addrReg)); + } + ins = INS_adrp; + fmt = IF_DI_1E; + dst = emitOutputShortAddress(dst, ins, fmt, relPageAddr, addrReg); + + // ldr x, [x, page offs] -- load constant from page address + page offset into integer register. + ssize_t imm12 = (ssize_t)dstAddr & 0xFFF; // 12 bits + assert(isValidUimm12(imm12)); + ins = INS_ldr; + fmt = IF_LS_2B; + dst = emitOutputShortConstant(dst, ins, fmt, imm12, addrReg, opSize); + + // fmov v, d -- copy constant in integer register to vector register. + // This is needed only for vector constant. + if (addrReg != dstReg) + { + // fmov Vd,Rn DV_2I X00111100X100111 000000nnnnnddddd 1E27 0000 Vd,Rn (scalar, from general) + assert(isVectorRegister(dstReg) && isGeneralRegister(addrReg)); + ins = INS_fmov; + fmt = IF_DV_2I; + code_t code = emitInsCode(ins, fmt); + + code |= insEncodeReg_Vd(dstReg); // ddddd + code |= insEncodeReg_Rn(addrReg); // nnnnn + if (id->idOpSize() == EA_8BYTE) + { + code |= 0x80400000; // X ... X + } + dst += emitOutput_Instr(dst, code); + } + } + + return dst; + } + + assert(loadLabel || isJump); + if (id->idAddr()->iiaHasInstrCount()) { assert(ig != NULL); @@ -7877,30 +8025,6 @@ BYTE* emitter::emitOutputLJ(insGroup *ig, BYTE *dst, instrDesc *i dstOffs = ig->igOffs + emitFindOffset(ig, (insNum + 1 + instrCount)); dstAddr = emitOffsetToPtr(dstOffs); } - else if (id->idAddr()->iiaIsJitDataOffset()) - { - assert(loadLabel); - - /* This is actually a reference to the JIT data section */ - - int doff = id->idAddr()->iiaGetJitDataOffset(); - assert(doff >= 0); - ssize_t imm = emitGetInsSC(id); - assert((imm >= 0) && (imm < 0x1000)); // 0x1000 is arbitrary, currently 'imm' is always 0 - - unsigned dataOffs = (unsigned) (doff + imm); - assert(dataOffs < emitDataSize()); - dstAddr = emitDataOffsetToPtr(dataOffs); - dstOffs = (unsigned) ((ssize_t) (dstAddr - srcAddr) + srcOffs); - assert((dstOffs & 3) == 0); - - // Failing the following assertion means the corresponding JIT data is not within +/-1MB range - // from the current code reference. This could happen for a large method or extremely large - // amount of JIT data for the method, or access it from cold method. - // Ideally, we should detect such case earlier to expand the code sequence using a fix-up - // similar to emitIns_R_AI. - assert(isValidSimm19(dstOffs)); - } else { dstOffs = id->idAddr()->iiaIGlabel->igOffs; @@ -7977,120 +8101,277 @@ BYTE* emitter::emitOutputLJ(insGroup *ig, BYTE *dst, instrDesc *i } #endif - loBits = (distVal & 3); - distVal >>= 2; // branch offset encodings are scaled by 4. - /* For forward jumps, record the address of the distance value */ id->idjTemp.idjAddr = (distVal > 0) ? dst : NULL; - assert(emitJumpCrossHotColdBoundary(srcOffs, dstOffs) == false); + if (emitJumpCrossHotColdBoundary(srcOffs, dstOffs)) + { + assert(!id->idjShort); + NYI_ARM64("Relocation Support for long address"); + } + assert(insOptsNone(id->idInsOpt())); if (isJump) { - assert(!id->idjKeepLong); - - // branch offsets must be a multiple of 4 - noway_assert(loBits == 0); - - if (fmt == IF_BI_0A) - { - // INS_b or INS_bl_local - noway_assert(isValidSimm26(distVal)); - distVal &= 0x3FFFFFFLL; - code |= distVal; - } - else if (fmt == IF_BI_0B) + if (id->idjShort) { - // INS_beq, INS_bne, etc... - noway_assert(isValidSimm19(distVal)); - distVal &= 0x7FFFFLL; - code |= distVal << 5; + // Short conditional/unconditional jump + assert(!id->idjKeepLong); + assert(emitJumpCrossHotColdBoundary(srcOffs, dstOffs) == false); + assert((fmt == IF_BI_0A) || (fmt == IF_BI_0B)); } - else if (fmt == IF_BI_1A) // BI_1A X.......iiiiiiii iiiiiiiiiiittttt Rt simm19:00 + else { - // INS_cbz or INS_cbnz - code |= insEncodeDatasize(id->idOpSize()); // X - code |= insEncodeReg_Rt(id->idReg1()); // ttttt + // Long conditional jump + assert(fmt == IF_LARGEJMP); + // This is a pseudo-instruction format representing a large conditional branch, to allow + // us to get a greater branch target range than we can get by using a straightforward conditional + // branch. It is encoded as a short conditional branch that branches around a long unconditional + // branch. + // + // Conceptually, we have: + // + // b<cond> L_target + // + // The code we emit is: + // + // b<!cond> L_not // 4 bytes. Note that we reverse the condition. + // b L_target // 4 bytes + // L_not: + // + // Note that we don't actually insert any blocks: we simply encode "b <!cond> L_not" as a branch with + // the correct offset. Note also that this works for both integer and floating-point conditions, because + // the condition inversion takes ordered/unordered into account, preserving NaN behavior. For example, + // "GT" (greater than) is inverted to "LE" (less than, equal, or unordered). + dst = emitOutputShortBranch(dst, + emitJumpKindToIns(emitReverseJumpKind(emitInsToJumpKind(ins))), // reverse the conditional instruction + IF_BI_0B, + 8, /* 8 bytes from start of this large conditional pseudo-instruction to L_not. */ + nullptr /* only used for tbz/tbnzcbz/cbnz */); - noway_assert(isValidSimm19(distVal)); - distVal &= 0x7FFFFLL; // 19 bits - code |= distVal << 5; - } - else if (fmt == IF_BI_1B) // BI_1B B.......bbbbbiii iiiiiiiiiiittttt Rt imm6, simm14:00 - { - // INS_tbz or INS_tbnz - ssize_t imm = emitGetInsSC(id); - assert(isValidImmShift(imm, id->idOpSize())); + // Now, pretend we've got a normal unconditional branch, and fall through to the code to emit that. + ins = INS_b; + fmt = IF_BI_0A; - if (imm & 0x20) // test bit 32-63 ? - { - code |= 0x80000000; // B - } - code |= ((imm & 0x1F) << 19); // bbbbb - code |= insEncodeReg_Rt(id->idReg1()); // ttttt + // The distVal was computed based on the beginning of the pseudo-instruction. + // So subtract the size of the conditional branch so that it is relative to the + // unconditional branch. + distVal -= 4; + } - noway_assert(isValidSimm14(distVal)); - distVal &= 0x3FFFLL; // 14 bits - code |= distVal << 5; + dst = emitOutputShortBranch(dst, ins, fmt, distVal, id); + } + else if (loadLabel) + { + regNumber dstReg = id->idReg1(); + if (id->idjShort) + { + // adr x, [rel addr] -- compute address: current addr(ip) + rel addr. + assert(ins == INS_adr); + assert(fmt == IF_DI_1E); + dst = emitOutputShortAddress(dst, ins, fmt, distVal, dstReg); } else { - assert(!"Unknown fmt"); + // adrp x, [rel page addr] -- compute page address: current page addr + rel page addr + assert(fmt == IF_LARGEADR); + ssize_t relPageAddr = (((ssize_t)dstAddr & 0xFFFFFFFFFFFFF000LL) - ((ssize_t)srcAddr & 0xFFFFFFFFFFFFF000LL)) >> 12; + dst = emitOutputShortAddress(dst, INS_adrp, IF_DI_1E, relPageAddr, dstReg); + + // add x, x, page offs -- compute address = page addr + page offs + ssize_t imm12 = (ssize_t)dstAddr & 0xFFF; // 12 bits + assert(isValidUimm12(imm12)); + code_t code = emitInsCode(INS_add, IF_DI_2A); // DI_2A X0010001shiiiiii iiiiiinnnnnddddd 1100 0000 imm(i12, sh) + code |= insEncodeDatasize(EA_8BYTE); // X + code |= ((code_t)imm12 << 10); // iiiiiiiiiiii + code |= insEncodeReg_Rd(dstReg); // ddddd + code |= insEncodeReg_Rn(dstReg); // nnnnn + dst += emitOutput_Instr(dst, code); } } - else if (loadLabel) + + return dst; +} + +/***************************************************************************** +* +* Output a short branch instruction. +*/ +BYTE* emitter::emitOutputShortBranch(BYTE *dst, instruction ins, insFormat fmt, ssize_t distVal, instrDescJmp* id) +{ + code_t code = emitInsCode(ins, fmt); + + ssize_t loBits = (distVal & 3); + noway_assert(loBits == 0); + distVal >>= 2; // branch offset encodings are scaled by 4. + + if (fmt == IF_BI_0A) + { + // INS_b or INS_bl_local + noway_assert(isValidSimm26(distVal)); + distVal &= 0x3FFFFFFLL; + code |= distVal; + } + else if (fmt == IF_BI_0B) // BI_0B 01010100iiiiiiii iiiiiiiiiiiXXXXX simm19:00 + { + // INS_beq, INS_bne, etc... + noway_assert(isValidSimm19(distVal)); + distVal &= 0x7FFFFLL; + code |= distVal << 5; + } + else if (fmt == IF_BI_1A) // BI_1A X.......iiiiiiii iiiiiiiiiiittttt Rt simm19:00 + { + // INS_cbz or INS_cbnz + assert(id != nullptr); + code |= insEncodeDatasize(id->idOpSize()); // X + code |= insEncodeReg_Rt(id->idReg1()); // ttttt + + noway_assert(isValidSimm19(distVal)); + distVal &= 0x7FFFFLL; // 19 bits + code |= distVal << 5; + } + else if (fmt == IF_BI_1B) // BI_1B B.......bbbbbiii iiiiiiiiiiittttt Rt imm6, simm14:00 { - if (fmt == IF_LS_1A) // LS_1A XX...V..iiiiiiii iiiiiiiiiiittttt Rt simm21 + // INS_tbz or INS_tbnz + assert(id != nullptr); + ssize_t imm = emitGetInsSC(id); + assert(isValidImmShift(imm, id->idOpSize())); + + if (imm & 0x20) // test bit 32-63 ? { - // INS_ldr or INS_ldrsw (PC-Relative) + code |= 0x80000000; // B + } + code |= ((imm & 0x1F) << 19); // bbbbb + code |= insEncodeReg_Rt(id->idReg1()); // ttttt - // Is the target a vector register? - if (isVectorRegister(id->idReg1())) - { - code |= insEncodeDatasizeVLS(code, id->idOpSize()); // XX V - code |= insEncodeReg_Vt(id->idReg1()); // ttttt - } - else - { - assert(isGeneralRegister(id->idReg1())); - // insEncodeDatasizeLS is not quite right for this case. - // So just specialize it. - if ((ins == INS_ldr) && (id->idOpSize() == EA_8BYTE)) - { - // set the operation size in bit 30 - code |= 0x40000000; - } + noway_assert(isValidSimm14(distVal)); + distVal &= 0x3FFFLL; // 14 bits + code |= distVal << 5; + } + else + { + assert(!"Unknown fmt for emitOutputShortBranch"); + } - code |= insEncodeReg_Rt(id->idReg1()); // ttttt - } + dst += emitOutput_Instr(dst, code); + + return dst; +} + +/***************************************************************************** +* +* Output a short address instruction. +*/ +BYTE* emitter::emitOutputShortAddress(BYTE *dst, instruction ins, insFormat fmt, ssize_t distVal, regNumber reg) +{ + ssize_t loBits = (distVal & 3); + distVal >>= 2; + + code_t code = emitInsCode(ins, fmt); + if (fmt == IF_DI_1E) // DI_1E .ii.....iiiiiiii iiiiiiiiiiiddddd Rd simm21 + { + // INS_adr or INS_adrp + code |= insEncodeReg_Rd(reg); // ddddd + + noway_assert(isValidSimm19(distVal)); + distVal &= 0x7FFFFLL; // 19 bits + code |= distVal << 5; + code |= loBits << 29; // 2 bits + } + else + { + assert(!"Unknown fmt for emitOutputShortAddress"); + } - noway_assert(loBits == 0); - noway_assert(isValidSimm19(distVal)); - distVal &= 0x7FFFFLL; // 19 bits - code |= distVal << 5; + dst += emitOutput_Instr(dst, code); + + return dst; +} + +/***************************************************************************** +* +* Output a short constant instruction. +*/ +BYTE* emitter::emitOutputShortConstant(BYTE *dst, instruction ins, insFormat fmt, ssize_t imm, regNumber reg, emitAttr opSize) +{ + code_t code = emitInsCode(ins, fmt); + + if (fmt == IF_LS_1A) + { + // LS_1A XX...V..iiiiiiii iiiiiiiiiiittttt Rt simm21 + // INS_ldr or INS_ldrsw (PC-Relative) + + ssize_t loBits = (imm & 3); + noway_assert(loBits == 0); + ssize_t distVal = imm >>= 2; // load offset encodings are scaled by 4. + + noway_assert(isValidSimm19(distVal)); + + // Is the target a vector register? + if (isVectorRegister(reg)) + { + code |= insEncodeDatasizeVLS(code, opSize); // XX V + code |= insEncodeReg_Vt(reg); // ttttt } - else if (fmt == IF_DI_1E) // DI_1E .ii.....iiiiiiii iiiiiiiiiiiddddd Rd simm21 + else { - // INS_adr or INS_adrp - code |= insEncodeReg_Rd(id->idReg1()); // ddddd + assert(isGeneralRegister(reg)); + // insEncodeDatasizeLS is not quite right for this case. + // So just specialize it. + if ((ins == INS_ldr) && (opSize == EA_8BYTE)) + { + // set the operation size in bit 30 + code |= 0x40000000; + } + + code |= insEncodeReg_Rt(reg); // ttttt + } - noway_assert(isValidSimm19(distVal)); - distVal &= 0x7FFFFLL; // 19 bits - code |= distVal << 5; - code |= loBits << 29; // 2 bits + distVal &= 0x7FFFFLL; // 19 bits + code |= distVal << 5; + } + else if (fmt == IF_LS_2B) + { + // ldr Rt,[Xn+pimm12] LS_2B 1X11100101iiiiii iiiiiinnnnnttttt B940 0000 imm(0-4095<<{2,3}) + // INS_ldr or INS_ldrsw (PC-Relative) + noway_assert(isValidUimm12(imm)); + assert(isGeneralRegister(reg)); + + if (opSize == EA_8BYTE) + { + // insEncodeDatasizeLS is not quite right for this case. + // So just specialize it. + if (ins == INS_ldr) + { + // set the operation size in bit 30 + code |= 0x40000000; + } + // Low 3 bits should be 0 -- 8 byte JIT data should be aligned on 8 byte. + assert((imm & 7) == 0); + imm >>= 3; } else { - assert(!"Unknown fmt"); + assert(opSize == EA_4BYTE); + // Low 2 bits should be 0 -- 4 byte aligned data. + assert((imm & 3) == 0); + imm >>= 2; } + + code |= insEncodeReg_Rt(reg); // ttttt + code |= insEncodeReg_Rn(reg); // nnnnn + code |= imm << 10; + } + else + { + assert(!"Unknown fmt for emitOutputShortConstant"); } - dst += emitOutput_Instr(dst, code); + dst += emitOutput_Instr(dst, code); - return dst; + return dst; } - /***************************************************************************** * * Output a call instruction. @@ -8243,6 +8524,7 @@ size_t emitter::emitOutputInstr(insGroup *ig, case IF_BI_0A: // BI_0A ......iiiiiiiiii iiiiiiiiiiiiiiii simm26:00 case IF_BI_0B: // BI_0B ......iiiiiiiiii iiiiiiiiiii..... simm19:00 + case IF_LARGEJMP: assert(id->idGCref() == GCT_NONE); assert(id->idIsBound()); dst = emitOutputLJ(ig, dst, id); @@ -8293,6 +8575,7 @@ size_t emitter::emitOutputInstr(insGroup *ig, break; case IF_LS_1A: // LS_1A XX...V..iiiiiiii iiiiiiiiiiittttt Rt PC imm(1MB) + case IF_LARGELDC: assert(insOptsNone(id->idInsOpt())); assert(id->idIsBound()); @@ -8484,6 +8767,7 @@ size_t emitter::emitOutputInstr(insGroup *ig, break; case IF_DI_1E: // DI_1E .ii.....iiiiiiii iiiiiiiiiiiddddd Rd simm21 + case IF_LARGEADR: assert(insOptsNone(id->idInsOpt())); if (id->idIsReloc()) { @@ -9821,6 +10105,10 @@ void emitter::emitDispInsHex(BYTE * code, size_t sz) { printf(" %08X ", (*((code_t *) code))); } + else + { + printf(" "); + } } } @@ -9904,7 +10192,12 @@ void emitter::emitDispIns(instrDesc * id, case IF_BI_0A: // BI_0A ......iiiiiiiiii iiiiiiiiiiiiiiii simm26:00 case IF_BI_0B: // BI_0B ......iiiiiiiiii iiiiiiiiiii..... simm19:00 + case IF_LARGEJMP: { + if (fmt == IF_LARGEJMP) + { + printf("(LARGEJMP)"); + } if (id->idAddr()->iiaHasInstrCount()) { int instrCount = id->idAddr()->iiaGetInstrCount(); @@ -9995,6 +10288,7 @@ void emitter::emitDispIns(instrDesc * id, break; case IF_LS_1A: // LS_1A XX...V..iiiiiiii iiiiiiiiiiittttt Rt PC imm(1MB) + case IF_LARGELDC: assert(insOptsNone(id->idInsOpt())); emitDispReg(id->idReg1(), size, true); imm = emitGetInsSC(id); @@ -10002,6 +10296,10 @@ void emitter::emitDispIns(instrDesc * id, /* Is this actually a reference to a data section? */ doffs = Compiler::eeGetJitDataOffs(id->idAddr()->iiaFieldHnd); + if (fmt == IF_LARGELDC) + { + printf("(LARGELDC)"); + } printf("["); if (doffs >= 0) { @@ -10120,8 +10418,13 @@ void emitter::emitDispIns(instrDesc * id, break; case IF_DI_1E: // DI_1E .ii.....iiiiiiii iiiiiiiiiiiddddd Rd simm21 + case IF_LARGEADR: assert(insOptsNone(id->idInsOpt())); emitDispReg(id->idReg1(), size, true); + if (fmt == IF_LARGEADR) + { + printf("(LARGEADR)"); + } if (id->idIsBound()) { printf("G_M%03u_IG%02u", Compiler::s_compMethodsCount, id->idAddr()->iiaIGlabel->igNum); diff --git a/src/jit/emitarm64.h b/src/jit/emitarm64.h index f7549faf34..a3f1845de1 100644 --- a/src/jit/emitarm64.h +++ b/src/jit/emitarm64.h @@ -775,6 +775,7 @@ public: void emitIns_R_C (instruction ins, emitAttr attr, regNumber reg, + regNumber tmpReg, CORINFO_FIELD_HANDLE fldHnd, int offs); @@ -899,6 +900,9 @@ public: BYTE* emitOutputLJ (insGroup *ig, BYTE *dst, instrDesc *i); unsigned emitOutputCall(insGroup *ig, BYTE *dst, instrDesc *i, code_t code); + BYTE* emitOutputShortBranch(BYTE *dst, instruction ins, insFormat fmt, ssize_t distVal, instrDescJmp* id); + BYTE* emitOutputShortAddress(BYTE *dst, instruction ins, insFormat fmt, ssize_t distVal, regNumber reg); + BYTE* emitOutputShortConstant(BYTE *dst, instruction ins, insFormat fmt, ssize_t distVal, regNumber reg, emitAttr opSize); /***************************************************************************** * @@ -907,7 +911,8 @@ public: inline bool emitIsCondJump(instrDesc *jmp) { - return (jmp->idInsFmt() == IF_BI_0B); + return ((jmp->idInsFmt() == IF_BI_0B) || + (jmp->idInsFmt() == IF_LARGEJMP)); } @@ -949,7 +954,19 @@ inline bool emitIsDirectCall(instrDesc *call) inline bool emitIsLoadLabel(instrDesc *jmp) { - return (jmp->idInsFmt() == IF_DI_1E); // adr or adrp + return ((jmp->idInsFmt() == IF_DI_1E) || // adr or arp + (jmp->idInsFmt() == IF_LARGEADR)); +} + +/***************************************************************************** +* +* Given a instrDesc, return true if it's a load constant instruction. +*/ + +inline bool emitIsLoadConstant(instrDesc *jmp) +{ + return ((jmp->idInsFmt() == IF_LS_1A) || // ldr + (jmp->idInsFmt() == IF_LARGELDC)); } #endif // _TARGET_ARM64_ diff --git a/src/jit/emitfmtsarm64.h b/src/jit/emitfmtsarm64.h index 06cde03f8c..294bb38701 100644 --- a/src/jit/emitfmtsarm64.h +++ b/src/jit/emitfmtsarm64.h @@ -40,7 +40,9 @@ enum ID_OPS IF_DEF(NONE, IS_NONE, NONE) // IF_DEF(LABEL, IS_NONE, JMP ) // label -IF_DEF(LARGEJMP, IS_NONE, JMP) // large conditional branch pseudo-op +IF_DEF(LARGEJMP, IS_NONE, JMP) // large conditional branch pseudo-op (cond branch + uncond branch) +IF_DEF(LARGEADR, IS_NONE, JMP) // large address pseudo-op (adrp + add) +IF_DEF(LARGELDC, IS_NONE, JMP) // large constant pseudo-op (adrp + ldr) ///////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp index 2cfed2e23c..24312f239d 100644 --- a/src/jit/flowgraph.cpp +++ b/src/jit/flowgraph.cpp @@ -6666,6 +6666,19 @@ bool Compiler::fgInDifferentRegions(BasicBlock *blk1, BasicBlock *blk2) return ((blk1->bbFlags & BBF_COLD)!= (blk2->bbFlags & BBF_COLD)); } +bool Compiler::fgIsBlockCold(BasicBlock *blk) +{ + noway_assert(blk != NULL); + + if (fgFirstColdBlock == NULL) + { + return false; + } + + return ((blk->bbFlags & BBF_COLD) != 0); +} + + /***************************************************************************** * This function returns true if tree is a GT_COMMA node with a call * that unconditionally throws an exception diff --git a/src/jit/jitconfigvalues.h b/src/jit/jitconfigvalues.h index fc65436212..bae4c74a4a 100644 --- a/src/jit/jitconfigvalues.h +++ b/src/jit/jitconfigvalues.h @@ -58,7 +58,7 @@ CONFIG_INTEGER(JitInlineAdditionalMultiplier, W("JitInlineAdditionalMultiplier") CONFIG_INTEGER(JitInlinePrintStats, W("JitInlinePrintStats"), 0) CONFIG_INTEGER(JitInlineSize, W("JITInlineSize"), DEFAULT_MAX_INLINE_SIZE) CONFIG_INTEGER(JitInlineDepth, W("JITInlineDepth"), DEFAULT_MAX_INLINE_DEPTH) -CONFIG_INTEGER(JitLargeBranches, W("JitLargeBranches"), 0) // Force using the largest conditional branch format +CONFIG_INTEGER(JitLongAddress, W("JitLongAddress"), 0) // Force using the large pseudo instruction form for long address CONFIG_INTEGER(JitMaxTempAssert, W("JITMaxTempAssert"), 1) CONFIG_INTEGER(JitMaxUncheckedOffset, W("JitMaxUncheckedOffset"), 8) CONFIG_INTEGER(JitMinOpts, W("JITMinOpts"), 0) // Forces MinOpts diff --git a/src/jit/lowerarm64.cpp b/src/jit/lowerarm64.cpp index d03b771035..b3475cd4a9 100644 --- a/src/jit/lowerarm64.cpp +++ b/src/jit/lowerarm64.cpp @@ -201,6 +201,20 @@ void Lowering::TreeNodeInfoInit(GenTree* stmt) case GT_CNS_DBL: info->srcCount = 0; info->dstCount = 1; + { + GenTreeDblCon *dblConst = tree->AsDblCon(); + double constValue = dblConst->gtDblCon.gtDconVal; + + if (emitter::emitIns_valid_imm_for_fmov(constValue)) + { + // Directly encode constant to instructions. + } + else + { + // Reserve int to load constant from memory (IF_LARGELDC) + info->internalIntCount = 1; + } + } break; case GT_QMARK: diff --git a/src/jit/target.h b/src/jit/target.h index 2f43f2c8c9..4810dfe690 100644 --- a/src/jit/target.h +++ b/src/jit/target.h @@ -1713,13 +1713,21 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits #define LBL_DIST_SMALL_MAX_POS (+1048575) #define LBL_SIZE_SMALL (4) - #define LBL_SIZE_LARGE (8) // NYI + #define LBL_SIZE_LARGE (8) #define JCC_DIST_SMALL_MAX_NEG (-1048576) - #define JCC_DIST_SMALL_MAX_POS (+1048572) + #define JCC_DIST_SMALL_MAX_POS (+1048575) #define JCC_SIZE_SMALL (4) - #define JCC_SIZE_LARGE (8) // NYI + #define JCC_SIZE_LARGE (8) + + #define LDC_DIST_SMALL_MAX_NEG (-1048576) + #define LDC_DIST_SMALL_MAX_POS (+1048575) + + #define LDC_SIZE_SMALL (4) + #define LDC_SIZE_LARGE (8) + + #define JMP_SIZE_SMALL (4) #else #error Unsupported or unset target architecture diff --git a/tests/arm64/Tests.lst b/tests/arm64/Tests.lst index d189310e10..d6bdd4ac0b 100644 --- a/tests/arm64/Tests.lst +++ b/tests/arm64/Tests.lst @@ -39127,7 +39127,7 @@ RelativePath=JIT\Regression\VS-ia64-JIT\V1.2-M02\b28158\b28158\b28158.cmd WorkingDir=JIT\Regression\VS-ia64-JIT\V1.2-M02\b28158\b28158 Expected=0 MaxAllowedDurationSeconds=600 -Categories=Pri0;JIT;EXPECTED_FAIL;ISSUE_3668;LONG_RUNNING +Categories=Pri0;JIT;EXPECTED_PASS HostStyle=0 [b28158_64.cmd_5696] RelativePath=JIT\Regression\VS-ia64-JIT\V1.2-M02\b28158\b28158_64\b28158_64.cmd |