diff options
| author | Yong He <yonghe@outlook.com> | 2023-05-09 09:44:33 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-09 09:44:33 -0700 |
| commit | 38ed03a7203baacf36fca62539ac74fd45ed42d2 (patch) | |
| tree | 9648daee25c0a2aaac2fa8cd7d91908fd2aeef2f /source/slang/slang-ir-util.cpp | |
| parent | 89a1234964a1927c4936a2758f72b7d6c9d0bc73 (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.cpp | 175 |
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; |
