diff options
Diffstat (limited to 'source/slang')
| -rw-r--r-- | source/slang/core.meta.slang | 6 | ||||
| -rw-r--r-- | source/slang/diff.meta.slang | 40 | ||||
| -rw-r--r-- | source/slang/hlsl.meta.slang | 64 | ||||
| -rw-r--r-- | source/slang/slang-ast-modifier.h | 9 | ||||
| -rw-r--r-- | source/slang/slang-ir-addr-inst-elimination.cpp | 16 | ||||
| -rw-r--r-- | source/slang/slang-ir-addr-inst-elimination.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-ir-autodiff-fwd.cpp | 14 | ||||
| -rw-r--r-- | source/slang/slang-ir-autodiff-primal-hoist.cpp | 16 | ||||
| -rw-r--r-- | source/slang/slang-ir-inst-defs.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 6 | ||||
| -rw-r--r-- | source/slang/slang-ir-link.cpp | 1 | ||||
| -rw-r--r-- | source/slang/slang-ir-lower-witness-lookup.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-ir-propagate-func-properties.cpp | 293 | ||||
| -rw-r--r-- | source/slang/slang-ir-simplify-cfg.cpp | 173 | ||||
| -rw-r--r-- | source/slang/slang-ir-specialize-function-call.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-ir-util.cpp | 175 | ||||
| -rw-r--r-- | source/slang/slang-ir-util.h | 13 | ||||
| -rw-r--r-- | source/slang/slang-ir.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 10 |
19 files changed, 624 insertions, 226 deletions
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index a2ed1d1df..b992def6e 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -112,6 +112,12 @@ interface __BuiltinSignedArithmeticType : __BuiltinArithmeticType {} interface __BuiltinIntegerType : __BuiltinArithmeticType {} +__attributeTarget(AggTypeDecl) +attribute_syntax [__NonCopyableType] : NonCopyableTypeAttribute; + +__attributeTarget(FunctionDeclBase) +attribute_syntax [__NoSideEffect] : NoSideEffectAttribute; + /// Marks a function for forward-mode differentiation. /// i.e. the compiler will automatically generate a new function /// that computes the jacobian-vector product of the original. diff --git a/source/slang/diff.meta.slang b/source/slang/diff.meta.slang index 84a72a425..0725103da 100644 --- a/source/slang/diff.meta.slang +++ b/source/slang/diff.meta.slang @@ -32,16 +32,16 @@ __intrinsic_type($(kIROp_TensorViewType)) struct TensorView { __target_intrinsic(cuda, "$0.data_ptr<$G0>()") - [__readNone] + [__NoSideEffect] Ptr<T> data_ptr(); __target_intrinsic(cuda, "$0.data_ptr_at<$G0>($1)") - [__readNone] + [__NoSideEffect] Ptr<T> data_ptr_at(uint index); __generic<let N: int> __target_intrinsic(cuda, "$0.data_ptr_at<$G0>($1)") - [__readNone] + [__NoSideEffect] Ptr<T> data_ptr_at(vector<uint, N> index); __implicit_conversion($(kConversionCost_ImplicitDereference)) @@ -49,19 +49,19 @@ struct TensorView __init(TorchTensor<T> t); __target_intrinsic(cuda, "$0.load<$G0>($1)") - [__readNone] + [__NoSideEffect] T load(uint x); __target_intrinsic(cuda, "$0.load<$G0>($1, $2)") - [__readNone] + [__NoSideEffect] T load(uint x, uint y); __target_intrinsic(cuda, "$0.load<$G0>($1, $2, $3)") - [__readNone] + [__NoSideEffect] T load(uint x, uint y, uint z); __target_intrinsic(cuda, "$0.load<$G0>($1, $2, $3, $4)") - [__readNone] + [__NoSideEffect] T load(uint x, uint y, uint z, uint w); __target_intrinsic(cuda, "$0.load<$G0>($1, $2, $3, $4, $5)") - [__readNone] + [__NoSideEffect] T load(uint i0, uint i1, uint i2, uint i3, uint i4); __target_intrinsic(cuda, "$0.store<$G0>($1, $2)") @@ -96,59 +96,67 @@ struct TensorView __subscript(uint index) -> T { - [ForceInline] [__readNone] get { return load(index); } + [ForceInline] [__NoSideEffect] get { return load(index); } [ForceInline] set { store(index, newValue); } __target_intrinsic(cuda, "$0.load<$G0>($1)") + [__NoSideEffect] ref; } __subscript(uint i1, uint i2) -> T { - [ForceInline] [__readNone] get { return load(i1, i2); } + [ForceInline] [__NoSideEffect] get { return load(i1, i2); } [ForceInline] set { store(i1, i2, newValue); } __target_intrinsic(cuda, "$0.load<$G0>($1, $2)") + [__NoSideEffect] ref; } __subscript(uint2 i) -> T { - [ForceInline] [__readNone] get { return load(i.x, i.y); } + [ForceInline] [__NoSideEffect] get { return load(i.x, i.y); } [ForceInline] set { store(i.x, i.y, newValue); } __target_intrinsic(cuda, "$0.load<$G0>($1.x, $1.y)") + [__NoSideEffect] ref; } __subscript(uint i1, uint i2, uint i3) -> T { - [ForceInline] [__readNone] get { return load(i1, i2, i3); } + [ForceInline] [__NoSideEffect] get { return load(i1, i2, i3); } [ForceInline] set { store(i1, i2, i3, newValue); } __target_intrinsic(cuda, "$0.load<$G0>($1, $2, $3)") + [__NoSideEffect] ref; } __subscript(uint3 i) -> T { - [ForceInline] [__readNone] get { return load(i.x, i.y, i.z); } + [ForceInline] [__NoSideEffect] get { return load(i.x, i.y, i.z); } [ForceInline] set { store(i.x, i.y, i.z, newValue); } __target_intrinsic(cuda, "$0.load<$G0>($1.x, $1.y, $1.z)") + [__NoSideEffect] ref; } __subscript(uint i1, uint i2, uint i3, uint i4) -> T { - [ForceInline] [__readNone] get { return load(i1, i2, i3, i4); } + [ForceInline] [__NoSideEffect] get { return load(i1, i2, i3, i4); } [ForceInline] set { store(i1, i2, i3, i4, newValue); } __target_intrinsic(cuda, "$0.load<$G0>($1, $2, $3, $4)") + [__NoSideEffect] ref; } __subscript(uint4 i) -> T { - [__readNone][ForceInline] get { return load(i.x, i.y, i.z, i.w); } + [__NoSideEffect][ForceInline] get { return load(i.x, i.y, i.z, i.w); } [ForceInline] set { store(i.x, i.y, i.z, i.w, newValue); } __target_intrinsic(cuda, "$0.load<$G0>($1.x, $1.y, $1.z, $1.w)") + [__NoSideEffect] ref; } __subscript(uint i1, uint i2, uint i3, uint i4, uint i5) -> T { - [ForceInline] [__readNone] get { return load(i1, i2, i3, i4, i5); } + [ForceInline] [__NoSideEffect] get { return load(i1, i2, i3, i4, i5); } [ForceInline] set { store(i1, i2, i3, i4, i5, newValue); } __target_intrinsic(cuda, "$0.load<$G0>($1, $2, $3, $4, $5)") + [__NoSideEffect] ref; } } diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index 1580a7a23..37af3ef5a 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -300,25 +300,34 @@ struct $(item.name) out uint dim); __target_intrinsic(glsl, "$0._data[$1/4]") + [__NoSideEffect] uint Load(int location); + [__NoSideEffect] uint Load(int location, out uint status); __target_intrinsic(glsl, "uvec2($0._data[$1/4], $0._data[$1/4+1])") + [__NoSideEffect] uint2 Load2(int location); + [__NoSideEffect] uint2 Load2(int location, out uint status); __target_intrinsic(glsl, "uvec3($0._data[$1/4], $0._data[$1/4+1], $0._data[$1/4+2])") + [__NoSideEffect] uint3 Load3(int location); + [__NoSideEffect] uint3 Load3(int location, out uint status); __target_intrinsic(glsl, "uvec4($0._data[$1/4], $0._data[$1/4+1], $0._data[$1/4+2], $0._data[$1/4+3])") + [__NoSideEffect] uint4 Load4(int location); + [__NoSideEffect] uint4 Load4(int location, out uint status); + [__NoSideEffect] T Load<T>(int location) { return __byteAddressBufferLoad<T>(this, location); @@ -713,13 +722,16 @@ struct $(item.name) __target_intrinsic(glsl, "$0._data[$1]") __target_intrinsic(spirv_direct, "%addr = OpAccessChain resultType*StorageBuffer resultId _0 const(int, 0) _1; OpLoad resultType resultId %addr;") + [__NoSideEffect] T Load(int location); + [__NoSideEffect] T Load(int location, out uint status); __subscript(uint index) -> T { __target_intrinsic(glsl, "$0._data[$1]") __target_intrinsic(spirv_direct, "*StorageBuffer OpAccessChain resultType resultId _0 const(int, 0) _1") + [__NoSideEffect] ref; } }; @@ -5685,13 +5697,9 @@ __target_intrinsic(hlsl, RayQuery) __target_intrinsic(glsl, rayQueryEXT) __glsl_extension(GL_EXT_ray_query) __glsl_version(460) +[__NonCopyableType] struct RayQuery <let rayFlagsGeneric : RAY_FLAG = RAY_FLAG_NONE> { - // Initialize the query object in a "fresh" state. - // - __intrinsic_op($(kIROp_DefaultConstruct)) - __init(); - // Initialize a ray-tracing query. // // This method may be called on a "fresh" ray query, or @@ -5705,6 +5713,8 @@ struct RayQuery <let rayFlagsGeneric : RAY_FLAG = RAY_FLAG_NONE> // must obey any API-imposed restrictions. // __target_intrinsic(hlsl) + [__NoSideEffect] + [mutating] void TraceRayInline( RaytracingAccelerationStructure accelerationStructure, RAY_FLAG rayFlags, @@ -5725,6 +5735,7 @@ struct RayQuery <let rayFlagsGeneric : RAY_FLAG = RAY_FLAG_NONE> [__unsafeForceInlineEarly] __specialized_for_target(glsl) + [__NoSideEffect] void TraceRayInline( RaytracingAccelerationStructure accelerationStructure, RAY_FLAG rayFlags, @@ -5758,6 +5769,8 @@ struct RayQuery <let rayFlagsGeneric : RAY_FLAG = RAY_FLAG_NONE> __target_intrinsic(glsl, rayQueryProceedEXT) __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__NoSideEffect] + [mutating] bool Proceed(); // Causes the ray query to terminate. @@ -5769,6 +5782,8 @@ struct RayQuery <let rayFlagsGeneric : RAY_FLAG = RAY_FLAG_NONE> __target_intrinsic(glsl, rayQueryTerminateEXT) __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__NoSideEffect] + [mutating] void Abort(); // Get the type of candidate hit being considered. @@ -5783,6 +5798,7 @@ struct RayQuery <let rayFlagsGeneric : RAY_FLAG = RAY_FLAG_NONE> __target_intrinsic(glsl, "rayQueryGetIntersectionTypeEXT($0, false)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] CANDIDATE_TYPE CandidateType(); // Access properties of a candidate hit. @@ -5790,46 +5806,55 @@ struct RayQuery <let rayFlagsGeneric : RAY_FLAG = RAY_FLAG_NONE> __target_intrinsic(glsl, "transpose(rayQueryGetIntersectionObjectToWorldEXT($0, false))") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float3x4 CandidateObjectToWorld3x4(); __target_intrinsic(glsl, "rayQueryGetIntersectionObjectToWorldEXT($0, false)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float4x3 CandidateObjectToWorld4x3(); __target_intrinsic(glsl, "transpose(rayQueryGetIntersectionWorldToObjectEXT($0, false))") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float3x4 CandidateWorldToObject3x4(); __target_intrinsic(glsl, "rayQueryGetIntersectionWorldToObjectEXT($0, false)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float4x3 CandidateWorldToObject4x3(); __target_intrinsic(glsl, "rayQueryGetIntersectionInstanceIdEXT($0, false)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] uint CandidateInstanceIndex(); __target_intrinsic(glsl, "rayQueryGetIntersectionInstanceCustomIndexEXT($0, false)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] uint CandidateInstanceID(); __target_intrinsic(glsl, "rayQueryGetIntersectionGeometryIndexEXT($0, false)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] uint CandidateGeometryIndex(); __target_intrinsic(glsl, "rayQueryGetIntersectionPrimitiveIndexEXT($0, false)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] uint CandidatePrimitiveIndex(); __target_intrinsic(glsl, "rayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetEXT($0, false)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] uint CandidateInstanceContributionToHitGroupIndex(); // Access properties of the ray being traced @@ -5838,11 +5863,13 @@ struct RayQuery <let rayFlagsGeneric : RAY_FLAG = RAY_FLAG_NONE> __target_intrinsic(glsl, "rayQueryGetIntersectionObjectRayOriginEXT($0, false)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float3 CandidateObjectRayOrigin(); __target_intrinsic(glsl, "rayQueryGetIntersectionObjectRayDirectionEXT($0, false)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float3 CandidateObjectRayDirection(); // Access properties of a candidate procedural primitive hit. @@ -5850,6 +5877,7 @@ struct RayQuery <let rayFlagsGeneric : RAY_FLAG = RAY_FLAG_NONE> __target_intrinsic(glsl, "rayQueryGetIntersectionCandidateAABBOpaqueEXT($0, false)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] bool CandidateProceduralPrimitiveNonOpaque(); // Access properties of a candidate non-opaque triangle hit. @@ -5857,34 +5885,42 @@ struct RayQuery <let rayFlagsGeneric : RAY_FLAG = RAY_FLAG_NONE> __target_intrinsic(glsl, "rayQueryGetIntersectionFrontFaceEXT($0, false)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] bool CandidateTriangleFrontFace(); __target_intrinsic(glsl, "rayQueryGetIntersectionBarycentricsEXT($0, false)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float2 CandidateTriangleBarycentrics(); __target_intrinsic(glsl, "rayQueryGetIntersectionTEXT($0, false)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float CandidateTriangleRayT(); // Commit the current non-opaque triangle hit. __target_intrinsic(glsl, rayQueryConfirmIntersectionEXT) __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__NoSideEffect] + [mutating] void CommitNonOpaqueTriangleHit(); // Commit the current procedural primitive hit, with hit time `t`. __target_intrinsic(glsl, rayQueryGenerateIntersectionEXT) __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__NoSideEffect] + [mutating] void CommitProceduralPrimitiveHit(float t); // Get the status of the committed (closest) hit, if any. __target_intrinsic(glsl, "rayQueryGetIntersectionTypeEXT($0, true)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] COMMITTED_STATUS CommittedStatus(); // Access properties of the committed hit. @@ -5892,51 +5928,61 @@ struct RayQuery <let rayFlagsGeneric : RAY_FLAG = RAY_FLAG_NONE> __target_intrinsic(glsl, "transpose(rayQueryGetIntersectionObjectToWorldEXT($0, true))") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float3x4 CommittedObjectToWorld3x4(); __target_intrinsic(glsl, "rayQueryGetIntersectionObjectToWorldEXT($0, true)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float4x3 CommittedObjectToWorld4x3(); __target_intrinsic(glsl, "transpose(rayQueryGetIntersectionWorldToObjectEXT($0, true))") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float3x4 CommittedWorldToObject3x4(); __target_intrinsic(glsl, "rayQueryGetIntersectionWorldToObjectEXT($0, true)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float4x3 CommittedWorldToObject4x3(); __target_intrinsic(glsl, "rayQueryGetIntersectionTEXT($0, true)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float CommittedRayT(); __target_intrinsic(glsl, "rayQueryGetIntersectionInstanceIdEXT($0, true)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] uint CommittedInstanceIndex(); __target_intrinsic(glsl, "rayQueryGetIntersectionInstanceCustomIndexEXT($0, true)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] uint CommittedInstanceID(); __target_intrinsic(glsl, "rayQueryGetIntersectionGeometryIndexEXT($0, true)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] uint CommittedGeometryIndex(); __target_intrinsic(glsl, "rayQueryGetIntersectionPrimitiveIndexEXT($0, true)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] uint CommittedPrimitiveIndex(); __target_intrinsic(glsl, "rayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetEXT($0, true)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] uint CommittedInstanceContributionToHitGroupIndex(); // Access properties of the ray being traced @@ -5945,11 +5991,13 @@ struct RayQuery <let rayFlagsGeneric : RAY_FLAG = RAY_FLAG_NONE> __target_intrinsic(glsl, "rayQueryGetIntersectionObjectRayOriginEXT($0, true)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float3 CommittedObjectRayOrigin(); __target_intrinsic(glsl, "rayQueryGetIntersectionObjectRayDirectionEXT($0, true)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float3 CommittedObjectRayDirection(); // Access properties of a committed triangle hit. @@ -5957,11 +6005,13 @@ struct RayQuery <let rayFlagsGeneric : RAY_FLAG = RAY_FLAG_NONE> __target_intrinsic(glsl, "rayQueryGetIntersectionFrontFaceEXT($0, true)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] bool CommittedTriangleFrontFace(); __target_intrinsic(glsl, "rayQueryGetIntersectionBarycentricsEXT($0, true)") __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float2 CommittedTriangleBarycentrics(); // Access properties of the ray being traced. @@ -5969,21 +6019,25 @@ struct RayQuery <let rayFlagsGeneric : RAY_FLAG = RAY_FLAG_NONE> __target_intrinsic(glsl, rayQueryGetRayFlagsEXT) __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] uint RayFlags(); __target_intrinsic(glsl, rayQueryGetWorldRayOriginEXT) __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float3 WorldRayOrigin(); __target_intrinsic(glsl, rayQueryGetWorldRayDirectionEXT) __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float3 WorldRayDirection(); __target_intrinsic(glsl, rayQueryGetRayTMinEXT) __glsl_extension(GL_EXT_ray_query) __glsl_version(460) + [__readNone] float RayTMin(); } diff --git a/source/slang/slang-ast-modifier.h b/source/slang/slang-ast-modifier.h index ab66febb9..25761b11c 100644 --- a/source/slang/slang-ast-modifier.h +++ b/source/slang/slang-ast-modifier.h @@ -1294,6 +1294,15 @@ class DeprecatedAttribute : public Attribute String message; }; +class NonCopyableTypeAttribute : public Attribute +{ + SLANG_AST_CLASS(NonCopyableTypeAttribute) +}; + +class NoSideEffectAttribute : public Attribute +{ + SLANG_AST_CLASS(NoSideEffectAttribute) +}; /// A modifier that applies to types rather than declarations. /// /// In most cases, the Slang compiler assumes that a modifier should diff --git a/source/slang/slang-ir-addr-inst-elimination.cpp b/source/slang/slang-ir-addr-inst-elimination.cpp index 16bd67f66..4d44aac1f 100644 --- a/source/slang/slang-ir-addr-inst-elimination.cpp +++ b/source/slang/slang-ir-addr-inst-elimination.cpp @@ -1,5 +1,6 @@ #include "slang-ir-addr-inst-elimination.h" #include "slang-ir-insts.h" +#include "slang-ir-util.h" namespace Slang { @@ -110,7 +111,6 @@ struct AddressInstEliminationContext } SlangResult eliminateAddressInstsImpl( - AddressConversionPolicy* policy, IRFunc* func, DiagnosticSink* inSink) { @@ -123,9 +123,13 @@ struct AddressInstEliminationContext { for (auto inst : block->getChildren()) { - if (as<IRPtrTypeBase>(inst->getDataType())) + if (auto ptrType = as<IRPtrTypeBase>(inst->getDataType())) { - workList.add(inst); + auto valType = unwrapAttributedType(ptrType->getValueType()); + if (!getResolvedInstForDecorations(valType)->findDecoration<IRNonCopyableTypeDecoration>()) + { + workList.add(inst); + } } } } @@ -134,9 +138,6 @@ struct AddressInstEliminationContext { auto addrInst = workList[workListIndex]; - if (!policy->shouldConvertAddrInst(addrInst)) - continue; - for (auto use = addrInst->firstUse; use; ) { auto nextUse = use->nextUse; @@ -174,14 +175,13 @@ struct AddressInstEliminationContext }; SlangResult eliminateAddressInsts( - AddressConversionPolicy* policy, IRFunc* func, DiagnosticSink* sink) { AddressInstEliminationContext ctx; ctx.module = func->getModule(); ctx.sink = sink; - return ctx.eliminateAddressInstsImpl(policy, func, sink); + return ctx.eliminateAddressInstsImpl(func, sink); } } // namespace Slang diff --git a/source/slang/slang-ir-addr-inst-elimination.h b/source/slang/slang-ir-addr-inst-elimination.h index 6c6506bc0..b80372345 100644 --- a/source/slang/slang-ir-addr-inst-elimination.h +++ b/source/slang/slang-ir-addr-inst-elimination.h @@ -7,12 +7,7 @@ namespace Slang { class DiagnosticSink; -struct AddressConversionPolicy -{ - virtual bool shouldConvertAddrInst(IRInst* addrInst) = 0; -}; SlangResult eliminateAddressInsts( - AddressConversionPolicy* policy, IRFunc* func, DiagnosticSink* sink); diff --git a/source/slang/slang-ir-autodiff-fwd.cpp b/source/slang/slang-ir-autodiff-fwd.cpp index 444816ff7..e6bfc751c 100644 --- a/source/slang/slang-ir-autodiff-fwd.cpp +++ b/source/slang/slang-ir-autodiff-fwd.cpp @@ -1590,16 +1590,6 @@ void insertTempVarForMutableParams(IRModule* module, IRFunc* func) } } -struct AutoDiffAddressConversionPolicy : public AddressConversionPolicy -{ - DifferentiableTypeConformanceContext* diffTypeContext; - - virtual bool shouldConvertAddrInst(IRInst*) override - { - return true; - } -}; - SlangResult ForwardDiffTranscriber::prepareFuncForForwardDiff(IRFunc* func) { insertTempVarForMutableParams(autoDiffSharedContext->moduleInst->getModule(), func); @@ -1609,9 +1599,7 @@ SlangResult ForwardDiffTranscriber::prepareFuncForForwardDiff(IRFunc* func) initializeLocalVariables(autoDiffSharedContext->moduleInst->getModule(), func); - AutoDiffAddressConversionPolicy cvtPolicty; - cvtPolicty.diffTypeContext = &differentiableTypeConformanceContext; - auto result = eliminateAddressInsts(&cvtPolicty, func, sink); + auto result = eliminateAddressInsts(func, sink); if (SLANG_SUCCEEDED(result)) { diff --git a/source/slang/slang-ir-autodiff-primal-hoist.cpp b/source/slang/slang-ir-autodiff-primal-hoist.cpp index 353d56cfa..0016f25e3 100644 --- a/source/slang/slang-ir-autodiff-primal-hoist.cpp +++ b/source/slang/slang-ir-autodiff-primal-hoist.cpp @@ -1602,20 +1602,6 @@ static CheckpointPreference getCheckpointPreference(IRInst* callee) return CheckpointPreference::None; } -static bool isGlobalMutableAddress(IRInst* inst) -{ - auto root = getRootAddr(inst); - if (root) - { - if (as<IRParameterGroupType>(root->getDataType())) - { - return false; - } - return as<IRModuleInst>(root->getParent()) != nullptr; - } - return false; -} - static bool isInstInPrimalOrTransposedParameterBlocks(IRInst* inst) { auto func = getParentFunc(inst); @@ -1790,7 +1776,7 @@ bool DefaultCheckpointPolicy::canRecompute(UseOrPseudoUse use) // We can't recompute a `load` is if it is a load from a global mutable // variable. - if (isGlobalMutableAddress(ptr)) + if (isGlobalOrUnknownMutableAddress(getParentFunc(load), ptr)) return false; // We can't recompute a 'load' from a mutable function parameter. diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index ffdd2b337..d9036f8bc 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -763,6 +763,9 @@ INST(HighLevelDeclDecoration, highLevelDecl, 1, 0) /// Applie to an IR function and signals that inlining should not be performed unless unavoidable. INST(NoInlineDecoration, noInline, 0, 0) + // Marks a type to be non copyable, causing SSA pass to skip turning variables of the the type into SSA values. + INST(NonCopyableTypeDecoration, nonCopyable, 0, 0) + /// A call to the decorated function should always be folded into its use site. INST(AlwaysFoldIntoUseSiteDecoration, alwaysFold, 0, 0) diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 9c31798e0..3f49be801 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -336,6 +336,7 @@ IR_SIMPLE_DECORATION(RequiresNVAPIDecoration) IR_SIMPLE_DECORATION(NoInlineDecoration) IR_SIMPLE_DECORATION(AlwaysFoldIntoUseSiteDecoration) IR_SIMPLE_DECORATION(StaticRequirementDecoration) +IR_SIMPLE_DECORATION(NonCopyableTypeDecoration) struct IRNVAPIMagicDecoration : IRDecoration { @@ -3958,6 +3959,11 @@ public: addDecoration(value, kIROp_NVAPISlotDecoration, getStringValue(registerName), getStringValue(spaceName)); } + void addNonCopyableTypeDecoration(IRInst* value) + { + addDecoration(value, kIROp_NonCopyableTypeDecoration); + } + /// Add a decoration that indicates that the given `inst` depends on the given `dependency`. /// /// This decoration can be used to ensure that a value that an instruction diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index de9071adf..14e79560e 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -445,6 +445,7 @@ static void cloneExtraDecorationsFromInst( case kIROp_UserDefinedBackwardDerivativeDecoration: case kIROp_PrimalSubstituteDecoration: case kIROp_IntrinsicOpDecoration: + case kIROp_NonCopyableTypeDecoration: if (!clonedInst->findDecorationImpl(decoration->getOp())) { cloneInst(context, builder, decoration); diff --git a/source/slang/slang-ir-lower-witness-lookup.cpp b/source/slang/slang-ir-lower-witness-lookup.cpp index fd86e1d3c..f87633656 100644 --- a/source/slang/slang-ir-lower-witness-lookup.cpp +++ b/source/slang/slang-ir-lower-witness-lookup.cpp @@ -387,7 +387,7 @@ struct WitnessLookupLoweringContext bool changed = false; for (auto bb : func->getBlocks()) { - for (auto inst : bb->getChildren()) + for (auto inst : bb->getModifiableChildren()) { if (auto witnessLookupInst = as<IRLookupWitnessMethod>(inst)) { diff --git a/source/slang/slang-ir-propagate-func-properties.cpp b/source/slang/slang-ir-propagate-func-properties.cpp index 7ce4bfc80..7b29aaf14 100644 --- a/source/slang/slang-ir-propagate-func-properties.cpp +++ b/source/slang/slang-ir-propagate-func-properties.cpp @@ -7,7 +7,112 @@ namespace Slang { -bool propagateFuncProperties(IRModule* module) +class FuncPropertyPropagationContext +{ +public: + virtual bool canProcess(IRFunc* f) = 0; + virtual bool propagate(IRBuilder& builder, IRFunc* func) = 0; +}; + +class ReadNoneFuncPropertyPropagationContext : public FuncPropertyPropagationContext +{ +public: + virtual bool canProcess(IRFunc* f) override + { + // If the func has already been marked with any decorations, skip. + for (auto decoration : f->getDecorations()) + { + switch (decoration->getOp()) + { + case kIROp_ReadNoneDecoration: + case kIROp_TargetIntrinsicDecoration: + return false; + } + } + return true; + } + + virtual bool propagate(IRBuilder& builder, IRFunc* f) override + { + bool hasSideEffectCall = false; + for (auto block : f->getBlocks()) + { + for (auto inst : block->getChildren()) + { + // Is this inst known to not have global side effect/analyzable? + if (inst->mightHaveSideEffects()) + { + switch (inst->getOp()) + { + case kIROp_ifElse: + case kIROp_unconditionalBranch: + case kIROp_Switch: + case kIROp_Return: + case kIROp_loop: + case kIROp_Call: + case kIROp_Param: + case kIROp_Unreachable: + case kIROp_Store: + case kIROp_SwizzledStore: + break; + default: + // We have a inst that has side effect and is not understood by this method. + // e.g. bufferStore, discard, etc. + hasSideEffectCall = true; + break; + } + } + + if (auto call = as<IRCall>(inst)) + { + auto callee = getResolvedInstForDecorations(call->getCallee()); + switch (callee->getOp()) + { + default: + // We are calling an unknown function, so we have to assume + // there are side effects in the call. + hasSideEffectCall = true; + break; + case kIROp_Func: + if (!callee->findDecoration<IRReadNoneDecoration>()) + { + hasSideEffectCall = true; + break; + } + } + } + + // Do any operands defined have pointer type of global or + // unknown source? Passing them into a readNone callee may cause + // side effects that breaks the readNone property. + for (UInt o = 0; o < inst->getOperandCount(); o++) + { + auto operand = inst->getOperand(o); + if (as<IRConstant>(operand)) + continue; + if (as<IRType>(operand)) + continue; + if (isGlobalOrUnknownMutableAddress(f, operand)) + { + hasSideEffectCall = true; + break; + } + break; + } + } + if (hasSideEffectCall) + break; + } + if (!hasSideEffectCall) + { + builder.addDecoration(f, kIROp_ReadNoneDecoration); + return true; + } + return false; + } +}; + +bool propagateFuncPropertiesImpl(IRModule* module, FuncPropertyPropagationContext* context) { bool result = false; List<IRFunc*> workList; @@ -61,7 +166,7 @@ bool propagateFuncProperties(IRModule* module) } if (auto func = as<IRFunc>(inst)) { - if (func->findDecoration<IRReadNoneDecoration>()) + if (context->canProcess(func)) { addCallersToWorkList(func); } @@ -87,101 +192,139 @@ bool propagateFuncProperties(IRModule* module) for (Index i = 0; i < workList.getCount(); i++) { auto f = workList[i]; - bool hasSideEffectCall = false; - if (f->findDecoration<IRReadNoneDecoration>()) + if (!context->canProcess(f)) continue; + // Never propagate to functions without a body. if (f->getFirstBlock() == nullptr) continue; - if (f->findDecoration<IRTargetIntrinsicDecoration>()) - continue; - for (auto block : f->getBlocks()) + + if (context->propagate(builder, f)) + { + addCallersToWorkList(f); + changed = true; + } + } + result |= changed; + if (!changed) + break; + } + return result; +} + +class NoSideEffectFuncPropertyPropagationContext : public FuncPropertyPropagationContext +{ +public: + virtual bool canProcess(IRFunc* f) override + { + // If the func has already been marked with any decorations, skip. + for (auto decoration : f->getDecorations()) + { + switch (decoration->getOp()) { - for (auto inst : block->getChildren()) + case kIROp_ReadNoneDecoration: + case kIROp_NoSideEffectDecoration: + case kIROp_TargetIntrinsicDecoration: + return false; + } + } + return true; + } + + virtual bool propagate(IRBuilder& builder, IRFunc* f) override + { + bool hasSideEffectCall = false; + for (auto block : f->getBlocks()) + { + for (auto inst : block->getChildren()) + { + // Is this inst known to not have global side effect/analyzable? + if (inst->mightHaveSideEffects()) { - // Is this inst known to not have global side effect/analyzable? - if (inst->mightHaveSideEffects()) + switch (inst->getOp()) { - switch (inst->getOp()) - { - case kIROp_ifElse: - case kIROp_unconditionalBranch: - case kIROp_Switch: - case kIROp_Return: - case kIROp_loop: - case kIROp_Store: - case kIROp_Call: - case kIROp_Param: - case kIROp_Unreachable: - break; - default: - // We have a inst that has side effect and is not understood by this method. - // e.g. bufferStore, discard, etc. - hasSideEffectCall = true; - break; - } + case kIROp_ifElse: + case kIROp_unconditionalBranch: + case kIROp_Switch: + case kIROp_Return: + case kIROp_loop: + case kIROp_Call: + case kIROp_Param: + case kIROp_Unreachable: + case kIROp_Store: + case kIROp_SwizzledStore: + break; + default: + // We have a inst that has side effect and is not understood by this method. + // e.g. bufferStore, discard, etc. + hasSideEffectCall = true; + break; } + } + else + { + // A side effect free inst can't generate side effects for the function. + continue; + } - if (auto call = as<IRCall>(inst)) + if (auto call = as<IRCall>(inst)) + { + auto callee = getResolvedInstForDecorations(call->getCallee()); + switch (callee->getOp()) { - auto callee = getResolvedInstForDecorations(call->getCallee()); - switch (callee->getOp()) + default: + // We are calling an unknown function, so we have to assume + // there are side effects in the call. + hasSideEffectCall = true; + break; + case kIROp_Func: + if (!callee->findDecoration<IRReadNoneDecoration>() && + !callee->findDecoration<IRNoSideEffectDecoration>()) { - default: - // We are calling an unknown function, so we have to assume - // there are side effects in the call. hasSideEffectCall = true; break; - case kIROp_Func: - if (!callee->findDecoration<IRReadNoneDecoration>()) - { - hasSideEffectCall = true; - break; - } } } - - // Are any operands defined in global scope? - for (UInt o = 0; o < inst->getOperandCount(); o++) + } + + // Do any operands defined have pointer type of global or + // unknown source? Passing them into a NoSideEffect callee may cause + // side effects that breaks the NoSideEffect property. + for (UInt o = 0; o < inst->getOperandCount(); o++) + { + auto operand = inst->getOperand(o); + if (as<IRConstant>(operand)) + continue; + if (as<IRType>(operand)) + continue; + if (isGlobalOrUnknownMutableAddress(f, operand)) { - auto operand = inst->getOperand(o); - if (getParentFunc(operand) == f) - continue; - if (as<IRConstant>(operand)) - continue; - if (as<IRType>(operand)) - continue; - switch (operand->getOp()) - { - case kIROp_Specialize: - case kIROp_LookupWitness: - case kIROp_StructKey: - case kIROp_WitnessTable: - case kIROp_WitnessTableEntry: - case kIROp_undefined: - case kIROp_Func: - continue; - default: - break; - } hasSideEffectCall = true; break; } - } - if (hasSideEffectCall) break; + } } - if (!hasSideEffectCall) - { - builder.addDecoration(f, kIROp_ReadNoneDecoration); - addCallersToWorkList(f); - changed = true; - } + if (hasSideEffectCall) + break; } - result |= changed; - if (!changed) - break; + if (!hasSideEffectCall) + { + builder.addDecoration(f, kIROp_NoSideEffectDecoration); + return true; + } + return false; } - return result; +}; + +bool propagateFuncProperties(IRModule* module) +{ + ReadNoneFuncPropertyPropagationContext readNoneContext; + bool changed = propagateFuncPropertiesImpl(module, &readNoneContext); + + NoSideEffectFuncPropertyPropagationContext noSideEffectContext; + changed|= propagateFuncPropertiesImpl(module, &noSideEffectContext); + + return changed; } } diff --git a/source/slang/slang-ir-simplify-cfg.cpp b/source/slang/slang-ir-simplify-cfg.cpp index 797e5c9ea..c37284dce 100644 --- a/source/slang/slang-ir-simplify-cfg.cpp +++ b/source/slang/slang-ir-simplify-cfg.cpp @@ -6,6 +6,7 @@ #include "slang-ir-restructure.h" #include "slang-ir-util.h" #include "slang-ir-loop-unroll.h" +#include "slang-ir-reachability.h" namespace Slang { @@ -103,37 +104,59 @@ static bool doesLoopHasSideEffect(IRGlobalValueWithCode* func, IRLoop* loopInst) HashSet<IRBlock*> loopBlocks; for (auto b : blocks) loopBlocks.add(b); - auto addressHasOutOfLoopUses = [&](IRInst* addr) + + ReachabilityContext reachability = {}; + + // Construct a map from a root address to all derived addresses. + Dictionary<IRInst*, List<IRInst*>> relatedAddrMap; + for (auto b : func->getBlocks()) { - // The entire access chain of `addr` must have no uses outside the loop. - // The root variable must be a local var. - for (auto chainNode = addr; chainNode;) + for (auto inst : b->getChildren()) { - if (getParentFunc(chainNode) != func) - return true; - for (auto use = chainNode->firstUse; use; use = use->nextUse) + if (as<IRPtrTypeBase>(inst->getDataType())) { - if (!loopBlocks.contains(as<IRBlock>(use->getUser()->getParent()))) - return true; + auto root = getRootAddr(inst); + if (!root) continue; + auto list = relatedAddrMap.tryGetValue(root); + if (!list) + { + relatedAddrMap.add(root, List<IRInst*>()); + list = relatedAddrMap.tryGetValue(root); + } + list->add(inst); } - switch (chainNode->getOp()) - { - case kIROp_GetElementPtr: - case kIROp_FieldAddress: - chainNode = chainNode->getOperand(0); + } + } + + auto addressHasOutOfLoopUses = [&](IRInst* addr) + { + auto rootAddr = getRootAddr(addr); + if (isGlobalOrUnknownMutableAddress(func, rootAddr)) + return true; + if (as<IRParam>(rootAddr)) + return true; + + // If we can't find the address from our map, we conservatively assume it is an unknown address. + auto relatedAddrs = relatedAddrMap.tryGetValue(getRootAddr(addr)); + if (!relatedAddrs) + return true; + + // For all related address of `addr` that may alias with it, we check their uses. + for (auto relatedAddr : *relatedAddrs) + { + if (!canAddressesPotentiallyAlias(func, relatedAddr, addr)) continue; - case kIROp_Var: - if (auto rate = chainNode->getFullType()->getRate()) + for (auto use = relatedAddr->firstUse; use; use = use->nextUse) + { + if (!loopBlocks.contains(as<IRBlock>(use->getUser()->getParent()))) { - if (!as<IRConstExprRate>(rate)) + // Is this use reachable from the loop header? + if (reachability.isInstReachable(loopInst, use->getUser())) return true; } - break; - default: - return true; } - break; } + return false; }; @@ -267,16 +290,8 @@ static bool isTrivialIfElseBranch(IRIfElse* condBranch, IRBlock* branchBlock) return false; } -static bool arePhiArgsEquivalentInBranches(IRIfElse* ifElse) +static bool arePhiArgsEquivalentInBranchesImpl(IRBlock* branch1, IRBlock* branch2, IRBlock* afterBlock) { - // If one of the branch target is afterBlock itself, and the other branch - // is a trivial block that jumps into the afterBlock, this if-else is trivial. - // In this case the argCount must be 0 because a block with phi parameters can't - // be used as targets in a conditional branch. - auto branch1 = ifElse->getTrueBlock(); - auto branch2 = ifElse->getFalseBlock(); - auto afterBlock = ifElse->getAfterBlock(); - if (branch1 == afterBlock) return true; if (branch2 == afterBlock) return true; @@ -291,7 +306,7 @@ static bool arePhiArgsEquivalentInBranches(IRIfElse* ifElse) // This should never happen, return false now to be safe. return false; } - + for (UInt i = 0; i < branchInst1->getArgCount(); i++) { if (branchInst1->getArg(i) != branchInst2->getArg(i)) @@ -303,6 +318,19 @@ static bool arePhiArgsEquivalentInBranches(IRIfElse* ifElse) return true; } +static bool arePhiArgsEquivalentInBranches(IRIfElse* ifElse) +{ + // If one of the branch target is afterBlock itself, and the other branch + // is a trivial block that jumps into the afterBlock, this if-else is trivial. + // In this case the argCount must be 0 because a block with phi parameters can't + // be used as targets in a conditional branch. + auto branch1 = ifElse->getTrueBlock(); + auto branch2 = ifElse->getFalseBlock(); + auto afterBlock = ifElse->getAfterBlock(); + + return arePhiArgsEquivalentInBranchesImpl(branch1, branch2, afterBlock); +} + static bool isTrivialIfElse(IRIfElse* condBranch, bool& isTrueBranchTrivial, bool& isFalseBranchTrivial) { isTrueBranchTrivial = isTrivialIfElseBranch(condBranch, condBranch->getTrueBlock()); @@ -315,6 +343,62 @@ static bool isTrivialIfElse(IRIfElse* condBranch, bool& isTrueBranchTrivial, boo return false; } +// Return the true of the switch branch block if the branch is a trivial jump +// to after block with no other insts. +static bool isTrivialSwitchBranch(IRSwitch* switchInst, IRBlock* branchBlock) +{ + if (branchBlock != switchInst->getBreakLabel()) + { + if (auto br = as<IRUnconditionalBranch>(branchBlock->getFirstOrdinaryInst())) + { + if (br->getTargetBlock() == switchInst->getBreakLabel() && br->getOp() == kIROp_unconditionalBranch) + { + return true; + } + } + } + else + { + return true; + } + return false; +} + +static bool arePhiArgsEquivalentInBranches(IRSwitch* switchInst) +{ + ShortList<IRBlock*> jumpTargets; + if (switchInst->getDefaultLabel()) + jumpTargets.add(switchInst->getDefaultLabel()); + for (UInt i = 0; i < switchInst->getCaseCount(); i++) + { + jumpTargets.add(switchInst->getCaseLabel(i)); + } + if (jumpTargets.getCount() == 0) + return true; + for (Index i = 1; i < jumpTargets.getCount(); i++) + { + auto branch1 = jumpTargets[0]; + auto branch2 = jumpTargets[i]; + auto afterBlock = switchInst->getBreakLabel(); + + if (!arePhiArgsEquivalentInBranchesImpl(branch1, branch2, afterBlock)) + return false; + } + return true; +} + +static bool isTrivialSwitch(IRSwitch* switchBranch) +{ + for (UInt i = 0; i < switchBranch->getCaseCount(); i++) + { + if (!isTrivialSwitchBranch(switchBranch, switchBranch->getCaseLabel(i))) + return false; + } + if (!isTrivialSwitchBranch(switchBranch, switchBranch->getDefaultLabel())) + return false; + return true; +} + static bool trySimplifyIfElse(IRBuilder& builder, IRIfElse* ifElseInst) { bool isTrueBranchTrivial = false; @@ -338,6 +422,29 @@ static bool trySimplifyIfElse(IRBuilder& builder, IRIfElse* ifElseInst) return false; } +static bool trySimplifySwitch(IRBuilder& builder, IRSwitch* switchInst) +{ + if (!isTrivialSwitch(switchInst)) + return false; + if (switchInst->getCaseCount() == 0) + return false; + + auto termInst = as<IRUnconditionalBranch>(switchInst->getCaseLabel(0)->getTerminator()); + if (!termInst) + return false; + + if (!arePhiArgsEquivalentInBranches(switchInst)) + return false; + + List<IRInst*> args; + for (UInt i = 0; i < termInst->getArgCount(); i++) + args.add(termInst->getArg(i)); + builder.setInsertBefore(switchInst); + builder.emitBranch(switchInst->getBreakLabel(), (Int)args.getCount(), args.getBuffer()); + switchInst->removeAndDeallocate(); + return true; +} + static bool isTrueLit(IRInst* lit) { if (auto boolLit = as<IRBoolLit>(lit)) @@ -582,6 +689,10 @@ static bool processFunc(IRGlobalValueWithCode* func) { changed |= trySimplifyIfElse(builder, condBranch); } + else if (auto switchBranch = as<IRSwitch>(block->getTerminator())) + { + changed |= trySimplifySwitch(builder, switchBranch); + } // If `block` does not end with an unconditional branch, bail. if (block->getTerminator()->getOp() != kIROp_unconditionalBranch) diff --git a/source/slang/slang-ir-specialize-function-call.cpp b/source/slang/slang-ir-specialize-function-call.cpp index b0ad58f8c..9b7cdaea6 100644 --- a/source/slang/slang-ir-specialize-function-call.cpp +++ b/source/slang/slang-ir-specialize-function-call.cpp @@ -822,7 +822,7 @@ struct FunctionParameterSpecializationContext { decoration->removeAndDeallocate(); } - else if (as<IRReadNoneDecoration>(decoration)) + else if (as<IRReadNoneDecoration>(decoration) || as<IRNoSideEffectDecoration>(decoration)) { // After specialization, the function may no longer be side effect free // because the parameter we substituted in maybe a global param. 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; diff --git a/source/slang/slang-ir-util.h b/source/slang/slang-ir-util.h index 492a9f312..9fd6dd972 100644 --- a/source/slang/slang-ir-util.h +++ b/source/slang/slang-ir-util.h @@ -167,10 +167,18 @@ bool canAddressesPotentiallyAlias(IRGlobalValueWithCode* func, IRInst* addr1, IR String dumpIRToString(IRInst* root); -// Returns whether a call insts can be treated as a pure functional inst -// (no writes to memory, no side effects). +// Returns whether a call insts can be treated as a pure functional inst, and thus can be +// DCE'd and deduplicated. +// (no writes to memory, no reads from unknown memory, no side effects). bool isPureFunctionalCall(IRCall* callInst); +// Returns whether a call insts can be treated as a pure functional inst, and thus can be +// DCE'd (but not necessarily deduplicated). +// (no side effects). +bool isSideEffectFreeFunctionalCall(IRCall* call); + +bool doesCalleeHaveSideEffect(IRInst* callee); + bool isPtrLikeOrHandleType(IRInst* type); bool canInstHaveSideEffectAtAddress(IRGlobalValueWithCode* func, IRInst* inst, IRInst* addr); @@ -205,6 +213,7 @@ void removePhiArgs(IRInst* phiParam); int getParamIndexInBlock(IRParam* paramInst); +bool isGlobalOrUnknownMutableAddress(IRGlobalValueWithCode* parentFunc, IRInst* inst); } #endif diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 78b62265b..6adf8ee1c 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -7157,7 +7157,7 @@ namespace Slang // will treat it so, by-passing all other checks. if (call->findDecoration<IRNoSideEffectDecoration>()) return false; - return !isPureFunctionalCall(call); + return !isSideEffectFreeFunctionalCall(call); } break; diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index de2d5aff2..ad338709d 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -7664,7 +7664,11 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> irAggType->moveToEnd(); addTargetIntrinsicDecorations(irAggType, decl); - + for (auto modifier : decl->modifiers) + { + if (as<NonCopyableTypeAttribute>(modifier)) + subBuilder->addNonCopyableTypeDecoration(irAggType); + } return LoweredValInfo::simple(finishOuterGenerics(subBuilder, irAggType, outerGeneric)); } @@ -8779,6 +8783,10 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> { getBuilder()->addSimpleDecoration<IRReadNoneDecoration>(irFunc); } + else if (as<NoSideEffectAttribute>(modifier)) + { + getBuilder()->addSimpleDecoration<IRNoSideEffectDecoration>(irFunc); + } else if (as<EarlyDepthStencilAttribute>(modifier)) { getBuilder()->addSimpleDecoration<IREarlyDepthStencilDecoration>(irFunc); |
