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

if (conValTree != nullptr)
{
if (tree->OperIs(GT_LCL_VAR))
if (tree->OperIs(GT_LCL_VAR) && conValTree->OperIs(GT_CNS_VEC, GT_CNS_DBL, GT_CNS_INT))
{
if (conValTree->OperIs(GT_CNS_VEC) && !conValTree->IsVectorZero() && !conValTree->IsVectorAllBitsSet())
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
{
// Try to find the block this value was defined in
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 the vector constant was defined outside of the loop - do not propagate
// TODO: do propagate if it lives across a call in that loop and the ABI doesn't have
// callee-saved registers
if (defBlock != nullptr)
{
const weight_t defBlockWeight = defBlock->getBBWeight(this);
const weight_t blockWeight = block->getBBWeight(this);

// A simple heuristic: If the constant is defined outside the loop (not far from its head)
// 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 accross calls in that loops we'd better propagate it after those
// calls on ABIs without callee-saved registers (e.g. Linux-x64)
if ((defBlockWeight > 0) && (blockWeight / defBlockWeight) >= 2)

// 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())
{
JITDUMP("Skip constant propagation of vector constant inside loop " FMT_BB "\n", block->bbNum);
return nullptr;
//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;
}
}
}
}
}
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