Skip to content

Commit 694067c

Browse files
authored
Remove stores as operands of calls in LIR (#68460)
These are part of operands in HIR simply for ordering purposes, which is no longer necessary in LIR. This means that every argument operand in LIR now has exactly one node, which between rationalization and lowering is always a value. We still have cases of operands that are non-values, in particular the operand of GT_JTRUE nodes and also GT_PUTARG_STK nodes after lowering. However, at least for calls the LIR invariant is now simpler: before lowering all argument operands are values, and after all argument operands are GT_PUTARG_* nodes. Not having stores as operands will also allow us to get rid of GTF_LATE_ARG in a follow-up change.
1 parent 1351ac8 commit 694067c

File tree

5 files changed

+35
-46
lines changed

5 files changed

+35
-46
lines changed

src/coreclr/jit/gentree.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17867,7 +17867,7 @@ bool Compiler::gtIsStaticGCBaseHelperCall(GenTree* tree)
1786717867

1786817868
//------------------------------------------------------------------------
1786917869
// gtCallGetDefinedRetBufLclAddr:
17870-
// Get the tree corresponding to the address of the retbuf taht this call defines.
17870+
// Get the tree corresponding to the address of the retbuf that this call defines.
1787117871
//
1787217872
// Parameters:
1787317873
// call - The call node

src/coreclr/jit/lir.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,10 +1557,8 @@ bool LIR::Range::CheckLIR(Compiler* compiler, bool checkUnusedValues) const
15571557
// Stack arguments do not produce a value, but they are considered children of the call.
15581558
// It may be useful to remove these from being call operands, but that may also impact
15591559
// other code that relies on being able to reach all the operands from a call node.
1560-
// The GT_NOP case is because sometimes we eliminate stack argument stores as dead, but
1561-
// instead of removing them we replace with a NOP.
15621560
// The argument of a JTRUE doesn't produce a value (just sets a flag).
1563-
assert(((node->OperGet() == GT_CALL) && (def->OperIsStore() || def->OperIs(GT_PUTARG_STK, GT_NOP))) ||
1561+
assert(((node->OperGet() == GT_CALL) && def->OperIs(GT_PUTARG_STK)) ||
15641562
((node->OperGet() == GT_JTRUE) && (def->TypeGet() == TYP_VOID) &&
15651563
((def->gtFlags & GTF_SET_FLAGS) != 0)));
15661564
continue;

src/coreclr/jit/liveness.cpp

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2205,35 +2205,17 @@ bool Compiler::fgTryRemoveNonLocal(GenTree* node, LIR::Range* blockRange)
22052205
}
22062206

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

2220-
// If the store is marked as a late argument, it is referenced by a call.
2221-
// Instead of removing it, bash it to a NOP.
2222-
if ((store->gtFlags & GTF_LATE_ARG) != 0)
2223-
{
2224-
JITDUMP("node is a late arg; replacing with NOP\n");
2225-
store->gtBashToNOP();
2226-
2227-
// NOTE: this is a bit of a hack. We need to keep these nodes around as they are
2228-
// referenced by the call, but they're considered side-effect-free non-value-producing
2229-
// nodes, so they will be removed if we don't do this.
2230-
store->gtFlags |= GTF_ORDER_SIDEEFF;
2231-
}
2232-
else
2233-
{
2234-
blockRange.Remove(store);
2235-
}
2236-
2217+
assert((store->gtFlags & GTF_LATE_ARG) == 0);
2218+
blockRange.Remove(store);
22372219
assert(!opts.MinOpts());
22382220
fgStmtRemoved = true;
22392221
}

src/coreclr/jit/lower.cpp

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,20 +1343,7 @@ void Lowering::LowerArg(GenTreeCall* call, CallArg* callArg, bool late)
13431343

13441344
JITDUMP("lowering arg : ");
13451345
DISPNODE(arg);
1346-
1347-
// No assignments should remain by Lowering.
1348-
assert(!arg->OperIs(GT_ASG));
1349-
assert(!arg->OperIsPutArgStk());
1350-
1351-
// Assignments/stores at this level are not really placing an argument.
1352-
// They are setting up temporary locals that will later be placed into
1353-
// outgoing regs or stack.
1354-
// Note that atomic ops may be stores and still produce a value.
1355-
if (!arg->IsValue())
1356-
{
1357-
assert((arg->OperIsStore() && !arg->IsValue()) || arg->IsNothingNode() || arg->OperIsCopyBlkOp());
1358-
return;
1359-
}
1346+
assert(arg->IsValue());
13601347

13611348
var_types type = arg->TypeGet();
13621349

@@ -6425,7 +6412,7 @@ void Lowering::CheckCallArg(GenTree* arg)
64256412
{
64266413
if (!arg->IsValue() && !arg->OperIsPutArgStk())
64276414
{
6428-
assert(arg->OperIsStore() || arg->IsNothingNode() || arg->OperIsCopyBlkOp());
6415+
assert(arg->OperIsStore() || arg->OperIsCopyBlkOp());
64296416
return;
64306417
}
64316418

src/coreclr/jit/rationalize.cpp

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,28 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge
615615
RewriteIndir(use);
616616
break;
617617

618+
case GT_CALL:
619+
// In linear order we no longer need to retain the stores in early
620+
// args as these have now been sequenced.
621+
for (CallArg& arg : node->AsCall()->gtArgs.EarlyArgs())
622+
{
623+
if (!arg.GetEarlyNode()->IsValue())
624+
{
625+
// GTF_LATE_ARG is no longer meaningful.
626+
arg.GetEarlyNode()->gtFlags &= ~GTF_LATE_ARG;
627+
arg.SetEarlyNode(nullptr);
628+
}
629+
}
630+
631+
#ifdef DEBUG
632+
// The above means that all argument nodes are now true arguments.
633+
for (CallArg& arg : node->AsCall()->gtArgs.Args())
634+
{
635+
assert((arg.GetEarlyNode() == nullptr) != (arg.GetLateNode() == nullptr));
636+
}
637+
#endif
638+
break;
639+
618640
case GT_NOP:
619641
// fgMorph sometimes inserts NOP nodes between defs and uses
620642
// supposedly 'to prevent constant folding'. In this case, remove the
@@ -637,8 +659,8 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge
637659
if ((sideEffects & GTF_ALL_EFFECT) == 0)
638660
{
639661
// The LHS has no side effects. Remove it.
640-
// None of the transforms performed herein violate tree order, so isClosed
641-
// should always be true.
662+
// All transformations on pure trees keep their operands in LIR
663+
// and should not violate tree order.
642664
assert(isClosed);
643665

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

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

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

0 commit comments

Comments
 (0)