Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -1097,7 +1097,7 @@ class CodeGen final : public CodeGenInterface
#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
void genConsumeMultiOpOperands(GenTreeMultiOp* tree);
#endif
void genEmitGSCookieCheck(bool pushReg);
void genEmitGSCookieCheck(bool tailCall);
void genCodeForShift(GenTree* tree);

#if defined(TARGET_X86) || defined(TARGET_ARM)
Expand Down
18 changes: 11 additions & 7 deletions src/coreclr/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,10 @@ void CodeGen::genSetGSSecurityCookie(regNumber initReg, bool* pInitRegZeroed)
// genEmitGSCookieCheck: Generate code to check that the GS cookie
// wasn't thrashed by a buffer overrun.
//
void CodeGen::genEmitGSCookieCheck(bool pushReg)
// Parameters:
// tailCall - Whether or not this is being emitted for a tail call
//
void CodeGen::genEmitGSCookieCheck(bool tailCall)
{
noway_assert(compiler->gsGlobalSecurityCookieAddr || compiler->gsGlobalSecurityCookieVal);

Expand All @@ -623,8 +626,9 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
// We don't have any IR node representing this check, so LSRA can't communicate registers
// for us to use.

regNumber regGSConst = REG_GSCOOKIE_TMP_0;
regNumber regGSValue = REG_GSCOOKIE_TMP_1;
regMaskTP tmpRegs = genGetGSCookieTempRegs(tailCall);
regNumber regGSConst = genFirstRegNumFromMaskAndToggle(tmpRegs);
regNumber regGSValue = genFirstRegNumFromMaskAndToggle(tmpRegs);

if (compiler->gsGlobalSecurityCookieAddr == nullptr)
{
Expand Down Expand Up @@ -3266,12 +3270,12 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
{
regMaskTP trashedByEpilog = RBM_CALLEE_SAVED;

// The epilog may use and trash REG_GSCOOKIE_TMP_0/1. Make sure we have no
// non-standard args that may be trash if this is a tailcall.
// The epilog may use and trash some registers for the GS cookie check.
// Make sure we have no non-standard args that may be trash if this is
// a tailcall.
if (compiler->getNeedsGSSecurityCookie())
{
trashedByEpilog |= genRegMask(REG_GSCOOKIE_TMP_0);
trashedByEpilog |= genRegMask(REG_GSCOOKIE_TMP_1);
trashedByEpilog |= genGetGSCookieTempRegs(/* tailCall */ true);
}

for (CallArg& arg : call->gtArgs.Args())
Expand Down
48 changes: 43 additions & 5 deletions src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1552,14 +1552,11 @@ void CodeGen::genExitCode(BasicBlock* block)
Note that this may result in a duplicate IPmapping entry, and
that this is ok */

// For non-optimized debuggable code, there is only one epilog.
genIPmappingAdd(IPmappingDscKind::Epilog, DebugInfo(), true);

bool jmpEpilog = block->HasFlag(BBF_HAS_JMP);

#ifdef DEBUG
// For returnining epilogs do some validation that the GC info looks right.
if (!jmpEpilog)
if (!block->HasFlag(BBF_HAS_JMP))
{
if (compiler->compMethodReturnsRetBufAddr())
{
Expand All @@ -1583,7 +1580,7 @@ void CodeGen::genExitCode(BasicBlock* block)

if (compiler->getNeedsGSSecurityCookie())
{
genEmitGSCookieCheck(jmpEpilog);
genEmitGSCookieCheck(block->HasFlag(BBF_HAS_JMP));
}

genReserveEpilog(block);
Expand Down Expand Up @@ -2508,6 +2505,47 @@ CorInfoHelpFunc CodeGenInterface::genWriteBarrierHelperForWriteBarrierForm(GCInf
}
}

// -----------------------------------------------------------------------------
// genGetGSCookieTempRegs:
// Get a mask of registers to use for the GS cookie check generated in a
// block.
//
// Parameters:
// tailCall - Whether the block is a tailcall
//
// Returns:
// Mask of all the registers that can be used. Some targets may need more
// than one register.
//
regMaskTP CodeGenInterface::genGetGSCookieTempRegs(bool tailCall)
{
#ifdef TARGET_XARCH
if (tailCall)
{
// If we are tailcalling then return registers are available for use
return RBM_RAX;
}

#ifdef TARGET_AMD64
// Otherwise on x64 (win-x64 and SysV) r8 is never used for return values
return RBM_R8;
#else
// On x86 it's more difficult: we have only eax, ecx and edx available as volatile
// registers, and all of them may be used for return values (longs + async continuation).
if (compiler->compIsAsync())
{
// Just use a callee save for this rare async + gs cookie check case.
return RBM_ESI;
}

// Outside async ecx is not used for returns.
return RBM_ECX;
#endif
#else
return RBM_GSCOOKIE_TMP;
#endif
}

//----------------------------------------------------------------------
// genGCWriteBarrier: Generate a write barrier for a node.
//
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/codegeninterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ class CodeGenInterface
bool genWriteBarrierUsed;
#endif

regMaskTP genGetGSCookieTempRegs(bool tailCall);

// The following property indicates whether the current method sets up
// an explicit stack frame or not.
private:
Expand Down
15 changes: 9 additions & 6 deletions src/coreclr/jit/codegenloongarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4464,7 +4464,10 @@ void CodeGen::genSetGSSecurityCookie(regNumber initReg, bool* pInitRegZeroed)
// genEmitGSCookieCheck: Generate code to check that the GS cookie
// wasn't thrashed by a buffer overrun.
//
void CodeGen::genEmitGSCookieCheck(bool pushReg)
// Parameters:
// tailCall - Whether or not this is being emitted for a tail call
//
void CodeGen::genEmitGSCookieCheck(bool tailCall)
{
noway_assert(compiler->gsGlobalSecurityCookieAddr || compiler->gsGlobalSecurityCookieVal);

Expand All @@ -4474,8 +4477,9 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
// We don't have any IR node representing this check, so LSRA can't communicate registers
// for us to use.

regNumber regGSConst = REG_GSCOOKIE_TMP_0;
regNumber regGSValue = REG_GSCOOKIE_TMP_1;
regMaskTP tmpRegs = genGetGSCookieTempRegs(tailCall);
regNumber regGSConst = genFirstRegNumFromMaskAndToggle(tmpRegs);
regNumber regGSValue = genFirstRegNumFromMaskAndToggle(tmpRegs);

if (compiler->gsGlobalSecurityCookieAddr == nullptr)
{
Expand Down Expand Up @@ -5691,12 +5695,11 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
{
regMaskTP trashedByEpilog = RBM_CALLEE_SAVED;

// The epilog may use and trash REG_GSCOOKIE_TMP_0/1. Make sure we have no
// The epilog may use and trash REG_GSCOOKIE_TMP. Make sure we have no
// non-standard args that may be trash if this is a tailcall.
if (compiler->getNeedsGSSecurityCookie())
{
trashedByEpilog |= genRegMask(REG_GSCOOKIE_TMP_0);
trashedByEpilog |= genRegMask(REG_GSCOOKIE_TMP_1);
trashedByEpilog |= genGetGSCookieTempRegs(/* tailCall */ true);
}

for (CallArg& arg : call->gtArgs.Args())
Expand Down
18 changes: 11 additions & 7 deletions src/coreclr/jit/codegenriscv64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4473,7 +4473,10 @@ void CodeGen::genSetGSSecurityCookie(regNumber initReg, bool* pInitRegZeroed)
// genEmitGSCookieCheck: Generate code to check that the GS cookie
// wasn't thrashed by a buffer overrun.
//
void CodeGen::genEmitGSCookieCheck(bool pushReg)
// Parameters:
// tailCall - Whether or not this is being emitted for a tail call
//
void CodeGen::genEmitGSCookieCheck(bool tailCall)
{
noway_assert(compiler->gsGlobalSecurityCookieAddr || compiler->gsGlobalSecurityCookieVal);

Expand All @@ -4483,8 +4486,9 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
// We don't have any IR node representing this check, so LSRA can't communicate registers
// for us to use.

regNumber regGSConst = REG_GSCOOKIE_TMP_0;
regNumber regGSValue = REG_GSCOOKIE_TMP_1;
regMaskTP tmpRegs = genGetGSCookieTempRegs(tailCall);
regNumber regGSConst = genFirstRegNumFromMaskAndToggle(tmpRegs);
regNumber regGSValue = genFirstRegNumFromMaskAndToggle(tmpRegs);

if (compiler->gsGlobalSecurityCookieAddr == nullptr)
{
Expand Down Expand Up @@ -5748,12 +5752,12 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
{
regMaskTP trashedByEpilog = RBM_CALLEE_SAVED;

// The epilog may use and trash REG_GSCOOKIE_TMP_0/1. Make sure we have no
// non-standard args that may be trash if this is a tailcall.
// The epilog may use and trash some registers for the GS cookie check.
// Make sure we have no non-standard args that may be trash if this is
// a tailcall.
if (compiler->getNeedsGSSecurityCookie())
{
trashedByEpilog |= genRegMask(REG_GSCOOKIE_TMP_0);
trashedByEpilog |= genRegMask(REG_GSCOOKIE_TMP_1);
trashedByEpilog |= genGetGSCookieTempRegs(/* tailCall */ true);
}

for (CallArg& arg : call->gtArgs.Args())
Expand Down
98 changes: 11 additions & 87 deletions src/coreclr/jit/codegenxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,64 +84,20 @@ void CodeGen::genSetGSSecurityCookie(regNumber initReg, bool* pInitRegZeroed)
}
}

/*****************************************************************************
*
* Generate code to check that the GS cookie wasn't thrashed by a buffer
* overrun. If pushReg is true, preserve all registers around code sequence.
* Otherwise ECX could be modified.
*
* Implementation Note: pushReg = true, in case of tail calls.
*/
void CodeGen::genEmitGSCookieCheck(bool pushReg)
//---------------------------------------------------------------------
// genEmitGSCookieCheck:
// Emit the check that the GS cookie has its original value.
//
// Parameters:
// tailCall - Whether or not this is being emitted for a tail call
//
void CodeGen::genEmitGSCookieCheck(bool tailCall)
{
noway_assert(compiler->gsGlobalSecurityCookieAddr || compiler->gsGlobalSecurityCookieVal);

regNumber regGSCheck;
regMaskTP regMaskGSCheck = RBM_NONE;

if (!pushReg)
{
// Non-tail call: we can use any callee trash register that is not
// a return register or contain 'this' pointer (keep alive this), since
// we are generating GS cookie check after a GT_RETURN block.
// Note: On Amd64 System V RDX is an arg register - REG_ARG_2 - as well
// as return register for two-register-returned structs.
#ifdef TARGET_X86
// Note: ARG_0 can be REG_ASYNC_CONTINUATION_RET
// we will check for that later if we end up saving/restoring this.
regGSCheck = REG_ARG_0;
regNumber regGSCheckAlternative = REG_ARG_1;
#else
// these cannot be a part of any kind of return
regGSCheck = REG_R8;
regNumber regGSCheckAlternative = REG_R9;
#endif

if (compiler->lvaKeepAliveAndReportThis() && compiler->lvaGetDesc(compiler->info.compThisArg)->lvIsInReg() &&
(compiler->lvaGetDesc(compiler->info.compThisArg)->GetRegNum() == regGSCheck))
{
regGSCheck = regGSCheckAlternative;
}
}
else
{
#ifdef TARGET_X86
// It doesn't matter which register we pick, since we're going to save and restore it
// around the check.
// TODO-CQ: Can we optimize the choice of register to avoid doing the push/pop sometimes?
regGSCheck = REG_EAX;
regMaskGSCheck = RBM_EAX;
#else // !TARGET_X86
// Jmp calls: specify method handle using which JIT queries VM for its entry point
// address and hence it can neither be a VSD call nor PInvoke calli with cookie
// parameter. Therefore, in case of jmp calls it is safe to use R11.
regGSCheck = REG_R11;
#endif // !TARGET_X86
}

regMaskTP byrefPushedRegs = RBM_NONE;
regMaskTP norefPushedRegs = RBM_NONE;
regMaskTP pushedRegs = RBM_NONE;
regMaskTP tempRegs = genGetGSCookieTempRegs(tailCall);
assert(tempRegs != RBM_NONE);
regNumber regGSCheck = genFirstRegNumFromMask(tempRegs);

if (compiler->gsGlobalSecurityCookieAddr == nullptr)
{
Expand All @@ -163,18 +119,6 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
}
else
{
// AOT case - GS cookie value needs to be accessed through an indirection.

// if we use the continuation reg, the pop/push requires no-GC
// this can happen only when AOT supports async on x86
if (compiler->compIsAsync() && (regGSCheck == REG_ASYNC_CONTINUATION_RET))
{
regMaskGSCheck = RBM_ASYNC_CONTINUATION_RET;
GetEmitter()->emitDisableGC();
}

pushedRegs = genPushRegs(regMaskGSCheck, &byrefPushedRegs, &norefPushedRegs);

instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, regGSCheck, (ssize_t)compiler->gsGlobalSecurityCookieAddr);
GetEmitter()->emitIns_R_AR(ins_Load(TYP_I_IMPL), EA_PTRSIZE, regGSCheck, regGSCheck, 0);
GetEmitter()->emitIns_S_R(INS_cmp, EA_PTRSIZE, regGSCheck, compiler->lvaGSSecurityCookie, 0);
Expand All @@ -184,8 +128,6 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg)
inst_JMP(EJ_je, gsCheckBlk);
genEmitHelperCall(CORINFO_HELP_FAIL_FAST, 0, EA_UNKNOWN);
genDefineTempLabel(gsCheckBlk);

genPopRegs(pushedRegs, byrefPushedRegs, norefPushedRegs);
}

BasicBlock* CodeGen::genCallFinally(BasicBlock* block)
Expand Down Expand Up @@ -6043,24 +5985,6 @@ void CodeGen::genCall(GenTreeCall* call)
// all virtuals should have been expanded into a control expression
assert(!call->IsVirtual() || call->gtControlExpr || call->gtCallAddr);

// Insert a GS check if necessary
if (call->IsTailCallViaJitHelper())
{
if (compiler->getNeedsGSSecurityCookie())
{
#if FEATURE_FIXED_OUT_ARGS
// If either of the conditions below is true, we will need a temporary register in order to perform the GS
// cookie check. When FEATURE_FIXED_OUT_ARGS is disabled, we save and restore the temporary register using
// push/pop. When FEATURE_FIXED_OUT_ARGS is enabled, however, we need an alternative solution. For now,
// though, the tail prefix is ignored on all platforms that use fixed out args, so we should never hit this
// case.
assert(compiler->gsGlobalSecurityCookieAddr == nullptr);
assert((int)compiler->gsGlobalSecurityCookieVal == (ssize_t)compiler->gsGlobalSecurityCookieVal);
#endif
genEmitGSCookieCheck(true);
}
}

genCallPlaceRegArgs(call);

#if defined(TARGET_X86)
Expand Down
4 changes: 0 additions & 4 deletions src/coreclr/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3177,10 +3177,6 @@ void Lowering::LowerFastTailCall(GenTreeCall* call)
assert(!call->IsUnmanaged()); // tail calls to unamanaged methods
assert(!comp->compLocallocUsed); // tail call from methods that also do localloc

#ifdef TARGET_AMD64
assert(!comp->getNeedsGSSecurityCookie()); // jit64 compat: tail calls from methods that need GS check
#endif // TARGET_AMD64

// We expect to see a call that meets the following conditions
assert(call->IsFastTailCall());

Expand Down
3 changes: 1 addition & 2 deletions src/coreclr/jit/lsraarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,7 @@ int LinearScan::BuildCall(GenTreeCall* call)
ctrlExprCandidates = allRegs(TYP_INT) & RBM_INT_CALLEE_TRASH.GetIntRegSet() & ~SRBM_LR;
if (compiler->getNeedsGSSecurityCookie())
{
ctrlExprCandidates &=
~(genSingleTypeRegMask(REG_GSCOOKIE_TMP_0) | genSingleTypeRegMask(REG_GSCOOKIE_TMP_1));
ctrlExprCandidates &= ~compiler->codeGen->genGetGSCookieTempRegs(/* tailCall */ true).GetIntRegSet();
}
assert(ctrlExprCandidates != RBM_NONE);
}
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/jit/lsrabuild.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2535,6 +2535,16 @@ void LinearScan::buildIntervals()
currentLoc += 2;
}

if (compiler->getNeedsGSSecurityCookie() && block->KindIs(BBJ_RETURN))
{
// The cookie check will kill some registers that it is using.
// Model this to ensure values that are kept live throughout the
// method are properly made available.
bool isTailCall = block->HasFlag(BBF_HAS_JMP);
addKillForRegs(compiler->codeGen->genGetGSCookieTempRegs(isTailCall), currentLoc + 1);
currentLoc += 2;
}

if (localVarsEnregistered)
{
#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
Expand Down
6 changes: 2 additions & 4 deletions src/coreclr/jit/lsraloongarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -726,8 +726,7 @@ int LinearScan::BuildCall(GenTreeCall* call)
ctrlExprCandidates = allRegs(TYP_INT) & RBM_INT_CALLEE_TRASH.GetIntRegSet();
if (compiler->getNeedsGSSecurityCookie())
{
ctrlExprCandidates &=
~(genSingleTypeRegMask(REG_GSCOOKIE_TMP_0) | genSingleTypeRegMask(REG_GSCOOKIE_TMP_1));
ctrlExprCandidates &= ~compiler->codeGen->genGetGSCookieTempRegs(/* tailCall */ true).GetIntRegSet();
}
assert(ctrlExprCandidates != RBM_NONE);
}
Expand All @@ -742,8 +741,7 @@ int LinearScan::BuildCall(GenTreeCall* call)
candidates = allRegs(TYP_INT) & RBM_INT_CALLEE_TRASH.GetIntRegSet();
if (compiler->getNeedsGSSecurityCookie())
{
ctrlExprCandidates &=
~(genSingleTypeRegMask(REG_GSCOOKIE_TMP_0) | genSingleTypeRegMask(REG_GSCOOKIE_TMP_1));
ctrlExprCandidates &= ~compiler->codeGen->genGetGSCookieTempRegs(/* tailCall */ true).GetIntRegSet();
}
assert(candidates != RBM_NONE);
}
Expand Down
Loading
Loading