From c07f60af241b1b0f7b7eba62c65d9fe750f8f3b7 Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Fri, 9 Nov 2018 13:24:28 -0800 Subject: Update Vulkan ray tracing support to final extension spec (#717) * Update version of glslang used * Update VK raytracing support for final extension spec A lot of this change is just plain renaming: The `NVX` suffixes become just `NV`, and the extension name changes from `GL_NVX_raytracing` to `GL_NV_ray_tracing`. The Slang standard library and the GLSL baselines for the tests are consistently updated. The other detail is that the final spec requires the "payload" identifier in a `traceNV()` call to be a compile-time constant, which means it cannot be defined as a local variable first, as in: ```glsl int payloadID = 0; traceNV(..., payloadID); // ERROR ``` In terms of how the original support was implemented, the payload ID is being computed via a special builtin function that maps each global GLSL payload variable to a unique ID. There are a few ways we could try to resolve the problem here: 1. We could aspire to put our equivalent of the `constexpr` modifier on the output of the function, so that the GLSL variable gets declared `const` and thus fits the GLSL rules for a constant expression. 2. We could introduce a pass to replace the payload-location instructions with literal integers. 3. We could use a special-purpose instruction instead of a builtin function call, and have that instruction indicate that it doesn't have side effects (so it can be folded into the call site) 4. We could somehow mark the builtin function as not having side effects. We choose option (4) simply because it provides a feature that could have other applications. This change adds a `[__readNone]` attribute that can be applied to function declarations to express a promise on the part of the programmer that the given function has no side effects and computes its result strictly from the bits of its input arguments (and not things they point to, etc.). This mirrors an equivalent function attribute in LLVM. We mark the function that computes a ray payload location with this attribute, and propagate the attribute through the layers of the IR, so that when the emit logic asks if an operation has side effects (to see if it can be folded into the arguments of a subsequent expression), we get an affirmative response. This change should get all of the features that were present in the experiemntal `NVX` extension working with the final extension spec. It does not address callable shaders, which will come as a subsequent change. --- source/slang/core.meta.slang | 6 +++++ source/slang/core.meta.slang.h | 6 +++++ source/slang/emit.cpp | 20 ++++++++-------- source/slang/hlsl.meta.slang | 47 ++++++++++++++++++------------------ source/slang/hlsl.meta.slang.h | 47 ++++++++++++++++++------------------ source/slang/ir-insts.h | 10 ++++++++ source/slang/ir-serialize.cpp | 7 ++++++ source/slang/ir.cpp | 54 +++++++++++++++++++++++++++++++++++++++++- source/slang/ir.h | 13 ++++++++-- source/slang/lower-to-ir.cpp | 5 ++++ source/slang/modifier-defs.h | 8 +++++++ 11 files changed, 164 insertions(+), 59 deletions(-) (limited to 'source') diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index 0502c9bf6..26bf1112d 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -1238,3 +1238,9 @@ attribute_syntax [__vulkanHitAttributes] : VulkanHitAttributesAttribute; __attributeTarget(FunctionDeclBase) attribute_syntax [mutating] : MutatingAttribute; + + /// Indicates that a function computes its result as a function of its arguments without loading/storing any memory or other state. + /// + /// This is equivalent to the LLVM `readnone` function attribute. +__attributeTarget(FunctionDeclBase) +attribute_syntax [__readNone] : ReadNoneAttribute; diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h index 97a2d1edd..5380bd90f 100644 --- a/source/slang/core.meta.slang.h +++ b/source/slang/core.meta.slang.h @@ -1256,3 +1256,9 @@ SLANG_RAW("attribute_syntax [__vulkanHitAttributes] : VulkanHitAttributesAttribu SLANG_RAW("\n") SLANG_RAW("__attributeTarget(FunctionDeclBase)\n") SLANG_RAW("attribute_syntax [mutating] : MutatingAttribute;\n") +SLANG_RAW("\n") +SLANG_RAW(" /// Indicates that a function computes its result as a function of its arguments without loading/storing any memory or other state.\n") +SLANG_RAW(" ///\n") +SLANG_RAW(" /// This is equivalent to the LLVM `readnone` function attribute.\n") +SLANG_RAW("__attributeTarget(FunctionDeclBase)\n") +SLANG_RAW("attribute_syntax [__readNone] : ReadNoneAttribute;\n") diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index bb9a6ad8b..5c39dd50c 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -1168,8 +1168,8 @@ struct EmitVisitor switch (type->op) { case kIROp_RaytracingAccelerationStructureType: - requireGLSLExtension("GL_NVX_raytracing"); - Emit("accelerationStructureNVX"); + requireGLSLExtension("GL_NV_ray_tracing"); + Emit("accelerationStructureNV"); break; // TODO: These "translations" are obviously wrong for GLSL. @@ -3332,17 +3332,17 @@ struct EmitVisitor case 'T': { // The `$XT` case handles selecting between - // the `gl_HitTNVX` and `gl_RayTmaxNVX` builtins, + // the `gl_HitTNV` and `gl_RayTmaxNV` builtins, // based on what stage we are using: switch( ctx->shared->entryPoint->getStage() ) { default: - Emit("gl_RayTmaxNVX"); + Emit("gl_RayTmaxNV"); break; case Stage::AnyHit: case Stage::ClosestHit: - Emit("gl_HitTNVX"); + Emit("gl_HitTNV"); break; } } @@ -5363,11 +5363,11 @@ struct EmitVisitor emit("layout(location = "); Emit(getRayPayloadLocation(ctx, varDecl)); emit(")\n"); - emit("rayPayloadNVX\n"); + emit("rayPayloadNV\n"); } if(varDecl->findDecoration()) { - emit("hitAttributeNVX\n"); + emit("hitAttributeNV\n"); } if (!layout) @@ -5516,13 +5516,13 @@ struct EmitVisitor case LayoutResourceKind::RayPayload: { - emit("rayPayloadInNVX "); + emit("rayPayloadInNV "); } break; case LayoutResourceKind::HitAttributes: { - emit("hitAttributeNVX "); + emit("hitAttributeNV "); } break; @@ -6535,7 +6535,7 @@ String emitEntryPoint( case Stage::RayGeneration: if( target == CodeGenTarget::GLSL ) { - requireGLSLExtension(&context.shared->extensionUsageTracker, "GL_NVX_raytracing"); + requireGLSLExtension(&context.shared->extensionUsageTracker, "GL_NV_ray_tracing"); requireGLSLVersionImpl(&context.shared->extensionUsageTracker, ProfileVersion::GLSL_460); } break; diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index c666dadd3..56741f6b3 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -1369,8 +1369,8 @@ void TraceRay( RayDesc Ray, inout payload_t Payload); -__target_intrinsic(glsl, "traceNVX") -void __traceNVX( +__target_intrinsic(glsl, "traceNV") +void __traceNV( RaytracingAccelerationStructure AccelerationStructure, uint RayFlags, uint InstanceInclusionMask, @@ -1391,6 +1391,7 @@ void __traceNVX( // __generic __target_intrinsic(glsl, "$XP") +[__readNone] int __rayPayloadLocation(Payload payload); __generic @@ -1409,7 +1410,7 @@ void TraceRay( static payload_t p; p = Payload; - __traceNVX( + __traceNV( AccelerationStructure, RayFlags, InstanceInclusionMask, @@ -1427,8 +1428,8 @@ void TraceRay( // 10.3.3 bool ReportHit(float tHit, uint hitKind, A attributes); -__target_intrinsic(glsl, "reportIntersectionNVX") -bool __reportIntersectionNVX(float tHit, uint hitKind); +__target_intrinsic(glsl, "reportIntersectionNV") +bool __reportIntersectionNV(float tHit, uint hitKind); __generic __specialized_for_target(glsl) @@ -1438,15 +1439,15 @@ bool ReportHit(float tHit, uint hitKind, A attributes) static A a; a = attributes; - return __reportIntersectionNVX(tHit, hitKind); + return __reportIntersectionNV(tHit, hitKind); } // 10.3.4 -__target_intrinsic(glsl, ignoreIntersectionNVX) +__target_intrinsic(glsl, ignoreIntersectionNV) void IgnoreHit(); // 10.3.5 -__target_intrinsic(glsl, terminateRayNVX) +__target_intrinsic(glsl, terminateRayNV) void AcceptHitAndEndSearch(); // 10.4 - System Values and Special Semantics @@ -1456,25 +1457,25 @@ void AcceptHitAndEndSearch(); // 10.4.1 - Ray Dispatch System Values -__target_intrinsic(glsl, "uvec3(gl_LaunchIDNVX, 0)") +__target_intrinsic(glsl, "(gl_LaunchIDNV)") uint3 DispatchRaysIndex(); -__target_intrinsic(glsl, "uvec3(gl_LaunchSizeNVX, 0)") +__target_intrinsic(glsl, "(gl_LaunchSizeNV)") uint3 DispatchRaysDimensions(); // 10.4.2 - Ray System Values -__target_intrinsic(glsl, "(gl_WorldRayOriginNVX)") +__target_intrinsic(glsl, "(gl_WorldRayOriginNV)") float3 WorldRayOrigin(); -__target_intrinsic(glsl, "(gl_WorldRayDirectionNVX)") +__target_intrinsic(glsl, "(gl_WorldRayDirectionNV)") float3 WorldRayDirection(); -__target_intrinsic(glsl, "(gl_RayTminNVX)") +__target_intrinsic(glsl, "(gl_RayTminNV)") float RayTMin(); // Note: The `RayTCurrent()` intrinsic should translate to -// either `gl_HitTNVX` (for hit shaders) or `gl_RayTmaxNVX` +// either `gl_HitTNV` (for hit shaders) or `gl_RayTmaxNV` // (for intersection shaders). Right now we are handling this // during code emission, for simplicity. // @@ -1486,12 +1487,12 @@ float RayTMin(); __target_intrinsic(glsl, "$XT") float RayTCurrent(); -__target_intrinsic(glsl, "(gl_RayFlagsNVX)") +__target_intrinsic(glsl, "(gl_IncomingRayFlagsNV)") uint RayFlags(); // 10.4.3 - Primitive/Object Space System Values -__target_intrinsic(glsl, "(gl_InstanceCustomIndexNVX)") +__target_intrinsic(glsl, "(gl_InstanceCustomIndexNV)") uint InstanceIndex(); __target_intrinsic(glsl, "(gl_InstanceID)") @@ -1500,22 +1501,22 @@ uint InstanceID(); __target_intrinsic(glsl, "(gl_PrimitiveID)") uint PrimitiveIndex(); -__target_intrinsic(glsl, "(gl_ObjectRayOriginNVX)") +__target_intrinsic(glsl, "(gl_ObjectRayOriginNV)") float3 ObjectRayOrigin(); -__target_intrinsic(glsl, "(gl_ObjectRayDirectionNVX)") +__target_intrinsic(glsl, "(gl_ObjectRayDirectionNV)") float3 ObjectRayDirection(); -__target_intrinsic(glsl, "transpose(gl_ObjectToWorldNVX)") +__target_intrinsic(glsl, "transpose(gl_ObjectToWorldNV)") float3x4 ObjectToWorld3x4(); -__target_intrinsic(glsl, "transpose(gl_WorldToObjectNVX)") +__target_intrinsic(glsl, "transpose(gl_WorldToObjectNV)") float3x4 WorldToObject3x4(); -__target_intrinsic(glsl, "(gl_ObjectToWorldNVX)") +__target_intrinsic(glsl, "(gl_ObjectToWorldNV)") float4x3 ObjectToWorld4x3(); -__target_intrinsic(glsl, "(gl_WorldToObjectNVX)") +__target_intrinsic(glsl, "(gl_WorldToObjectNV)") float4x3 WorldToObject4x3(); // Note: The provisional DXR spec included these unadorned @@ -1531,5 +1532,5 @@ float3x4 ObjectToWorld() { return ObjectToWorld3x4(); } float3x4 WorldToObject() { return WorldToObject3x4(); } // 10.4.4 - Hit Specific System values -__target_intrinsic(glsl, "(gl_HitKindNVX)") +__target_intrinsic(glsl, "(gl_HitKindNV)") uint HitKind(); diff --git a/source/slang/hlsl.meta.slang.h b/source/slang/hlsl.meta.slang.h index 2db710ccb..5e00c2719 100644 --- a/source/slang/hlsl.meta.slang.h +++ b/source/slang/hlsl.meta.slang.h @@ -1417,8 +1417,8 @@ SLANG_RAW(" uint MissShaderIndex,\n") SLANG_RAW(" RayDesc Ray,\n") SLANG_RAW(" inout payload_t Payload);\n") SLANG_RAW("\n") -SLANG_RAW("__target_intrinsic(glsl, \"traceNVX\")\n") -SLANG_RAW("void __traceNVX(\n") +SLANG_RAW("__target_intrinsic(glsl, \"traceNV\")\n") +SLANG_RAW("void __traceNV(\n") SLANG_RAW(" RaytracingAccelerationStructure AccelerationStructure,\n") SLANG_RAW(" uint RayFlags,\n") SLANG_RAW(" uint InstanceInclusionMask,\n") @@ -1439,6 +1439,7 @@ SLANG_RAW("// syntax works in a pinch.\n") SLANG_RAW("//\n") SLANG_RAW("__generic\n") SLANG_RAW("__target_intrinsic(glsl, \"$XP\")\n") +SLANG_RAW("[__readNone]\n") SLANG_RAW("int __rayPayloadLocation(Payload payload);\n") SLANG_RAW("\n") SLANG_RAW("__generic\n") @@ -1457,7 +1458,7 @@ SLANG_RAW(" [__vulkanRayPayload]\n") SLANG_RAW(" static payload_t p;\n") SLANG_RAW("\n") SLANG_RAW(" p = Payload;\n") -SLANG_RAW(" __traceNVX(\n") +SLANG_RAW(" __traceNV(\n") SLANG_RAW(" AccelerationStructure,\n") SLANG_RAW(" RayFlags,\n") SLANG_RAW(" InstanceInclusionMask,\n") @@ -1475,8 +1476,8 @@ SLANG_RAW("\n") SLANG_RAW("// 10.3.3\n") SLANG_RAW("bool ReportHit(float tHit, uint hitKind, A attributes);\n") SLANG_RAW("\n") -SLANG_RAW("__target_intrinsic(glsl, \"reportIntersectionNVX\")\n") -SLANG_RAW("bool __reportIntersectionNVX(float tHit, uint hitKind);\n") +SLANG_RAW("__target_intrinsic(glsl, \"reportIntersectionNV\")\n") +SLANG_RAW("bool __reportIntersectionNV(float tHit, uint hitKind);\n") SLANG_RAW("\n") SLANG_RAW("__generic\n") SLANG_RAW("__specialized_for_target(glsl)\n") @@ -1486,15 +1487,15 @@ SLANG_RAW(" [__vulkanHitAttributes]\n") SLANG_RAW(" static A a;\n") SLANG_RAW("\n") SLANG_RAW(" a = attributes;\n") -SLANG_RAW(" return __reportIntersectionNVX(tHit, hitKind);\n") +SLANG_RAW(" return __reportIntersectionNV(tHit, hitKind);\n") SLANG_RAW("}\n") SLANG_RAW("\n") SLANG_RAW("// 10.3.4\n") -SLANG_RAW("__target_intrinsic(glsl, ignoreIntersectionNVX)\n") +SLANG_RAW("__target_intrinsic(glsl, ignoreIntersectionNV)\n") SLANG_RAW("void IgnoreHit();\n") SLANG_RAW("\n") SLANG_RAW("// 10.3.5\n") -SLANG_RAW("__target_intrinsic(glsl, terminateRayNVX)\n") +SLANG_RAW("__target_intrinsic(glsl, terminateRayNV)\n") SLANG_RAW("void AcceptHitAndEndSearch();\n") SLANG_RAW("\n") SLANG_RAW("// 10.4 - System Values and Special Semantics\n") @@ -1504,25 +1505,25 @@ SLANG_RAW("// they can only be accessed from specific stages.\n") SLANG_RAW("\n") SLANG_RAW("// 10.4.1 - Ray Dispatch System Values\n") SLANG_RAW("\n") -SLANG_RAW("__target_intrinsic(glsl, \"uvec3(gl_LaunchIDNVX, 0)\")\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_LaunchIDNV)\")\n") SLANG_RAW("uint3 DispatchRaysIndex();\n") SLANG_RAW("\n") -SLANG_RAW("__target_intrinsic(glsl, \"uvec3(gl_LaunchSizeNVX, 0)\")\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_LaunchSizeNV)\")\n") SLANG_RAW("uint3 DispatchRaysDimensions();\n") SLANG_RAW("\n") SLANG_RAW("// 10.4.2 - Ray System Values\n") SLANG_RAW("\n") -SLANG_RAW("__target_intrinsic(glsl, \"(gl_WorldRayOriginNVX)\")\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_WorldRayOriginNV)\")\n") SLANG_RAW("float3 WorldRayOrigin();\n") SLANG_RAW("\n") -SLANG_RAW("__target_intrinsic(glsl, \"(gl_WorldRayDirectionNVX)\")\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_WorldRayDirectionNV)\")\n") SLANG_RAW("float3 WorldRayDirection();\n") SLANG_RAW("\n") -SLANG_RAW("__target_intrinsic(glsl, \"(gl_RayTminNVX)\")\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_RayTminNV)\")\n") SLANG_RAW("float RayTMin();\n") SLANG_RAW("\n") SLANG_RAW("// Note: The `RayTCurrent()` intrinsic should translate to\n") -SLANG_RAW("// either `gl_HitTNVX` (for hit shaders) or `gl_RayTmaxNVX`\n") +SLANG_RAW("// either `gl_HitTNV` (for hit shaders) or `gl_RayTmaxNV`\n") SLANG_RAW("// (for intersection shaders). Right now we are handling this\n") SLANG_RAW("// during code emission, for simplicity.\n") SLANG_RAW("//\n") @@ -1534,12 +1535,12 @@ SLANG_RAW("//\n") SLANG_RAW("__target_intrinsic(glsl, \"$XT\")\n") SLANG_RAW("float RayTCurrent();\n") SLANG_RAW("\n") -SLANG_RAW("__target_intrinsic(glsl, \"(gl_RayFlagsNVX)\")\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_IncomingRayFlagsNV)\")\n") SLANG_RAW("uint RayFlags();\n") SLANG_RAW("\n") SLANG_RAW("// 10.4.3 - Primitive/Object Space System Values\n") SLANG_RAW("\n") -SLANG_RAW("__target_intrinsic(glsl, \"(gl_InstanceCustomIndexNVX)\")\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_InstanceCustomIndexNV)\")\n") SLANG_RAW("uint InstanceIndex();\n") SLANG_RAW("\n") SLANG_RAW("__target_intrinsic(glsl, \"(gl_InstanceID)\")\n") @@ -1548,22 +1549,22 @@ SLANG_RAW("\n") SLANG_RAW("__target_intrinsic(glsl, \"(gl_PrimitiveID)\")\n") SLANG_RAW("uint PrimitiveIndex();\n") SLANG_RAW("\n") -SLANG_RAW("__target_intrinsic(glsl, \"(gl_ObjectRayOriginNVX)\")\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_ObjectRayOriginNV)\")\n") SLANG_RAW("float3 ObjectRayOrigin();\n") SLANG_RAW("\n") -SLANG_RAW("__target_intrinsic(glsl, \"(gl_ObjectRayDirectionNVX)\")\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_ObjectRayDirectionNV)\")\n") SLANG_RAW("float3 ObjectRayDirection();\n") SLANG_RAW("\n") -SLANG_RAW("__target_intrinsic(glsl, \"transpose(gl_ObjectToWorldNVX)\")\n") +SLANG_RAW("__target_intrinsic(glsl, \"transpose(gl_ObjectToWorldNV)\")\n") SLANG_RAW("float3x4 ObjectToWorld3x4();\n") SLANG_RAW("\n") -SLANG_RAW("__target_intrinsic(glsl, \"transpose(gl_WorldToObjectNVX)\")\n") +SLANG_RAW("__target_intrinsic(glsl, \"transpose(gl_WorldToObjectNV)\")\n") SLANG_RAW("float3x4 WorldToObject3x4();\n") SLANG_RAW("\n") -SLANG_RAW("__target_intrinsic(glsl, \"(gl_ObjectToWorldNVX)\")\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_ObjectToWorldNV)\")\n") SLANG_RAW("float4x3 ObjectToWorld4x3();\n") SLANG_RAW("\n") -SLANG_RAW("__target_intrinsic(glsl, \"(gl_WorldToObjectNVX)\")\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_WorldToObjectNV)\")\n") SLANG_RAW("float4x3 WorldToObject4x3();\n") SLANG_RAW("\n") SLANG_RAW("// Note: The provisional DXR spec included these unadorned\n") @@ -1579,5 +1580,5 @@ SLANG_RAW("float3x4 ObjectToWorld() { return ObjectToWorld3x4(); }\n") SLANG_RAW("float3x4 WorldToObject() { return WorldToObject3x4(); }\n") SLANG_RAW("\n") SLANG_RAW("// 10.4.4 - Hit Specific System values\n") -SLANG_RAW("__target_intrinsic(glsl, \"(gl_HitKindNVX)\")\n") +SLANG_RAW("__target_intrinsic(glsl, \"(gl_HitKindNV)\")\n") SLANG_RAW("uint HitKind();\n") diff --git a/source/slang/ir-insts.h b/source/slang/ir-insts.h index 6075c247c..db61cae88 100644 --- a/source/slang/ir-insts.h +++ b/source/slang/ir-insts.h @@ -147,6 +147,11 @@ struct IRRequireGLSLExtensionDecoration : IRDecoration StringRepresentation* extensionName; }; +struct IRReadNoneDecoration : IRDecoration +{ + enum { kDecorationOp = kIRDecorationOp_ReadNone }; +}; + // An instruction that specializes another IR value // (representing a generic) to a particular set of generic arguments // (instructions representing types, witness tables, etc.) @@ -187,6 +192,11 @@ struct IRLookupWitnessTable : IRInst struct IRCall : IRInst { IRUse func; + + IRInst* getCallee() { return getOperand(0); } + + UInt getArgCount() { return getOperandCount() - 1; } + IRInst* getArg(UInt index) { return getOperand(index + 1); } }; struct IRLoad : IRInst diff --git a/source/slang/ir-serialize.cpp b/source/slang/ir-serialize.cpp index e2fd6d3f8..7cc3d6384 100644 --- a/source/slang/ir-serialize.cpp +++ b/source/slang/ir-serialize.cpp @@ -705,6 +705,7 @@ Result IRSerialWriter::write(IRModule* module, SourceManager* sourceManager, Opt } case kIRDecorationOp_VulkanRayPayload: case kIRDecorationOp_VulkanHitAttributes: + case kIRDecorationOp_ReadNone: { dstInst.m_payloadType = PayloadType::Empty; break; @@ -1579,6 +1580,12 @@ IRDecoration* IRSerialReader::_createDecoration(const Ser::Inst& srcInst) decor->languageVersion = Int(srcInst.m_payload.m_uint32); return decor; } + case kIRDecorationOp_ReadNone: + { + auto decor = createEmptyDecoration(m_module); + SLANG_ASSERT(srcInst.m_payloadType == PayloadType::Empty); + return decor; + } default: { SLANG_ASSERT(!"Unhandled decoration type"); diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp index cc3350aef..a66b26e93 100644 --- a/source/slang/ir.cpp +++ b/source/slang/ir.cpp @@ -2939,6 +2939,11 @@ namespace Slang dump(context, "\n[__vulkanHitAttributes]"); } break; + case kIRDecorationOp_ReadNone: + { + dump(context, "\n[__readNone]"); + } + break; } } } @@ -3521,7 +3526,30 @@ namespace Slang return true; case kIROp_Call: - // This is the most interesting. + { + // In the general case, a function call must be assumed to + // have almost arbitrary side effects. + // + // However, it is possible that the callee can be identified, + // and it may be a function with an attribute that explicitly + // limits the side effects it is allowed to have. + // + // For now, we will explicitly check for the `[__readNone]` + // attribute, which was used to mark functions that compute + // their result strictly as a function of the arguments (and + // not anything they point to, or other non-argument state). + // Calls to such functions cannot have side effects (except + // for things like stack overflow that abstract language models + // tend to ignore), and can be subject to dead code elimination, + // common subexpression elimination, etc. + // + auto call = cast(this); + auto callee = getResolvedInstForDecorations(call->getCallee()); + if(callee->findDecoration()) + { + return false; + } + } return true; case kIROp_Nop: @@ -5371,6 +5399,12 @@ namespace Slang } break; + case kIRDecorationOp_ReadNone: + { + context->builder->addDecoration(clonedValue); + } + break; + default: // Don't clone any decorations we don't understand. break; @@ -6005,6 +6039,24 @@ namespace Slang return val; } + IRInst* getResolvedInstForDecorations(IRInst* inst) + { + IRInst* candidate = inst; + while(auto specInst = as(candidate)) + { + auto genericInst = as(specInst->getBase()); + if(!genericInst) + break; + + auto returnVal = findGenericReturnVal(genericInst); + if(!returnVal) + break; + + candidate = returnVal; + } + return candidate; + } + bool isDefinition( IRGlobalValue* inVal) { diff --git a/source/slang/ir.h b/source/slang/ir.h index 41c9ab6ab..def68098d 100644 --- a/source/slang/ir.h +++ b/source/slang/ir.h @@ -157,8 +157,9 @@ enum IRDecorationOp : uint16_t kIRDecorationOp_VulkanHitAttributes, kIRDecorationOp_RequireGLSLVersion, kIRDecorationOp_RequireGLSLExtension, - - kIRDecorationOp_CountOf + kIRDecorationOp_ReadNone, + + kIRDecorationOp_CountOf }; // represents an object allocated in an IR memory arena @@ -1048,6 +1049,14 @@ struct IRGeneric : IRGlobalValueWithParams // a pass can glean information from it. IRInst* findGenericReturnVal(IRGeneric* generic); +// Resolve an instruction that might reference a static definition +// to the most specific IR node possible, so that we can read +// decorations from it (e.g., if this is a `specialize` instruction, +// then try to chase down the generic being specialized, and what +// it seems to return). +// +IRInst* getResolvedInstForDecorations(IRInst* inst); + // The IR module itself is represented as an instruction, which // serves at the root of the tree of all instructions in the module. struct IRModuleInst : IRParentInst diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp index 806654ccb..815822495 100644 --- a/source/slang/lower-to-ir.cpp +++ b/source/slang/lower-to-ir.cpp @@ -5137,6 +5137,11 @@ struct DeclLoweringVisitor : DeclVisitor decoration->languageVersion = Int(getIntegerLiteralValue(versionMod->versionNumberToken)); } + if(decl->FindModifier()) + { + getBuilder()->addDecoration(irFunc); + } + // For convenience, ensure that any additional global // values that were emitted while outputting the function // body appear before the function itself in the list diff --git a/source/slang/modifier-defs.h b/source/slang/modifier-defs.h index dc4e99cdd..bb9aefcb7 100644 --- a/source/slang/modifier-defs.h +++ b/source/slang/modifier-defs.h @@ -405,6 +405,14 @@ SIMPLE_SYNTAX_CLASS(VulkanHitAttributesAttribute, Attribute) // SIMPLE_SYNTAX_CLASS(MutatingAttribute, Attribute) +// A `[__readNone]` attribute, which indicates that a function +// computes its results strictly based on argument values, without +// reading or writing through any pointer arguments, or any other +// state that could be observed by a caller. +// +SIMPLE_SYNTAX_CLASS(ReadNoneAttribute, Attribute) + + // HLSL modifiers for geometry shader input topology SIMPLE_SYNTAX_CLASS(HLSLGeometryShaderInputPrimitiveTypeModifier, Modifier) SIMPLE_SYNTAX_CLASS(HLSLPointModifier , HLSLGeometryShaderInputPrimitiveTypeModifier) -- cgit v1.2.3