summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-ir-redundancy-removal.cpp
diff options
context:
space:
mode:
authorArielG-NV <159081215+ArielG-NV@users.noreply.github.com>2025-08-07 00:22:22 -0700
committerGitHub <noreply@github.com>2025-08-07 07:22:22 +0000
commit063cbeaaea2fb00a10c6058ea4a9632092772ea5 (patch)
treeb4412347d6c264c3b1a84ec971921a5e2fe76134 /source/slang/slang-ir-redundancy-removal.cpp
parent9e2685853033f4286feaf22d04a755a7395d95ce (diff)
Initial copy elision pass (#8042)
Fixes #7574 Changes: * Add an initial (fairly simple) optimization pass which is able to eliminate redundant copies. * Our current existing optimizer passes remove redundant load/store very robustly, this pass will focus on other cases of copy elimination * Primary approach is to make all functions which are `in T` and `T` is trivial to copy into a `__constref T`. We then (depending on scenario) manually insert a variable+load if a pass-by-reference is not possible; otherwise we pass by `constref`. * Added optimizations to eliminate redundant code which causes `constref` to fail to compile --------- Co-authored-by: Harsh Aggarwal <haaggarwal@nvidia.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: slangbot <ellieh+slangbot@nvidia.com> Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
Diffstat (limited to 'source/slang/slang-ir-redundancy-removal.cpp')
-rw-r--r--source/slang/slang-ir-redundancy-removal.cpp137
1 files changed, 113 insertions, 24 deletions
diff --git a/source/slang/slang-ir-redundancy-removal.cpp b/source/slang/slang-ir-redundancy-removal.cpp
index a6dac723e..1feab47dd 100644
--- a/source/slang/slang-ir-redundancy-removal.cpp
+++ b/source/slang/slang-ir-redundancy-removal.cpp
@@ -361,43 +361,132 @@ bool tryRemoveRedundantStore(IRGlobalValueWithCode* func, IRStore* store)
return false;
}
+// Checks if we can change or have a modified rootVar
+// at some point.
+bool isExternallyModifiableAddr(IRInst* rootVar)
+{
+ if (!rootVar)
+ return false;
+
+ auto ptr = as<IRConstRefType>(rootVar->getDataType());
+ if (!ptr)
+ return true;
+
+ // Only a UserPointer can potentially be modified and changed to point to a different address
+ // if constRef. This may happen from a different thread even if constref to the current thread.
+ auto addrSpace = ptr->getAddressSpace();
+ if (addrSpace == AddressSpace::UserPointer)
+ return true;
+
+ return false;
+}
+
+bool tryRemoveRedundantLoad(IRGlobalValueWithCode* func, IRLoad* load)
+{
+ bool changed = false;
+
+ // If the load is preceeded by a store without any side-effect insts
+ // in-between, remove the load.
+ for (auto prev = load->getPrevInst(); prev; prev = prev->getPrevInst())
+ {
+ if (auto store = as<IRStore>(prev))
+ {
+ if (store->getPtr() == load->getPtr())
+ {
+ auto value = store->getVal();
+ load->replaceUsesWith(value);
+ load->removeAndDeallocate();
+ changed = true;
+ break;
+ }
+ }
+
+ if (canInstHaveSideEffectAtAddress(func, prev, load->getPtr()))
+ {
+ break;
+ }
+ }
+
+ return changed;
+}
+
bool eliminateRedundantLoadStore(IRGlobalValueWithCode* func)
{
bool changed = false;
for (auto block : func->getBlocks())
{
- for (auto inst = block->getFirstInst(); inst;)
+ IRInst* nextInst = nullptr;
+ for (auto inst = block->getFirstInst(); inst; inst = nextInst)
{
- auto nextInst = inst->getNextInst();
+ nextInst = inst->getNextInst();
if (auto load = as<IRLoad>(inst))
{
- for (auto prev = inst->getPrevInst(); prev; prev = prev->getPrevInst())
- {
- if (auto store = as<IRStore>(prev))
- {
- if (store->getPtr() == load->getPtr())
- {
- // If the load is preceeded by a store without any side-effect insts
- // in-between, remove the load.
- auto value = store->getVal();
- load->replaceUsesWith(value);
- load->removeAndDeallocate();
- changed = true;
- break;
- }
- }
-
- if (canInstHaveSideEffectAtAddress(func, prev, load->getPtr()))
- {
- break;
- }
- }
+ changed |= tryRemoveRedundantLoad(func, load);
}
else if (auto store = as<IRStore>(inst))
{
changed |= tryRemoveRedundantStore(func, store);
}
- inst = nextInst;
+ else if (auto getElementPtr = as<IRGetElementPtr>(inst))
+ {
+ auto rootAddr = getRootAddr(getElementPtr);
+ if (isExternallyModifiableAddr(rootAddr))
+ continue;
+
+ // GetElement(Load(GetElementPtr(x)))) ==> Load(GetElementPtr(GetElementPtr(x)))
+ // The benefit is that any GetAddr(Load(...)) can then transitively be optimized
+ // out.
+ // This can only be done if we have no side-effects. `constref` never has
+ // single-invocation side-effects.
+ traverseUsers<IRLoad>(
+ getElementPtr,
+ [&](IRLoad* load)
+ {
+ traverseUsers<IRGetElement>(
+ load,
+ [&](IRGetElement* getElement)
+ {
+ IRBuilder builder(getElement);
+ builder.setInsertBefore(getElement);
+ auto newGetElementPtr = builder.emitElementAddress(
+ getElementPtr,
+ getElement->getIndex());
+ auto newLoad = builder.emitLoad(newGetElementPtr);
+ getElement->replaceUsesWith(newLoad);
+ changed = true;
+ });
+ });
+ }
+ else if (auto fieldAddress = as<IRFieldAddress>(inst))
+ {
+ auto rootAddr = getRootAddr(fieldAddress);
+ if (isExternallyModifiableAddr(rootAddr))
+ continue;
+
+ // ExtractField(Load(GetFieldAddr(x)))) ==> Load(GetFieldAddr(GetFieldAddr(x)))
+ // The benefit is that any GetAddr(Load(...)) can then transitively be optimized
+ // out.
+ // This can only be done if we have no side-effects. `constref` never has
+ // single-invocation side-effects.
+ traverseUsers<IRLoad>(
+ fieldAddress,
+ [&](IRLoad* load)
+ {
+ traverseUsers<IRFieldExtract>(
+ load,
+ [&](IRFieldExtract* fieldExtract)
+ {
+ IRBuilder builder(fieldExtract);
+ builder.setInsertBefore(fieldExtract);
+ auto newGetFieldAddress = builder.emitFieldAddress(
+ fieldAddress,
+ fieldExtract->getField());
+ auto newLoad = builder.emitLoad(newGetFieldAddress);
+ fieldExtract->replaceUsesWith(newLoad);
+ changed = true;
+ });
+ });
+ }
}
}
return changed;