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

JIT: Check for call interference when placing PUTARG nodes #103301

Merged
merged 8 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
159 changes: 146 additions & 13 deletions src/coreclr/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1495,6 +1495,14 @@ bool Lowering::TryLowerSwitchToBitTest(FlowEdge* jumpTable[],
return true;
}

//------------------------------------------------------------------------
// ReplaceArgWithPutArgOrBitcast: Insert a PUTARG_* node in the right location
// and replace the call operand with that node.
//
// Arguments:
// argSlot - slot in call of argument
// putArgOrBitcast - the node that is being inserted
//
void Lowering::ReplaceArgWithPutArgOrBitcast(GenTree** argSlot, GenTree* putArgOrBitcast)
{
assert(argSlot != nullptr);
Expand Down Expand Up @@ -1981,6 +1989,113 @@ void Lowering::LowerArgsForCall(GenTreeCall* call)
{
LowerArg(call, &arg, true);
}

LegalizeArgPlacement(call);
}

//------------------------------------------------------------------------
// LegalizeArgPlacement: Move arg placement nodes (PUTARG_*) into a legal
// ordering after they have been created.
//
// Arguments:
// call - GenTreeCall node that has had PUTARG_* nodes created for arguments.
//
// Remarks:
// PUTARG_* nodes are created and inserted right after the definitions of the
// argument values. However, there are constraints on how the PUTARG nodes
// can appear:
//
// - No other GT_CALL nodes are allowed between a PUTARG_REG/PUTARG_SPLIT
// node and the call. For FEATURE_FIXED_OUT_ARGS this condition is also true
// for PUTARG_STK.
// - For !FEATURE_FIXED_OUT_ARGS, the PUTARG_STK nodes must come in push
// order.
//
// Morph has mostly already solved this problem, but transformations on LIR
// can make the ordering we end up with here illegal. This function legalizes
// the placement while trying to minimize the distance between an argument
// definition and its corresponding placement node.
//
void Lowering::LegalizeArgPlacement(GenTreeCall* call)
{
size_t numMarked = MarkCallPutArgNodes(call);

// We currently do not try to resort the PUTARG_STK nodes, but rather just
// assert here that they are ordered.
#if defined(DEBUG) && !FEATURE_FIXED_OUT_ARGS
unsigned nextPushOffset = UINT_MAX;
#endif

GenTree* cur = call->gtPrev;
while (numMarked > 0)
{
assert(cur != nullptr);

if ((cur->gtLIRFlags & LIR::Flags::Mark) != 0)
{
numMarked--;
cur->gtLIRFlags &= ~LIR::Flags::Mark;

#if defined(DEBUG) && !FEATURE_FIXED_OUT_ARGS
if (cur->OperIs(GT_PUTARG_STK))
{
// For !FEATURE_FIXED_OUT_ARGS (only x86) byte offsets are
// subtracted from the top of the stack frame; so last pushed
// arg has highest offset.
assert(nextPushOffset > cur->AsPutArgStk()->getArgOffset());
nextPushOffset = cur->AsPutArgStk()->getArgOffset();
}
#endif
}

if (cur->IsCall())
{
break;
}

cur = cur->gtPrev;
}

if (numMarked == 0)
{
// Already legal; common case
return;
}

JITDUMP("Call [%06u] has %zu PUTARG nodes that interfere with [%06u]; will move them after it\n",
Compiler::dspTreeID(call), numMarked, Compiler::dspTreeID(cur));

// We found interference; remaining PUTARG nodes need to be moved after
// this point.
GenTree* insertionPoint = cur;

while (numMarked > 0)
{
assert(cur != nullptr);

GenTree* prev = cur->gtPrev;
if ((cur->gtLIRFlags & LIR::Flags::Mark) != 0)
{
numMarked--;
cur->gtLIRFlags &= ~LIR::Flags::Mark;

// For FEATURE_FIXED_OUT_ARGS: all PUTARG nodes must be moved after the interfering call
// For !FEATURE_FIXED_OUT_ARGS: only PUTARG_REG nodes must be moved after the interfering call
if (FEATURE_FIXED_OUT_ARGS || cur->OperIs(GT_PUTARG_REG))
{
JITDUMP("Relocating [%06u] after [%06u]\n", Compiler::dspTreeID(cur),
Compiler::dspTreeID(insertionPoint));

BlockRange().Remove(cur);
BlockRange().InsertAfter(insertionPoint, cur);
}
}

cur = prev;
}

JITDUMP("Final result after legalization:\n");
DISPTREERANGE(BlockRange(), call);
}

