summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang/core.meta.slang6
-rw-r--r--source/slang/diff.meta.slang40
-rw-r--r--source/slang/hlsl.meta.slang64
-rw-r--r--source/slang/slang-ast-modifier.h9
-rw-r--r--source/slang/slang-ir-addr-inst-elimination.cpp16
-rw-r--r--source/slang/slang-ir-addr-inst-elimination.h5
-rw-r--r--source/slang/slang-ir-autodiff-fwd.cpp14
-rw-r--r--source/slang/slang-ir-autodiff-primal-hoist.cpp16
-rw-r--r--source/slang/slang-ir-inst-defs.h3
-rw-r--r--source/slang/slang-ir-insts.h6
-rw-r--r--source/slang/slang-ir-link.cpp1
-rw-r--r--source/slang/slang-ir-lower-witness-lookup.cpp2
-rw-r--r--source/slang/slang-ir-propagate-func-properties.cpp293
-rw-r--r--source/slang/slang-ir-simplify-cfg.cpp173
-rw-r--r--source/slang/slang-ir-specialize-function-call.cpp2
-rw-r--r--source/slang/slang-ir-util.cpp175
-rw-r--r--source/slang/slang-ir-util.h13
-rw-r--r--source/slang/slang-ir.cpp2
-rw-r--r--source/slang/slang-lower-to-ir.cpp10
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);