diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 2da2a737bccc32..86680d6e20c91e 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -3372,8 +3372,18 @@ class ICorDynamicInfo : public ICorStaticInfo #define IMAGE_REL_BASED_REL32 0x10 #define IMAGE_REL_BASED_THUMB_BRANCH24 0x13 #define IMAGE_REL_SECREL 0x104 + +// Linux x64 +// GD model #define IMAGE_REL_TLSGD 0x105 +// Linux arm64 +// TLSDESC (dynamic) +#define IMAGE_REL_AARCH64_TLSDESC_ADR_PAGE21 0x107 +#define IMAGE_REL_AARCH64_TLSDESC_LD64_LO12 0x108 +#define IMAGE_REL_AARCH64_TLSDESC_ADD_LO12 0x109 +#define IMAGE_REL_AARCH64_TLSDESC_CALL 0x10A + // The identifier for ARM32-specific PC-relative address // computation corresponds to the following instruction // sequence: diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 5d63f9df9a80d4..6355fc20dd0fd5 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* b8a05f18-503e-47e4-9193-931c50b151d1 */ - 0xb8a05f18, - 0x503e, - 0x47e4, - {0x91, 0x93, 0x93, 0x1c, 0x50, 0xb1, 0x51, 0xd1} +constexpr GUID JITEEVersionIdentifier = { /* 0fb71692-0ee6-4914-88a8-6446e45f23e8 */ + 0x0fb71692, + 0x0ee6, + 0x4914, + {0x88, 0xa8, 0x64, 0x46, 0xe4, 0x5f, 0x23, 0xe8} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 4efe4a235f6a7d..3883b20118ad8a 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -2333,6 +2333,11 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre if (con->ImmedValNeedsReloc(compiler)) { attr = EA_SET_FLG(attr, EA_CNS_RELOC_FLG); + if (tree->IsTlsIconHandle()) + { + // no need to generate because we generate it as part of GT_CALL + break; + } } if (targetType == TYP_BYREF) diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index cb04cf702f2b94..92e99471956f0d 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -3623,6 +3623,44 @@ void CodeGen::genCallInstruction(GenTreeCall* call) // assert(genIsValidIntReg(target->GetRegNum())); +#ifdef TARGET_ARM64 + bool isTlsHandleTarget = + compiler->IsTargetAbi(CORINFO_NATIVEAOT_ABI) && TargetOS::IsUnix && target->IsTlsIconHandle(); + + if (isTlsHandleTarget) + { + assert(call->gtFlags & GTF_TLS_GET_ADDR); + emitter* emitter = GetEmitter(); + emitAttr attr = (emitAttr)(EA_CNS_TLSGD_RELOC | EA_CNS_RELOC_FLG | retSize); + GenTreeIntCon* iconNode = target->AsIntCon(); + methHnd = (CORINFO_METHOD_HANDLE)iconNode->gtIconVal; + retSize = EA_SET_FLG(retSize, EA_CNS_TLSGD_RELOC); + + // For NativeAOT, linux/arm64, linker wants the following pattern, so we will generate + // it as part of the call. Generating individual instructions is tricky to get it + // correct in the format the way linker needs. Also, we might end up spilling or + // reloading a register, which can break the pattern. + // + // mrs x1, tpidr_el0 + // adrp x0, :tlsdesc:tlsRoot ; R_AARCH64_TLSDESC_ADR_PAGE21 + // ldr x2, [x0] ; R_AARCH64_TLSDESC_LD64_LO12 + // add x0, x0, #0 ; R_AARCH64_TLSDESC_ADD_LO12 + // blr x2 ; R_AARCH64_TLSDESC_CALL + // add x0, x1, x0 + // We guaranteed in LSRA that r0, r1 and r2 are assigned to this node. + + // mrs + emitter->emitIns_R(INS_mrs_tpid0, attr, REG_R1); + + // adrp + // ldr + // add + emitter->emitIns_Adrp_Ldr_Add(attr, REG_R0, target->GetRegNum(), + (ssize_t)methHnd DEBUGARG(iconNode->gtTargetHandle) + DEBUGARG(iconNode->gtFlags)); + } +#endif + // clang-format off genEmitCall(emitter::EC_INDIR_R, methHnd, @@ -3633,6 +3671,14 @@ void CodeGen::genCallInstruction(GenTreeCall* call) di, target->GetRegNum(), call->IsFastTailCall()); + +#ifdef TARGET_ARM64 + if (isTlsHandleTarget) + { + // add x0, x1, x0 + GetEmitter()->emitIns_R_R_R(INS_add, EA_8BYTE, REG_R0, REG_R1, REG_R0); + } +#endif // clang-format on } else diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index f460833a28e31c..a59dc7713c2874 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -215,7 +215,15 @@ void emitter::emitInsSanityCheck(instrDesc* id) break; case IF_BR_1B: // BR_1B ................ ......nnnnn..... Rn - assert(isGeneralRegister(id->idReg3())); + if (emitComp->IsTargetAbi(CORINFO_NATIVEAOT_ABI) && id->idIsTlsGD()) + { + assert(isGeneralRegister(id->idReg1())); + assert(id->idAddr()->iiaAddr != nullptr); + } + else + { + assert(isGeneralRegister(id->idReg3())); + } break; case IF_LS_1A: // LS_1A .X......iiiiiiii iiiiiiiiiiittttt Rt PC imm(1MB) @@ -227,7 +235,7 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(isIntegerRegister(id->idReg1()) || // ZR isVectorRegister(id->idReg1())); assert(isIntegerRegister(id->idReg2())); // SP - assert(emitGetInsSC(id) == 0); + assert((emitGetInsSC(id) == 0) || (id->idIsTlsGD())); assert(insOptsNone(id->idInsOpt())); break; @@ -9244,7 +9252,7 @@ void emitter::emitIns_R_R_I(instruction ins, reg2 = encodingSPtoZR(reg2); ssize_t mask = (1 << scale) - 1; // the mask of low bits that must be zero to encode the immediate - if (imm == 0) + if (imm == 0 || EA_IS_CNS_TLSGD_RELOC(attr)) { assert(insOptsNone(opt)); // PRE/POST Index doesn't make sense with an immediate of zero @@ -9351,7 +9359,11 @@ void emitter::emitIns_R_R_I(instruction ins, id->idReg1(reg1); id->idReg2(reg2); - + if (EA_IS_CNS_TLSGD_RELOC(attr)) + { + assert(imm != 0); + id->idSetTlsGD(); + } dispIns(id); appendToCurIG(id); } @@ -14980,6 +14992,61 @@ void emitter::emitIns_R_AR(instruction ins, emitAttr attr, regNumber ireg, regNu NYI("emitIns_R_AR"); } +// This generates code to populate the access for TLS on linux +void emitter::emitIns_Adrp_Ldr_Add(emitAttr attr, + regNumber reg1, + regNumber reg2, + ssize_t addr DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) +{ + assert(emitComp->IsTargetAbi(CORINFO_NATIVEAOT_ABI)); + assert(TargetOS::IsUnix); + assert(EA_IS_RELOC(attr)); + assert(EA_IS_CNS_TLSGD_RELOC(attr)); + + emitAttr size = EA_SIZE(attr); + insFormat fmt = IF_DI_1E; + bool needAdd = false; + instrDescJmp* id = emitNewInstrJmp(); + + // adrp + id->idIns(INS_adrp); + id->idInsFmt(fmt); + id->idInsOpt(INS_OPTS_NONE); + id->idOpSize(size); + id->idAddr()->iiaAddr = (BYTE*)addr; + id->idReg1(reg1); + id->idSetIsDspReloc(); + id->idSetTlsGD(); + +#ifdef DEBUG + id->idDebugOnlyInfo()->idMemCookie = targetHandle; + id->idDebugOnlyInfo()->idFlags = gtFlags; +#endif + + dispIns(id); + appendToCurIG(id); + + // ldr + emitIns_R_R_I(INS_ldr, attr, reg2, reg1, (ssize_t)addr); + + // add + fmt = IF_DI_2A; + instrDesc* addId = emitNewInstr(attr); + assert(id->idIsReloc()); + + addId->idIns(INS_add); + addId->idInsFmt(fmt); + addId->idInsOpt(INS_OPTS_NONE); + addId->idOpSize(size); + addId->idAddr()->iiaAddr = (BYTE*)addr; + addId->idReg1(reg1); + addId->idReg2(reg1); + addId->idSetTlsGD(); + + dispIns(addId); + appendToCurIG(addId); +} + // This computes address from the immediate which is relocatable. void emitter::emitIns_R_AI(instruction ins, emitAttr attr, @@ -15493,8 +15560,21 @@ void emitter::emitIns_Call(EmitCallType callType, id->idIns(ins); id->idInsFmt(fmt); - id->idReg3(ireg); assert(xreg == REG_NA); + if (emitComp->IsTargetAbi(CORINFO_NATIVEAOT_ABI) && EA_IS_CNS_TLSGD_RELOC(retSize)) + { + // For NativeAOT linux/arm64, we need to also record the relocation of methHnd. + // Since we do not have space to embed it in instrDesc, we store the register in + // reg1 and instead use the `iiaAdd` to store the method handle. Likewise, during + // emitOutputInstr, we retrieve the register from reg1 for this specific case. + id->idSetTlsGD(); + id->idReg1(ireg); + id->idAddr()->iiaAddr = (BYTE*)methHnd; + } + else + { + id->idReg3(ireg); + } } else { @@ -19588,10 +19668,19 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) assert(insOptsNone(id->idInsOpt())); assert((ins == INS_br_tail) || (ins == INS_blr)); code = emitInsCode(ins, fmt); - code |= insEncodeReg_Rn(id->idReg3()); // nnnnn - sz = id->idIsLargeCall() ? sizeof(instrDescCGCA) : sizeof(instrDesc); + if (emitComp->IsTargetAbi(CORINFO_NATIVEAOT_ABI) && id->idIsTlsGD()) + { + emitRecordRelocation(odst, (CORINFO_METHOD_HANDLE)id->idAddr()->iiaAddr, + IMAGE_REL_AARCH64_TLSDESC_CALL); + code |= insEncodeReg_Rn(id->idReg1()); // nnnnn + } + else + { + code |= insEncodeReg_Rn(id->idReg3()); // nnnnn + } dst += emitOutputCall(ig, dst, id, code); + sz = id->idIsLargeCall() ? sizeof(instrDescCGCA) : sizeof(instrDesc); break; case IF_LS_1A: // LS_1A XX...V..iiiiiiii iiiiiiiiiiittttt Rt PC imm(1MB) @@ -19620,6 +19709,10 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) } code |= insEncodeReg_Rn(id->idReg2()); // nnnnn dst += emitOutput_Instr(dst, code); + if (id->idIsTlsGD()) + { + emitRecordRelocation(odst, (void*)emitGetInsSC(id), IMAGE_REL_AARCH64_TLSDESC_LD64_LO12); + } break; case IF_LS_2B: // LS_2B .X.......Xiiiiii iiiiiinnnnnttttt Rt Rn imm(0-4095) @@ -19885,7 +19978,8 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) code = emitInsCode(ins, fmt); code |= insEncodeReg_Rd(id->idReg1()); // ddddd dst += emitOutput_Instr(dst, code); - emitRecordRelocation(odst, id->idAddr()->iiaAddr, IMAGE_REL_ARM64_PAGEBASE_REL21); + emitRecordRelocation(odst, id->idAddr()->iiaAddr, id->idIsTlsGD() ? IMAGE_REL_AARCH64_TLSDESC_ADR_PAGE21 + : IMAGE_REL_ARM64_PAGEBASE_REL21); } else { @@ -19928,7 +20022,8 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) { assert(sz == sizeof(instrDesc)); assert(id->idAddr()->iiaAddr != nullptr); - emitRecordRelocation(odst, id->idAddr()->iiaAddr, IMAGE_REL_ARM64_PAGEOFFSET_12A); + emitRecordRelocation(odst, id->idAddr()->iiaAddr, id->idIsTlsGD() ? IMAGE_REL_AARCH64_TLSDESC_ADD_LO12 + : IMAGE_REL_ARM64_PAGEOFFSET_12A); } break; @@ -23362,7 +23457,7 @@ void emitter::emitDispInsHelp( case IF_LS_2A: // LS_2A .X.......X...... ......nnnnnttttt Rt Rn assert(insOptsNone(id->idInsOpt())); - assert(emitGetInsSC(id) == 0); + assert((emitGetInsSC(id) == 0) || id->idIsTlsGD()); emitDispReg(id->idReg1(), emitInsTargetRegSize(id), true); emitDispAddrRI(id->idReg2(), id->idInsOpt(), 0); break; diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index abc745e9421c53..b29bed3709b27e 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -1443,6 +1443,11 @@ void emitIns_I_AR(instruction ins, emitAttr attr, int val, regNumber reg, int of void emitIns_R_AR(instruction ins, emitAttr attr, regNumber ireg, regNumber reg, int offs); +void emitIns_Adrp_Ldr_Add(emitAttr attr, + regNumber reg1, + regNumber reg2, + ssize_t addr DEBUGARG(size_t targetHandle = 0) DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); + void emitIns_R_AI(instruction ins, emitAttr attr, regNumber ireg, diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index e11c7605e2a201..2bfd5d39f4c7a5 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -2234,6 +2234,16 @@ struct GenTree return (gtOper == GT_CNS_INT) ? (gtFlags & GTF_ICON_HDL_MASK) : GTF_EMPTY; } + bool IsTlsIconHandle() + { + if (IsIconHandle()) + { + GenTreeFlags tlsFlags = (GTF_ICON_TLSGD_OFFSET | GTF_ICON_TLS_HDL); + return ((gtFlags & tlsFlags) == tlsFlags); + } + return false; + } + // Mark this node as no longer being a handle; clear its GTF_ICON_*_HDL bits. void ClearIconHandleMask() { diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index f0b042b16ca915..07fac6ed1b0816 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -576,8 +576,8 @@ bool Compiler::fgExpandThreadLocalAccessForCallNativeAOT(BasicBlock** pBlock, St // use(tlsRoot); // ... - GenTree* tlsRootAddr = nullptr; - CORINFO_CONST_LOOKUP tlsRootObject = threadStaticInfo.tlsRootObject; + GenTree* tlsRootAddr = nullptr; + CORINFO_GENERIC_HANDLE tlsRootObject = threadStaticInfo.tlsRootObject.handle; if (TargetOS::IsWindows) { @@ -598,7 +598,7 @@ bool Compiler::fgExpandThreadLocalAccessForCallNativeAOT(BasicBlock** pBlock, St tlsValue = gtNewIndir(TYP_I_IMPL, tlsValue, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); // This resolves to an offset which is TYP_INT - GenTree* tlsRootOffset = gtNewIconNode((size_t)tlsRootObject.handle, TYP_INT); + GenTree* tlsRootOffset = gtNewIconNode((size_t)tlsRootObject, TYP_INT); tlsRootOffset->gtFlags |= GTF_ICON_SECREL_OFFSET; // Add the tlsValue and tlsRootOffset to produce tlsRootAddr. @@ -606,34 +606,63 @@ bool Compiler::fgExpandThreadLocalAccessForCallNativeAOT(BasicBlock** pBlock, St } else if (TargetOS::IsUnix) { - // Code sequence to access thread local variable on linux/x64: - // data16 - // lea rdi, 0x7FE5C418CD28 ; tlsRootObject - // data16 data16 - // call _tls_get_addr - // - // This sequence along with `data16` prefix is expected by the linker so it - // will patch these with TLS access. - GenTree* tls_get_addr_val = - gtNewIconHandleNode((size_t)threadStaticInfo.tlsGetAddrFtnPtr.handle, GTF_ICON_FTN_ADDR); - tls_get_addr_val->SetContained(); + if (TargetArchitecture::IsX64) + { + // Code sequence to access thread local variable on linux/x64: + // data16 + // lea rdi, 0x7FE5C418CD28 ; tlsRootObject + // data16 data16 + // call _tls_get_addr + // + // This sequence along with `data16` prefix is expected by the linker so it + // will patch these with TLS access. + GenTree* tls_get_addr_val = + gtNewIconHandleNode((size_t)threadStaticInfo.tlsGetAddrFtnPtr.handle, GTF_ICON_FTN_ADDR); + tls_get_addr_val->SetContained(); + + GenTreeCall* tlsRefCall = gtNewIndCallNode(tls_get_addr_val, TYP_I_IMPL); + tlsRefCall->gtFlags |= GTF_TLS_GET_ADDR; + + // This is an indirect call which takes an argument. + // Populate and set the ABI appropriately. + assert(tlsRootObject != 0); + GenTree* tlsArg = gtNewIconNode((size_t)tlsRootObject, TYP_I_IMPL); + tlsArg->gtFlags |= GTF_ICON_TLSGD_OFFSET; + tlsRefCall->gtArgs.PushBack(this, NewCallArg::Primitive(tlsArg)); + + fgMorphArgs(tlsRefCall); + + tlsRefCall->gtFlags |= GTF_EXCEPT | (tls_get_addr_val->gtFlags & GTF_GLOB_EFFECT); + tlsRootAddr = tlsRefCall; + } + else if (TargetArchitecture::IsArm64) + { + /* + x0 = adrp :tlsdesc:tlsRoot ; 1st parameter + x0 += tlsdesc_lo12:tlsRoot ; update 1st parameter - // GenTreeCall* tlsRefCall = gtNewCallNode(CT_ tls_get_addr_val, TYP_I_IMPL); - GenTreeCall* tlsRefCall = gtNewIndCallNode(tls_get_addr_val, TYP_I_IMPL); - tlsRefCall->gtFlags |= GTF_TLS_GET_ADDR; - // // + x1 = tpidr_el0 ; 2nd parameter - // This is an indirect call which takes an argument. - // Populate and set the ABI appropriately. - assert(tlsRootObject.handle != 0); - GenTree* tlsArg = gtNewIconNode((size_t)tlsRootObject.handle, TYP_I_IMPL); - tlsArg->gtFlags |= GTF_ICON_TLSGD_OFFSET; - tlsRefCall->gtArgs.PushBack(this, NewCallArg::Primitive(tlsArg)); + x2 = [x0] ; call + blr x2 - fgMorphArgs(tlsRefCall); + */ - tlsRefCall->gtFlags |= GTF_EXCEPT | (tls_get_addr_val->gtFlags & GTF_GLOB_EFFECT); - tlsRootAddr = tlsRefCall; + GenTree* tlsRootOffset = gtNewIconHandleNode((size_t)tlsRootObject, GTF_ICON_TLS_HDL); + tlsRootOffset->gtFlags |= GTF_ICON_TLSGD_OFFSET; + + GenTree* tlsCallIndir = gtCloneExpr(tlsRootOffset); + GenTreeCall* tlsRefCall = gtNewIndCallNode(tlsCallIndir, TYP_I_IMPL); + tlsRefCall->gtFlags |= GTF_TLS_GET_ADDR; + fgMorphArgs(tlsRefCall); + + tlsRefCall->gtFlags |= GTF_EXCEPT | (tlsCallIndir->gtFlags & GTF_GLOB_EFFECT); + tlsRootAddr = tlsRefCall; + } + else + { + unreached(); + } } else { diff --git a/src/coreclr/jit/lsraarmarch.cpp b/src/coreclr/jit/lsraarmarch.cpp index 8b1305caec52ae..a0f27ba65b3058 100644 --- a/src/coreclr/jit/lsraarmarch.cpp +++ b/src/coreclr/jit/lsraarmarch.cpp @@ -368,6 +368,21 @@ int LinearScan::BuildCall(GenTreeCall* call) if (ctrlExpr != nullptr) { +#ifdef TARGET_ARM64 + if (compiler->IsTargetAbi(CORINFO_NATIVEAOT_ABI) && TargetOS::IsUnix && (call->gtArgs.CountArgs() == 0) && + ctrlExpr->IsTlsIconHandle()) + { + // For NativeAOT linux/arm64, we generate the needed code as part of + // call node because the generated code has to be in specific format + // that linker can patch. As such, the code needs specific registers + // that we will attach to this node to guarantee that they are available + // during generating this node. + assert(call->gtFlags & GTF_TLS_GET_ADDR); + newRefPosition(REG_R0, currentLoc, RefTypeFixedReg, nullptr, genRegMask(REG_R0)); + newRefPosition(REG_R1, currentLoc, RefTypeFixedReg, nullptr, genRegMask(REG_R1)); + ctrlExprCandidates = genRegMask(REG_R2); + } +#endif BuildUse(ctrlExpr, ctrlExprCandidates); srcCount++; } diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index 3d2441891fbfed..7585ca75fa8b4a 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -487,8 +487,11 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v case RelocType.IMAGE_REL_TPOFF: case RelocType.IMAGE_REL_SYMBOL_SIZE: case RelocType.IMAGE_REL_FILE_ABSOLUTE: + case RelocType.IMAGE_REL_AARCH64_TLSDESC_CALL: *(int*)location = (int)value; break; + case RelocType.IMAGE_REL_AARCH64_TLSDESC_LD64_LO12: + break; case RelocType.IMAGE_REL_BASED_DIR64: *(long*)location = value; break; @@ -503,9 +506,11 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v PutArm64Rel28((uint*)location, value); break; case RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21: + case RelocType.IMAGE_REL_AARCH64_TLSDESC_ADR_PAGE21: PutArm64Rel21((uint*)location, (int)value); break; case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A: + case RelocType.IMAGE_REL_AARCH64_TLSDESC_ADD_LO12: PutArm64Rel12((uint*)location, (int)value); break; case RelocType.IMAGE_REL_BASED_LOONGARCH64_PC: diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 62529c5b443c9b..2e72a18def7713 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3879,6 +3879,11 @@ private static RelocType GetRelocType(TargetArchitecture targetArchitecture, ush const ushort IMAGE_REL_ARM64_BRANCH26 = 3; const ushort IMAGE_REL_ARM64_PAGEBASE_REL21 = 4; const ushort IMAGE_REL_ARM64_PAGEOFFSET_12A = 6; + const ushort IMAGE_REL_ARM64_TLSDESC_ADR_PAGE21 = 0x107; + const ushort IMAGE_REL_ARM64_TLSDESC_LD64_LO12 = 0x108; + const ushort IMAGE_REL_ARM64_TLSDESC_ADD_LO12 = 0x109; + const ushort IMAGE_REL_ARM64_TLSDESC_CALL = 0x10A; + switch (fRelocType) { @@ -3888,6 +3893,14 @@ private static RelocType GetRelocType(TargetArchitecture targetArchitecture, ush return RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21; case IMAGE_REL_ARM64_PAGEOFFSET_12A: return RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A; + case IMAGE_REL_ARM64_TLSDESC_ADR_PAGE21: + return RelocType.IMAGE_REL_AARCH64_TLSDESC_ADR_PAGE21; + case IMAGE_REL_ARM64_TLSDESC_ADD_LO12: + return RelocType.IMAGE_REL_AARCH64_TLSDESC_ADD_LO12; + case IMAGE_REL_ARM64_TLSDESC_LD64_LO12: + return RelocType.IMAGE_REL_AARCH64_TLSDESC_LD64_LO12; + case IMAGE_REL_ARM64_TLSDESC_CALL: + return RelocType.IMAGE_REL_AARCH64_TLSDESC_CALL; default: Debug.Fail("Invalid RelocType: " + fRelocType); return 0; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs index 6d94afdc3ee4be..7f672fca6fa151 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs @@ -74,26 +74,7 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, ISortableSymbolNode index = factory.TypeThreadStaticIndex(target); if (index is TypeThreadStaticIndexNode ti && ti.IsInlined) { - if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) - { - EmitInlineTLSAccess(factory, ref encoder); - } - else - { - // First arg: unused address of the TypeManager - // encoder.EmitMOV(encoder.TargetRegister.Arg0, (ushort)0); - - // Second arg: ~0 (index of inlined storage) - encoder.EmitMVN(encoder.TargetRegister.Arg1, 0); - - encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); - encoder.EmitSUB(encoder.TargetRegister.Arg2, NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); - encoder.EmitLDR(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2); - encoder.EmitCMP(encoder.TargetRegister.Arg3, 0); - - encoder.EmitJNE(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); - EmitInlineTLSAccess(factory, ref encoder); - } + throw new NotImplementedException(); } else { @@ -226,81 +207,5 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, throw new NotImplementedException(); } } - - // emits code that results in ThreadStaticBase referenced in X0. - // may trash volatile registers. (there are calls to the slow helper and possibly to the platform's TLS support) - private static void EmitInlineTLSAccess(NodeFactory factory, ref ARM64Emitter encoder) - { - ISymbolNode getInlinedThreadStaticBaseSlow = factory.HelperEntrypoint(HelperEntrypoint.GetInlinedThreadStaticBaseSlow); - ISymbolNode tlsRoot = factory.TlsRoot; - // IsSingleFileCompilation is not enough to guarantee that we can use "Initial Executable" optimizations. - // we need a special compiler flag analogous to /GA. Just assume "false" for now. - // bool isInitialExecutable = factory.CompilationModuleGroup.IsSingleFileCompilation; - bool isInitialExecutable = false; - - if (factory.Target.OperatingSystem == TargetOS.Linux) - { - if (isInitialExecutable) - { - // mrs x0, tpidr_el0 - encoder.Builder.EmitUInt(0xd53bd040); - - // add x0, x0, #:tprel_hi12:tlsRoot, lsl #12 - encoder.Builder.EmitReloc(tlsRoot, RelocType.IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_HI12); - encoder.Builder.EmitUInt(0x91400000); - - // add x1, x0, #:tprel_lo12_nc:tlsRoot, lsl #0 - encoder.Builder.EmitReloc(tlsRoot, RelocType.IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_LO12_NC); - encoder.Builder.EmitUInt(0x91000001); - } - else - { - // stp x29, x30, [sp, -16]! - encoder.Builder.EmitUInt(0xa9bf7bfd); - // mov x29, sp - encoder.Builder.EmitUInt(0x910003fd); - - // mrs x1, tpidr_el0 - encoder.Builder.EmitUInt(0xd53bd041); - - // adrp x0, :tlsdesc:tlsRoot - encoder.Builder.EmitReloc(tlsRoot, RelocType.IMAGE_REL_AARCH64_TLSDESC_ADR_PAGE21); - encoder.Builder.EmitUInt(0x90000000); - - // ldr x2, [x0, #:tlsdesc_lo12:tlsRoot] - encoder.Builder.EmitReloc(tlsRoot, RelocType.IMAGE_REL_AARCH64_TLSDESC_LD64_LO12); - encoder.Builder.EmitUInt(0xf9400002); - - // add x0, x0, :tlsdesc_lo12:tlsRoot - encoder.Builder.EmitReloc(tlsRoot, RelocType.IMAGE_REL_AARCH64_TLSDESC_ADD_LO12); - encoder.Builder.EmitUInt(0x91000000); - - // blr :tlsdesc_call:tlsRoot:x2 - encoder.Builder.EmitReloc(tlsRoot, RelocType.IMAGE_REL_AARCH64_TLSDESC_CALL); - encoder.Builder.EmitUInt(0xd63f0040); - - // add x1, x1, x0 - encoder.Builder.EmitUInt(0x8b000021); - - // ldp x29, x30, [sp], 16 - encoder.Builder.EmitUInt(0xa8c17bfd); - } - - encoder.EmitLDR(Register.X0, Register.X1); - - // here we have: - // X1: addr, X0: storage - // if the storage is already allocated, just return, otherwise do slow path. - - encoder.EmitCMP(Register.X0, 0); - encoder.EmitRETIfNotEqual(); - encoder.EmitMOV(Register.X0, Register.X1); - encoder.EmitJMP(getInlinedThreadStaticBaseSlow); - } - else - { - throw new NotImplementedException(); - } - } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs index 66265e9d71bb49..ab20cf1c892fa3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs @@ -74,26 +74,7 @@ protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bo ISortableSymbolNode index = factory.TypeThreadStaticIndex(target); if (index is TypeThreadStaticIndexNode ti && ti.IsInlined) { - if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) - { - EmitInlineTLSAccess(factory, ref encoder); - } - else - { - // First arg: unused address of the TypeManager - // encoder.EmitMOV(encoder.TargetRegister.Arg0, 0); - - // Second arg: -1 (index of inlined storage) - encoder.EmitMOV(encoder.TargetRegister.Arg1, -1); - - encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); - - AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg2, null, 0, 0, AddrModeSize.Int64); - encoder.EmitCMP(ref initialized, 0); - encoder.EmitJNE(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); - - EmitInlineTLSAccess(factory, ref encoder); - } + throw new NotImplementedException(); } else { @@ -225,16 +206,5 @@ protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bo throw new NotImplementedException(); } } - - // emits code that results in ThreadStaticBase referenced in RAX. - // may trash volatile registers. (there are calls to the slow helper and possibly to platform's TLS support) - private static void EmitInlineTLSAccess(NodeFactory factory, ref X64Emitter encoder) - { - // For factory.Target.IsApplePlatform - // movq _\Var @TLVP(% rip), % rdi - // callq * (% rdi) - - throw new NotImplementedException(); - } } } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 14676fd8fe917f..4495e0322d4a78 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -2177,7 +2177,10 @@ private void getFieldInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_MET } else if (field.IsThreadStatic) { - if ((MethodBeingCompiled.Context.Target.IsWindows || MethodBeingCompiled.Context.Target.OperatingSystem == TargetOS.Linux) && MethodBeingCompiled.Context.Target.Architecture == TargetArchitecture.X64) + var target = MethodBeingCompiled.Context.Target; + if ((target.IsWindows && target.Architecture is TargetArchitecture.X64) || + ((target.OperatingSystem == TargetOS.Linux) && + (target.Architecture is TargetArchitecture.X64 or TargetArchitecture.ARM64))) { ISortableSymbolNode index = _compilation.NodeFactory.TypeThreadStaticIndex((MetadataType)field.OwningType); if (index is TypeThreadStaticIndexNode ti)