summaryrefslogtreecommitdiff
path: root/source/slang/slang-ir-util.cpp
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2023-05-09 09:44:33 -0700
committerGitHub <noreply@github.com>2023-05-09 09:44:33 -0700
commit38ed03a7203baacf36fca62539ac74fd45ed42d2 (patch)
tree9648daee25c0a2aaac2fa8cd7d91908fd2aeef2f /source/slang/slang-ir-util.cpp
parent89a1234964a1927c4936a2758f72b7d6c9d0bc73 (diff)
Fix function side-effectness prop logic. (#2875)
Diffstat (limited to 'source/slang/slang-ir-util.cpp')
-rw-r--r--source/slang/slang-ir-util.cpp175
1 files changed, 123 insertions, 52 deletions
diff --git a/source/slang/slang-ir-util.cpp b/source/slang/slang-ir-util.cpp
index 506e96c81..ba34a725d 100644
--- a/source/slang/slang-ir-util.cpp
+++ b/source/slang/slang-ir-util.cpp
@@ -180,6 +180,7 @@ bool isValueType(IRInst* dataType)
case kIROp_AnyValueType:
case kIROp_ArrayType:
case kIROp_FuncType:
+ case kIROp_RaytracingAccelerationStructureType:
return true;
default:
// Read-only resource handles are considered as Value type.
@@ -406,12 +407,19 @@ bool isPtrLikeOrHandleType(IRInst* type)
{
if (!type)
return false;
+ if (as<IRPointerLikeType>(type))
+ return true;
+ if (as<IRPseudoPtrType>(type))
+ return true;
+ if (as<IRMeshOutputType>(type))
+ return true;
+ if (as<IRHLSLOutputPatchType>(type))
+ return true;
switch (type->getOp())
{
case kIROp_ComPtrType:
case kIROp_RawPointerType:
case kIROp_RTTIPointerType:
- case kIROp_PseudoPtrType:
case kIROp_OutType:
case kIROp_InOutType:
case kIROp_PtrType:
@@ -445,7 +453,7 @@ bool canInstHaveSideEffectAtAddress(IRGlobalValueWithCode* func, IRInst* inst, I
{
auto callee = call->getCallee();
if (callee &&
- callee->findDecoration<IRReadNoneDecoration>())
+ doesCalleeHaveSideEffect(callee))
{
// An exception is if the callee is side-effect free and is not reading from
// memory.
@@ -641,71 +649,100 @@ void setInsertAfterOrdinaryInst(IRBuilder* builder, IRInst* inst)
}
}
-bool isPureFunctionalCall(IRCall* call)
+bool areCallArgumentsSideEffectFree(IRCall* call)
{
- auto callee = getResolvedInstForDecorations(call->getCallee());
- if (callee->findDecoration<IRReadNoneDecoration>())
+ // If the function has no side effect and is not writing to any outputs,
+ // we can safely treat the call as a normal inst.
+ IRFunc* parentFunc = nullptr;
+ for (UInt i = 0; i < call->getArgCount(); i++)
{
- // If the function has no side effect and is not writing to any outputs,
- // we can safely treat the call as a normal inst.
- IRFunc* parentFunc = nullptr;
- for (UInt i = 0; i < call->getArgCount(); i++)
- {
- auto arg = call->getArg(i);
- if (isValueType(arg->getDataType()))
- continue;
+ auto arg = call->getArg(i);
+ if (isValueType(arg->getDataType()))
+ continue;
- // If the argument type is not a known value type,
- // assume it is a pointer or handle through which side effect can take place.
+ // If the argument type is not a known value type,
+ // assume it is a pointer or handle through which side effect can take place.
+ if (!parentFunc)
+ {
+ parentFunc = getParentFunc(call);
if (!parentFunc)
- {
- parentFunc = getParentFunc(call);
- if (!parentFunc)
- return false;
- }
+ return false;
+ }
- if (arg->getOp() == kIROp_Var && getParentFunc(arg) == parentFunc)
+ if (arg->getOp() == kIROp_Var && getParentFunc(arg) == parentFunc)
+ {
+ // If the pointer argument is a local variable (thus can't alias with other addresses)
+ // and it is never read from in the function, we can safely treat the call as having
+ // no side-effect.
+ // This is a conservative test, but is sufficient to detect the most common case where
+ // a temporary variable is used as the inout argument and the result stored in the temp
+ // variable isn't being used elsewhere in the parent func.
+ //
+ // A more aggresive test can check all other address uses reachable from the call site
+ // and see if any of them are aliasing with the argument.
+ for (auto use = arg->firstUse; use; use = use->nextUse)
{
- // If the pointer argument is a local variable (thus can't alias with other addresses)
- // and it is never read from in the function, we can safely treat the call as having
- // no side-effect.
- // This is a conservative test, but is sufficient to detect the most common case where
- // a temporary variable is used as the inout argument and the result stored in the temp
- // variable isn't being used elsewhere in the parent func.
- //
- // A more aggresive test can check all other address uses reachable from the call site
- // and see if any of them are aliasing with the argument.
- for (auto use = arg->firstUse; use; use = use->nextUse)
+ if (as<IRDecoration>(use->getUser()))
+ continue;
+ switch (use->getUser()->getOp())
{
- if (as<IRDecoration>(use->getUser()))
- continue;
- switch (use->getUser()->getOp())
- {
- case kIROp_Store:
- // We are fine with stores into the variable, since store operations
- // are not dependent on whatever we do in the call here.
+ case kIROp_Store:
+ case kIROp_SwizzledStore:
+ // We are fine with stores into the variable, since store operations
+ // are not dependent on whatever we do in the call here.
+ continue;
+ default:
+ // Skip the call itself, since we are checking if the call has side effect.
+ if (use->getUser() == call)
continue;
- default:
- // Skip the call itself, since we are checking if the call has side effect.
- if (use->getUser() == call)
- continue;
- // We have some other unknown use of the variable address, they can
- // be loads, or calls using addresses derived from the variable,
- // we will treat the call as having side effect to be safe.
- return false;
- }
+ // We have some other unknown use of the variable address, they can
+ // be loads, or calls using addresses derived from the variable,
+ // we will treat the call as having side effect to be safe.
+ return false;
}
}
- else
- {
- return false;
- }
}
- return true;
+ else
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool isPureFunctionalCall(IRCall* call)
+{
+ auto callee = getResolvedInstForDecorations(call->getCallee());
+ if (callee->findDecoration<IRReadNoneDecoration>())
+ {
+ return areCallArgumentsSideEffectFree(call);
}
return false;
}
+bool isSideEffectFreeFunctionalCall(IRCall* call)
+{
+ if (!doesCalleeHaveSideEffect(call->getCallee()))
+ {
+ return areCallArgumentsSideEffectFree(call);
+ }
+ return false;
+}
+
+bool doesCalleeHaveSideEffect(IRInst* callee)
+{
+ for (auto decor : getResolvedInstForDecorations(callee)->getDecorations())
+ {
+ switch (decor->getOp())
+ {
+ case kIROp_NoSideEffectDecoration:
+ case kIROp_ReadNoneDecoration:
+ return false;
+ }
+ }
+ return true;
+}
+
IRInst* findInterfaceRequirement(IRInterfaceType* type, IRInst* key)
{
for (UInt i = 0; i < type->getOperandCount(); i++)
@@ -779,6 +816,40 @@ int getParamIndexInBlock(IRParam* paramInst)
return -1;
}
+bool isGlobalOrUnknownMutableAddress(IRGlobalValueWithCode* parentFunc, IRInst* inst)
+{
+ auto root = getRootAddr(inst);
+
+ auto type = unwrapAttributedType(inst->getDataType());
+ if (!isPtrLikeOrHandleType(type))
+ return false;
+
+ switch (root->getOp())
+ {
+ case kIROp_GlobalVar:
+ return true;
+ case kIROp_GlobalParam:
+ case kIROp_GlobalConstant:
+ case kIROp_Var:
+ case kIROp_Param:
+ break;
+ default:
+ // The inst is defined by an unknown inst.
+ return true;
+ }
+
+ if (root)
+ {
+ if (as<IRParameterGroupType>(root->getDataType()))
+ {
+ return false;
+ }
+ auto addrInstParent = getParentFunc(root);
+ return (addrInstParent != parentFunc);
+ }
+ return false;
+}
+
struct GenericChildrenMigrationContextImpl
{
IRCloneEnv cloneEnv;