Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NativeAOT] Inline TLS access for linux/arm64 #97910

Merged
merged 24 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/inc/jiteeversionguid.h
Original file line number Diff line number Diff line change
Expand Up @@ -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}
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
46 changes: 46 additions & 0 deletions src/coreclr/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down
105 changes: 96 additions & 9 deletions src/coreclr/jit/emitarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,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;

Expand Down Expand Up @@ -8521,7 +8521,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

Expand Down Expand Up @@ -8628,7 +8628,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);
}
Expand Down Expand Up @@ -13000,6 +13004,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,
Expand Down Expand Up @@ -13513,8 +13572,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
{
Expand Down Expand Up @@ -16820,10 +16892,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)
Expand Down Expand Up @@ -16852,6 +16933,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)
Expand Down Expand Up @@ -17117,7 +17202,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
{
Expand Down Expand Up @@ -17160,7 +17246,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;

Expand Down Expand Up @@ -20119,7 +20206,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;
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/jit/emitarm64.h
Original file line number Diff line number Diff line change
Expand Up @@ -1316,6 +1316,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,
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
Loading
Loading