// helper that create a node representing a relocatable physical address computation
Expand All @@ -1997,6 +2112,7 @@ GenTree* Lowering::AddrGen(void* addr)
return AddrGen((ssize_t)addr);
}

//------------------------------------------------------------------------
// LowerCallMemset: Replaces the following memset-like special intrinsics:
//
// SpanHelpers.Fill<T>(ref dstRef, CNS_SIZE, CNS_VALUE)
Expand Down Expand Up @@ -2780,19 +2896,7 @@ void Lowering::InsertProfTailCallHook(GenTreeCall* call, GenTree* insertionPoint
//
GenTree* Lowering::FindEarliestPutArg(GenTreeCall* call)
{
size_t numMarkedNodes = 0;
for (CallArg& arg : call->gtArgs.Args())
{
if (arg.GetEarlyNode() != nullptr)
{
numMarkedNodes += MarkPutArgNodes(arg.GetEarlyNode());
}

if (arg.GetLateNode() != nullptr)
{
numMarkedNodes += MarkPutArgNodes(arg.GetLateNode());
}
}
size_t numMarkedNodes = MarkCallPutArgNodes(call);

if (numMarkedNodes <= 0)
{
Expand All @@ -2817,6 +2921,35 @@ GenTree* Lowering::FindEarliestPutArg(GenTreeCall* call)
return node;
}

//------------------------------------------------------------------------
// MarkCallPutArgNodes: Mark all PUTARG_* operand nodes corresponding to a
// call.
//
// Arguments:
// call - the call
//
// Returns:
// The number of nodes marked.
//
size_t Lowering::MarkCallPutArgNodes(GenTreeCall* call)
{
size_t numMarkedNodes = 0;
for (CallArg& arg : call->gtArgs.Args())
{
if (arg.GetEarlyNode() != nullptr)
{
numMarkedNodes += MarkPutArgNodes(arg.GetEarlyNode());
}

if (arg.GetLateNode() != nullptr)
{
numMarkedNodes += MarkPutArgNodes(arg.GetLateNode());
}
}

return numMarkedNodes;
}

//------------------------------------------------------------------------
// MarkPutArgNodes: Mark all direct operand PUTARG nodes with a LIR mark.
//
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/lower.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ class Lowering final : public Phase
GenTreeCall* callNode);
void InsertProfTailCallHook(GenTreeCall* callNode, GenTree* insertionPoint);
GenTree* FindEarliestPutArg(GenTreeCall* call);
size_t MarkCallPutArgNodes(GenTreeCall* call);
size_t MarkPutArgNodes(GenTree* node);
GenTree* LowerVirtualVtableCall(GenTreeCall* call);
GenTree* LowerVirtualStubCall(GenTreeCall* call);
Expand All @@ -186,6 +187,7 @@ class Lowering final : public Phase
GenTree* LowerFloatArg(GenTree** pArg, CallArg* callArg);
GenTree* LowerFloatArgReg(GenTree* arg, regNumber regNum);
#endif
void LegalizeArgPlacement(GenTreeCall* call);

void InsertPInvokeCallProlog(GenTreeCall* call);
void InsertPInvokeCallEpilog(GenTreeCall* call);
Expand Down
Loading