diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 5308e680efa374..07ccdb02951347 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -235,7 +235,7 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, { // We will use lea so displacement and not immediate will be relocatable size = EA_SET_FLG(EA_REMOVE_FLG(size, EA_CNS_RELOC_FLG), EA_DSP_RELOC_FLG); - GetEmitter()->emitIns_R_AI(INS_lea, size, reg, imm DEBUGARG(targetHandle) DEBUGARG(gtFlags)); + GetEmitter()->emitIns_R_AI(INS_lea, size, reg, imm, 0 DEBUGARG(targetHandle) DEBUGARG(gtFlags)); } else { @@ -496,8 +496,35 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre } } - instGen_Set_Reg_To_Imm(attr, targetReg, cnsVal, - INS_FLAGS_DONT_CARE DEBUGARG(con->GetTargetHandle()) DEBUGARG(con->gtFlags)); +#ifdef TARGET_AMD64 + if (con->IsIconHandle(GTF_ICON_RELOC_ADDR)) + { + assert(m_compiler->IsTargetAbi(CORINFO_NATIVEAOT_ABI)); + + if (m_compiler->opts.compReloc && genDataIndirAddrCanBeEncodedAsPCRelOffset(cnsVal)) + { + emitAttr leaAttr = EA_SET_FLG(EA_REMOVE_FLG(attr, EA_CNS_RELOC_FLG), EA_DSP_RELOC_FLG); + GetEmitter()->emitIns_R_AI(INS_lea, leaAttr, targetReg, cnsVal, + con->GetRelocOffset() DEBUGARG(con->GetTargetHandle()) + DEBUGARG(con->gtFlags)); + } + else + { + instGen_Set_Reg_To_Imm(attr, targetReg, cnsVal, + INS_FLAGS_DONT_CARE DEBUGARG(con->GetTargetHandle()) DEBUGARG(con->gtFlags)); + if (con->GetRelocOffset() != 0) + { + emitAttr addAttr = EA_IS_BYREF(attr) ? EA_BYREF : EA_PTRSIZE; + GetEmitter()->emitIns_R_I(INS_add, addAttr, targetReg, con->GetRelocOffset()); + } + } + } + else +#endif // TARGET_AMD64 + { + instGen_Set_Reg_To_Imm(attr, targetReg, cnsVal, + INS_FLAGS_DONT_CARE DEBUGARG(con->GetTargetHandle()) DEBUGARG(con->gtFlags)); + } regSet.verifyRegUsed(targetReg); } break; diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 780f06bd1b138c..8a6cc732eb2c68 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -2575,6 +2575,7 @@ class emitter ssize_t emitGetInsCns(instrDesc* id) const; ssize_t emitGetInsDsp(instrDesc* id) const; ssize_t emitGetInsAmd(instrDesc* id) const; + int32_t emitGetInsAmdRelocOffset(const instrDesc* id) const; ssize_t emitGetInsCIdisp(instrDesc* id) const; unsigned emitGetInsCIargs(instrDesc* id) const; diff --git a/src/coreclr/jit/emitinl.h b/src/coreclr/jit/emitinl.h index ecd74685901e7b..bca5f55284e3a1 100644 --- a/src/coreclr/jit/emitinl.h +++ b/src/coreclr/jit/emitinl.h @@ -124,6 +124,21 @@ inline ssize_t emitter::emitGetInsAmd(instrDesc* id) const return id->idIsLargeDsp() ? ((instrDescAmd*)id)->idaAmdVal : id->idAddr()->iiaAddrMode.amDisp; } +inline int32_t emitter::emitGetInsAmdRelocOffset(const instrDesc* id) const +{ + assert(id->idIsDspReloc()); + + if (!id->idIsLargeCns()) + { + return 0; + } + + assert(id->idIsLargeDsp()); + ssize_t offset = ((instrDescCnsAmd*)id)->idacCnsVal; + assert(FitsIn(offset)); + return static_cast(offset); +} + inline int emitter::emitGetInsCDinfo(instrDesc* id) { if (id->idIsLargeCall()) diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 416c99d85d3584..36110b8391200f 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -9263,17 +9263,36 @@ void emitter::emitIns_R_AR(instruction ins, emitAttr attr, regNumber reg, regNum emitIns_R_ARX(ins, attr, reg, base, REG_NA, 1, disp); } -void emitter::emitIns_R_AI(instruction ins, - emitAttr attr, - regNumber ireg, - ssize_t disp DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) +void emitter::emitIns_R_AI(instruction ins, + emitAttr attr, + regNumber ireg, + ssize_t disp, + int32_t relocOffset DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) { assert((CodeGen::instIsFP(ins) == false) && (EA_SIZE(attr) <= EA_8BYTE) && (ireg != REG_NA)); noway_assert(emitVerifyEncodable(ins, EA_SIZE(attr), ireg)); + assert((relocOffset == 0) || EA_IS_DSP_RELOC(attr)); UNATIVE_OFFSET sz; - instrDesc* id = emitNewInstrAmd(attr, disp); - insFormat fmt = emitInsModeFormat(ins, IF_RRD_ARD); + instrDesc* id; + if (relocOffset == 0) + { + id = emitNewInstrAmd(attr, disp); + } + else + { + // IF_RRD_ARD has no immediate, so use the large constant slot for the relocation addend. + instrDescCnsAmd* relocId = emitAllocInstrCnsAmd(attr); + relocId->idSetIsLargeCns(); + relocId->idSetIsLargeDsp(); +#ifdef DEBUG + relocId->idAddr()->iiaAddrMode.amDisp = AM_DISP_BIG_VAL; +#endif + relocId->idacCnsVal = relocOffset; + relocId->idacAmdVal = disp; + id = relocId; + } + insFormat fmt = emitInsModeFormat(ins, IF_RRD_ARD); id->idIns(ins); id->idInsFmt(fmt); @@ -14425,7 +14444,7 @@ BYTE* emitter::emitOutputAM(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) dst += emitOutputWord(dst, code | 0x0500); } - INT32 addlDelta = 0; + INT32 addlDelta = (addc == nullptr) ? emitGetInsAmdRelocOffset(id) : 0; #ifdef TARGET_AMD64 if (addc) { diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h index 038f6bd29177cf..32a36aee32954c 100644 --- a/src/coreclr/jit/emitxarch.h +++ b/src/coreclr/jit/emitxarch.h @@ -1073,10 +1073,11 @@ void emitIns_I_AR( void emitIns_R_AR(instruction ins, emitAttr attr, regNumber reg, regNumber base, int disp); -void emitIns_R_AI(instruction ins, - emitAttr attr, - regNumber ireg, - ssize_t disp DEBUGARG(size_t targetHandle = 0) DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); +void emitIns_R_AI(instruction ins, + emitAttr attr, + regNumber ireg, + ssize_t disp, + int32_t relocOffset = 0 DEBUGARG(size_t targetHandle = 0) DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); void emitIns_AR_R(instruction ins, emitAttr attr, diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index c0d4c5a9c61a60..24e253fea301d1 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -20219,6 +20219,12 @@ UINT64 GenTreeIntConCommon::UnsignedIntegralValue() const // be encoded as 32-bit offset relative to IP or zero. bool GenTreeIntConCommon::FitsInAddrBase(Compiler* comp) { + // The relocation addend is not part of IconValue(), so address containment cannot preserve it. + if (IsIconHandle(GTF_ICON_RELOC_ADDR)) + { + return false; + } + #ifdef DEBUG // Early out if PC-rel encoding of absolute addr is disabled. if (!comp->opts.compEnablePCRelAddr) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 6b2c5f2d95e70d..faaa43adcc0488 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -3430,6 +3430,7 @@ struct GenTreeIntCon : public GenTreeIntConCommon in AOT mode, the handle in that statement does not correspond to the compile time handle (rather it lets you get a handle at run-time). In that case, we also need to store a compile time handle, which goes in this gtCompileTimeHandle field. + For GTF_ICON_RELOC_ADDR, this field instead stores the relocation addend. */ ssize_t gtCompileTimeHandle; @@ -3458,6 +3459,19 @@ struct GenTreeIntCon : public GenTreeIntConCommon gtCompileTimeHandle = compileTimeHandle; } + int32_t GetRelocOffset() const + { + assert(IsIconHandle(GTF_ICON_RELOC_ADDR)); + assert(FitsIn(gtCompileTimeHandle)); + return static_cast(gtCompileTimeHandle); + } + + void SetRelocOffset(int32_t offset) + { + assert(IsIconHandle(GTF_ICON_RELOC_ADDR)); + gtCompileTimeHandle = offset; + } + // Accessors for the field sequence. See "gtFieldSeq" above. FieldSeq* GetFieldSeq() const { diff --git a/src/coreclr/jit/handlekinds.h b/src/coreclr/jit/handlekinds.h index 9516c5193c8baf..d6309e0b3ba4f2 100644 --- a/src/coreclr/jit/handlekinds.h +++ b/src/coreclr/jit/handlekinds.h @@ -27,6 +27,7 @@ HANDLE_KIND(GTF_ICON_FIELD_SEQ , "field seq" , 0) HANDLE_KIND(GTF_ICON_STATIC_ADDR_PTR , "static base addr cell" , HKF_INVARIANT | HKF_NONNULL) // pointer to a static base address HANDLE_KIND(GTF_ICON_SECREL_OFFSET , "relative offset in section" , HKF_INVARIANT) // offset in a certain section. HANDLE_KIND(GTF_ICON_TLSGD_OFFSET , "tls global dynamic offset" , HKF_INVARIANT) // argument to tls_get_addr. +HANDLE_KIND(GTF_ICON_RELOC_ADDR , "relocatable address" , 0) // relocatable handle with an addend #undef HANDLE_KIND // clang-format on diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 73ab90010716a3..e969151a13dbf7 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -8011,16 +8011,36 @@ GenTree* Lowering::LowerAdd(GenTreeOp* node) // We could do this folding earlier, but that is not trivial as we'll have to introduce a way to restore // the original object from a byref constant for optimizations. if (op1->IsCnsIntOrI() && op2->IsCnsIntOrI() && !node->gtOverflow() && - (op1->IsIconHandle(GTF_ICON_OBJ_HDL) || op2->IsIconHandle(GTF_ICON_OBJ_HDL)) && - !op1->AsIntCon()->ImmedValNeedsReloc(m_compiler) && !op2->AsIntCon()->ImmedValNeedsReloc(m_compiler)) + (op1->IsIconHandle(GTF_ICON_OBJ_HDL) || op2->IsIconHandle(GTF_ICON_OBJ_HDL))) { assert(node->TypeIs(TYP_I_IMPL, TYP_BYREF)); - // TODO-CQ: we should allow this for AOT too. For that we need to guarantee that the new constant - // will be lowered as the original handle with offset in a reloc. - BlockRange().Remove(op1); - BlockRange().Remove(op2); - node->BashToConst(op1->AsIntCon()->IconValue() + op2->AsIntCon()->IconValue(), node->TypeGet()); + GenTreeIntCon* objHandle = op1->IsIconHandle(GTF_ICON_OBJ_HDL) ? op1->AsIntCon() : op2->AsIntCon(); + GenTreeIntCon* offset = (objHandle == op1) ? op2->AsIntCon() : op1->AsIntCon(); + + if (!objHandle->ImmedValNeedsReloc(m_compiler) && !offset->ImmedValNeedsReloc(m_compiler)) + { + BlockRange().Remove(op1); + BlockRange().Remove(op2); + node->BashToConst(op1->AsIntCon()->IconValue() + op2->AsIntCon()->IconValue(), node->TypeGet()); + } +#ifdef TARGET_AMD64 + else if (m_compiler->IsTargetAbi(CORINFO_NATIVEAOT_ABI) && objHandle->ImmedValNeedsReloc(m_compiler) && + !offset->IsIconHandle() && !offset->ImmedValNeedsReloc(m_compiler) && + FitsIn(offset->IconValue())) + { + FieldSeq* fieldSeq = + m_compiler->GetFieldSeqStore()->Append(objHandle->GetFieldSeq(), offset->GetFieldSeq()); + int32_t relocOffset = static_cast(offset->IconValue()); + + BlockRange().Remove(op1); + BlockRange().Remove(op2); + node->BashToConst(objHandle->IconValue(), node->TypeGet()); + node->gtFlags |= GTF_ICON_RELOC_ADDR; + node->AsIntCon()->SetRelocOffset(relocOffset); + node->AsIntCon()->SetFieldSeq(fieldSeq); + } +#endif // TARGET_AMD64 } }