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

Remove stores as operands of calls in LIR #68460

Merged
merged 4 commits into from
Apr 27, 2022
Merged
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/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17866,7 +17866,7 @@ bool Compiler::gtIsStaticGCBaseHelperCall(GenTree* tree)

//------------------------------------------------------------------------
// gtCallGetDefinedRetBufLclAddr:
// Get the tree corresponding to the address of the retbuf taht this call defines.
// Get the tree corresponding to the address of the retbuf that this call defines.
//
// Parameters:
// call - The call node
Expand Down
4 changes: 1 addition & 3 deletions src/coreclr/jit/lir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1557,10 +1557,8 @@ bool LIR::Range::CheckLIR(Compiler* compiler, bool checkUnusedValues) const
// Stack arguments do not produce a value, but they are considered children of the call.
// It may be useful to remove these from being call operands, but that may also impact
// other code that relies on being able to reach all the operands from a call node.
// The GT_NOP case is because sometimes we eliminate stack argument stores as dead, but
// instead of removing them we replace with a NOP.
// The argument of a JTRUE doesn't produce a value (just sets a flag).
assert(((node->OperGet() == GT_CALL) && (def->OperIsStore() || def->OperIs(GT_PUTARG_STK, GT_NOP))) ||
assert(((node->OperGet() == GT_CALL) && def->OperIs(GT_PUTARG_STK)) ||
((node->OperGet() == GT_JTRUE) && (def->TypeGet() == TYP_VOID) &&
((def->gtFlags & GTF_SET_FLAGS) != 0)));
continue;
Expand Down
28 changes: 5 additions & 23 deletions src/coreclr/jit/liveness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2205,35 +2205,17 @@ bool Compiler::fgTryRemoveNonLocal(GenTree* node, LIR::Range* blockRange)
}

//---------------------------------------------------------------------
// fgRemoveDeadStoreSimple - remove a dead store
// fgRemoveDeadStoreLIR - remove a dead store from LIR
//
// pTree - GenTree** to local, including store-form local or local addr (post-rationalize)
// varDsc - var that is being stored to
// life - current live tracked vars (maintained as we walk backwards)
// doAgain - out parameter, true if we should restart the statement
// pStmtInfoDirty - should defer the cost computation to the point after the reverse walk is completed?
// store - A store tree
// block - Block that the store is part of
//
void Compiler::fgRemoveDeadStoreLIR(GenTree* store, BasicBlock* block)
{
LIR::Range& blockRange = LIR::AsRange(block);

// If the store is marked as a late argument, it is referenced by a call.
// Instead of removing it, bash it to a NOP.
if ((store->gtFlags & GTF_LATE_ARG) != 0)
Copy link
Member Author

@jakobbotsch jakobbotsch Apr 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a cursory glance this use of GTF_LATE_ARG is (was) the only blocker of its removal.

{
JITDUMP("node is a late arg; replacing with NOP\n");
store->gtBashToNOP();

// NOTE: this is a bit of a hack. We need to keep these nodes around as they are
// referenced by the call, but they're considered side-effect-free non-value-producing
// nodes, so they will be removed if we don't do this.
store->gtFlags |= GTF_ORDER_SIDEEFF;
}
else
{
blockRange.Remove(store);
}

assert((store->gtFlags & GTF_LATE_ARG) == 0);
blockRange.Remove(store);
assert(!opts.MinOpts());
fgStmtRemoved = true;
}
Expand Down
17 changes: 2 additions & 15 deletions src/coreclr/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1343,20 +1343,7 @@ void Lowering::LowerArg(GenTreeCall* call, CallArg* callArg, bool late)

JITDUMP("lowering arg : ");
DISPNODE(arg);

// No assignments should remain by Lowering.
assert(!arg->OperIs(GT_ASG));
assert(!arg->OperIsPutArgStk());

// Assignments/stores at this level are not really placing an argument.
// They are setting up temporary locals that will later be placed into
// outgoing regs or stack.
// Note that atomic ops may be stores and still produce a value.
if (!arg->IsValue())
{
assert((arg->OperIsStore() && !arg->IsValue()) || arg->IsNothingNode() || arg->OperIsCopyBlkOp());
return;
}
assert(arg->IsValue());

var_types type = arg->TypeGet();

Expand Down Expand Up @@ -6425,7 +6412,7 @@ void Lowering::CheckCallArg(GenTree* arg)
{
if (!arg->IsValue() && !arg->OperIsPutArgStk())
{
assert(arg->OperIsStore() || arg->IsNothingNode() || arg->OperIsCopyBlkOp());
assert(arg->OperIsStore() || arg->OperIsCopyBlkOp());
return;
}

Expand Down
30 changes: 26 additions & 4 deletions src/coreclr/jit/rationalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,28 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge
RewriteIndir(use);
break;

case GT_CALL:
// In linear order we no longer need to retain the stores in early
// args as these have now been sequenced.
for (CallArg& arg : node->AsCall()->gtArgs.EarlyArgs())
{
if (!arg.GetEarlyNode()->IsValue())
{
// GTF_LATE_ARG is no longer meaningful.
arg.GetEarlyNode()->gtFlags &= ~GTF_LATE_ARG;
arg.SetEarlyNode(nullptr);
Comment on lines +626 to +627
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GTF_CALL_M_RETBUFFARG_LCLOPT has a dependency on being able to reach the setup node from the call.

Currently it will work ok because liveness will count the LCL_VAR|FLD_ADDR as the definition point, but that itself is only fine because GTF_CALL_M_RETBUFFARG_LCLOPT is tied to lvIsTemp.

Would be curious to know your thinking on this; my idea was to forbid forcing the buffer arguments to temps.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised it would ever be evaluated to a temp since it should be invariant. But yes, we should avoid creating a temp for it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised it would ever be evaluated to a temp since it should be invariant

Agree, it is mostly a "model" concern, without GTF_CALL_M_RETBUFFARG_LCLOPT forcing things to temps is always legal, with it (+ this change), it isn't.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opened #68469 to stop it looking into the stores and to assert the contract more heavily.

}
}

#ifdef DEBUG
// The above means that all argument nodes are now true arguments.
for (CallArg& arg : node->AsCall()->gtArgs.Args())
{
assert((arg.GetEarlyNode() == nullptr) != (arg.GetLateNode() == nullptr));
}
#endif
break;

case GT_NOP:
// fgMorph sometimes inserts NOP nodes between defs and uses
// supposedly 'to prevent constant folding'. In this case, remove the
Expand All @@ -637,8 +659,8 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge
if ((sideEffects & GTF_ALL_EFFECT) == 0)
{
// The LHS has no side effects. Remove it.
// None of the transforms performed herein violate tree order, so isClosed
// should always be true.
// All transformations on pure trees keep their operands in LIR
// and should not violate tree order.
assert(isClosed);

BlockRange().Delete(comp, m_block, std::move(lhsRange));
Expand Down Expand Up @@ -666,8 +688,8 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge

if ((sideEffects & GTF_ALL_EFFECT) == 0)
{
// None of the transforms performed herein violate tree order, so isClosed
// should always be true.
// All transformations on pure trees keep their operands in
// LIR and should not violate tree order.
assert(isClosed);

BlockRange().Delete(comp, m_block, std::move(rhsRange));
Expand Down