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

Simplify defer buffer load. #5552

Merged
merged 2 commits into from
Nov 14, 2024
Merged
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
150 changes: 2 additions & 148 deletions source/slang/slang-ir-defer-buffer-load.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,170 +11,25 @@ namespace Slang
{
struct DeferBufferLoadContext
{
struct AccessChain
{
List<IRInst*> chain;
mutable HashCode64 hash = 0;

bool operator==(const AccessChain& rhs) const
{
ensureHash();
rhs.ensureHash();
if (hash != rhs.hash)
return false;
if (chain.getCount() != rhs.chain.getCount())
return false;
for (Index i = 0; i < chain.getCount(); i++)
{
if (chain[i] != rhs.chain[i])
return false;
}
return true;
}
void ensureHash() const
{
if (hash == 0)
{
for (auto inst : chain)
{
hash = combineHash(hash, Slang::getHashCode(inst));
}
}
}
HashCode64 getHashCode() const
{
ensureHash();
return hash;
}
};

// Map an original SSA value to a pointer that can be used to load the value.
Dictionary<AccessChain, IRInst*> mapAccessChainToPtr;
Dictionary<IRInst*, IRInst*> mapValueToPtr;

// Map an ptr to its loaded value.
Dictionary<IRInst*, IRInst*> mapPtrToValue;

IRFunc* currentFunc = nullptr;
IRDominatorTree* dominatorTree = nullptr;

// Find the block that is dominated by all dependent blocks, and is the earliest block that
// dominates the target block.
// This is the place where we can insert the load instruction such that all access chain
// operands are defined and the load can be made avaialble to the location of valueInst.
//
IRBlock* findEarliestDominatingBlock(IRInst* valueInst, List<IRBlock*>& dependentBlocks)
{
auto targetBlock = getBlock(valueInst);
while (targetBlock)
{
auto idom = dominatorTree->getImmediateDominator(targetBlock);
if (!idom)
break;
bool isValid = true;
for (auto block : dependentBlocks)
{
if (!dominatorTree->dominates(block, idom))
{
isValid = false;
break;
}
}
if (isValid)
{
targetBlock = idom;
}
else
{
break;
}
}
return targetBlock;
}

// Find the earliest instruction before which we can insert the load instruction such that
// all dependent instructions for the load address are defined, and the load can reach all
// locations where the address is available.
//
IRInst* findEarliestInsertionPoint(IRInst* valueInst, AccessChain& chain)
{
List<IRBlock*> dependentBlocks;
List<IRInst*> dependentInsts;
for (auto inst : chain.chain)
{
if (auto block = getBlock(inst))
{
dependentBlocks.add(block);
dependentInsts.add(inst);
}
}
auto targetBlock = findEarliestDominatingBlock(valueInst, dependentBlocks);
IRInst* insertBeforeInst =
targetBlock == getBlock(valueInst) ? valueInst : targetBlock->getTerminator();
for (;;)
{
auto prev = insertBeforeInst->getPrevInst();
if (!prev)
break;
bool valid = true;
for (auto inst : dependentInsts)
{
if (!dominatorTree->dominates(inst, prev) || inst == prev)
{
valid = false;
break;
}
}
if (valid)
{
insertBeforeInst = prev;
}
else
{
break;
}
}
return insertBeforeInst;
}

// Ensure that for an original SSA value, we have formed a pointer that can be used to load the
// value.
IRInst* ensurePtr(IRInst* valueInst)
{
IRInst* result = nullptr;
if (mapValueToPtr.tryGetValue(valueInst, result))
return result;
AccessChain chain;
IRInst* current = valueInst;
while (current)
{
bool processed = false;
switch (current->getOp())
{
case kIROp_GetElement:
case kIROp_FieldExtract:
chain.chain.add(current->getOperand(1));
current = current->getOperand(0);
processed = true;
break;
default:
break;
}
if (!processed)
break;
}
chain.chain.add(current);
chain.chain.reverse();
if (mapAccessChainToPtr.tryGetValue(chain, result))
return result;

// Find the proper place to insert the load instruction.
// This is the location where all operands of the access chain are defined.
// And is the earliest block so all possible uses of the value at access chain
// can be reached.
IRBuilder b(valueInst);

auto insertBeforeInst = findEarliestInsertionPoint(valueInst, chain);
b.setInsertBefore(insertBeforeInst);
b.setInsertBefore(valueInst);

switch (valueInst->getOp())
{
Expand Down Expand Up @@ -205,7 +60,6 @@ struct DeferBufferLoadContext
}
if (result)
{
mapAccessChainToPtr[chain] = result;
mapValueToPtr[valueInst] = result;
}
return result;
Expand Down
Loading