Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
78 changes: 78 additions & 0 deletions src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3177,6 +3177,84 @@ GenTree* Compiler::optVNConstantPropOnTree(BasicBlock* block, GenTree* tree)

if (conValTree != nullptr)
{
if (tree->OperIs(GT_LCL_VAR) && conValTree->OperIs(GT_CNS_VEC, GT_CNS_DBL, GT_CNS_INT))
{
bool keepPropagating = false;

if (conValTree->OperIs(GT_CNS_VEC))
{
if (conValTree->IsVectorZero() || conValTree->IsVectorAllBitsSet())
{
keepPropagating = true;
}
}
else if (conValTree->OperIs(GT_CNS_DBL))
{
if (conValTree->IsFloatPositiveZero())
{
keepPropagating = true;
}
#ifdef TARGET_ARM64
if (emitter::canEncodeFloatImm8(conValTree->AsDblCon()->gtDconVal))
{
// This float won't be expensive for the loop
keepPropagating = true;
}
#endif
}
else
{
assert(conValTree->OperIs(GT_CNS_INT));
#ifdef TARGET_ARM64
if ((unsigned_abs(conValTree->AsIntCon()->IconValue()) <= 0xFFF) ||
isPow2(conValTree->AsIntCon()->IconValue()))
{
keepPropagating = true;
}
#endif
}

if (!keepPropagating)
{
// Try to find the block this constant was originally defined in
const unsigned lclNum = tree->AsLclVarCommon()->GetLclNum();
const unsigned ssaNum = GetSsaNumForLocalVarDef(tree);
if (ssaNum != SsaConfig::RESERVED_SSA_NUM)
{
BasicBlock* defBlock = lvaTable[lclNum].GetPerSsaData(ssaNum)->GetBlock();
Copy link
Contributor

Choose a reason for hiding this comment

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

Style nit: lvaTable[lclNum]. -> lvaGetDesc(lclNum)->.

General comment on the algorithm: this walks only one step up in the def chain, so if we have something like this:

var a = const;
for (...) {
    var b = a;
    Use(b);
}

We will still propagate const to Use.

Copy link
Member Author

Choose a reason for hiding this comment

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

@SingleAccretion I don't see why it would.
When we're at Use(b) and we query SsaData()->Block from b we get a's Block and it leads to "avoid propagating" path

Copy link
Member Author

@EgorBo EgorBo Jun 9, 2022

Choose a reason for hiding this comment

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

Ah, actually for your case b is going to be replaced with a in an earlier phase VN based copy prop

Copy link
Contributor

@SingleAccretion SingleAccretion Jun 9, 2022

Choose a reason for hiding this comment

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

When we're at Use(b) and we query SsaData()->Block from b we get a's Block and it leads to "avoid propagating" path

If I annotate this with blocks:

BB01:
var a = const;

for (...) {
BB02:
    var b = a; // b:d:1
    Use(b);    // b:u:1
}

I am pretty sure that when we query the def block for b:u:1, we'd get BB02, since the def b:d:1 occurs inside it.

Ah, actually for your case b is going to be replaced with a in an earlier phase VN based copy prop

I agree that copy propagation helps us here; not sure by how much because of the liveness constraints it has.

In any case, I also agree that there is no reason, for now, to complicate the code with a full UD chain walk, because we are pretty much targeting people hoisting constants out of loops by hands.


if (defBlock != nullptr)
{
// A simple heuristic: If the constant is defined outside of a loop (not far from its head)
// and is used inside it - don't propagate.

// TODO: if it lives across calls in that loops we'd better propagate it after those
// calls on ABIs without callee-saved registers

if (!defBlock->IsInLoop() && block->IsInLoop())
{
//const LoopDsc* loopInfo = &optLoopTable[block->bbNatLoopNum];
// Definition better to be close (e.g. result of CSE) so we won't occupy a register

//if ((loopInfo->lpHead == defBlock) || loopInfo->lpHead->bbIDom == defBlock)
{
// Last, let's limit it to cases where block is not some not-always-taken
// block inside that loop
//weight_t defBlockWeight = defBlock->getBBWeight(this);
//weight_t blockWeight = block->getBBWeight(this);

//if (((blockWeight / defBlockWeight) >= BB_LOOP_WEIGHT_SCALE) && (defBlockWeight > 0))
{
JITDUMP("Skip constant propagation inside loop " FMT_BB "\n", block->bbNum);
return nullptr;
}
}
}
}
}
}
}

// Were able to optimize.
conValTree->gtVNPair = vnPair;
GenTree* sideEffList = optExtractSideEffListFromConst(tree);
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/jit/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,11 @@ struct BasicBlock : private LIR::Range
return PredEdgeList(bbPreds);
}

bool IsInLoop() const
{
return bbNatLoopNum != NOT_IN_LOOP;
}

// PredBlocks: convenience method for enabling range-based `for` iteration over predecessor blocks, e.g.:
// for (BasicBlock* const predBlock : block->PredBlocks()) ...
//
Expand Down