summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-ir-undo-param-copy.cpp
Commit message (Collapse)AuthorAge
* Rewriting the lower-buffer-element-type pass to avoid unnecessary ↵Yong He2025-09-30
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | packing/unpacking. (#8526) Part of the effort to improve the performance of generated SPIRV code. The existing lower-buffer-element-type pass works by loading the entire buffer element content from memory, and translate it to logical type stored in a local variable at the earliest reference of a buffer handle. This means that is can generate inefficient code that reads more than necessary. Consider this example: ``` struct BigStruct { bool values[1024]; } ConstantBuffer<BigStruct> cb; void test(BigStruct v) { if (v.values[0]) { printf("ok"); } } [numthreads(1,1,1)] void computeMain() { test(cb); } ``` In IR, the `computeMain` function before lower-buffer-element-type pass is something like following: ``` func test: %v = param : BigStruct %barr = fieldExtract(%v, "values") %element = elementExtract(%barr, 0) ... // uses %element func computeMain: %v = load(cb) call %test %v ``` The existing lower-buffer-element-type pass will rewrite the bool array in `BigStruct` into `int` array so it is legal in SPIRV. However, it does so by inserting the translation on the first `load` of the constant buffer: ``` struct BigStruct_std430 { int values[1024]; } var cb : ConstantBuffer<BigStruct_std430>; func computeMain: %tmpVar : var<BigStruct> call %unpackStorage(%tmpVar, cb) %v : BigStruct = load %tmpVar call %test %v ``` This means that the entire array will be loaded and translated to int, before calling `test`, which only uses one element. It turns out that the downstream compiler isn't always able to optimize out this inefficient translation/copy. This PR completely rewrites the way buffer-element-type lowering is handled to avoid producing this inefficient code. It works in two parts: first we turn on the `transformParamsToConstRef` pass for SPIRV target as well, so we will translate the `test` function to take the `v` parameter as `constref`. The second part is a redesigned buffer-element-type pass that defers the storage-type to logical-type translation until a value is actually used by a `load` instruction. In this example, after `transformParamsToConstRef`, the IR is: ``` func test: %v = param : ConstRef<BigStruct> %barr = fieldAddr(%v, "values") %elementPtr = elementAddr(%barr, 0) %element = load(%elementPtr) ... // uses %element func computeMain: call %test %cb ``` The new `buffer-element-type-lowering` pass will take this IR, and insert translation at latest possible time across the entire call graph, and translate the IR into: ``` func test: %v = param : ConstRef<BigStruct_std430> %barr = fieldAddr(%v, "values") %elementPtr : ptr<int> = elementAddr(%barr, 0) %element_int = load(%elementPtr) %element = cast(%element_int) : %bool ... // uses %element func computeMain: call %test %cb ``` In this new IR, there is no longer a load and conversion of the entire array. See new comment in `slang-ir-lower-buffer-element-type.cpp` for more details of how the pass works. This PR also address many other issues surfaced by turning on `transformParamsToConstRef` pass on SPIRV backend. --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
* Fix: Preserve inout param modifications with OptiX IgnoreHit() (#6956)Harsh Aggarwal (NVIDIA)2025-05-17
* Add noreturn attribute to IgnoreHit * Revert "Add noreturn attribute to IgnoreHit" This reverts commit 3cf2354dada71b9a8713b08f3a2e261de4aabfa4. * Fix: Preserve inout param modifications with OptiX IgnoreHit() Issue #6326 identified that in OptiX, when using IgnoreHit() (which maps to the "noreturn" optixIgnoreIntersection() intrinsic), any modifications made to 'inout' parameters within the shader would be lost. This was due to IgnoreHit() preventing the execution of the copy-back operation from the temporary variable (used to implement 'inout' semantics) to the original parameter. This commit introduces a new IR pass, 'undoParameterCopy', specifically for CUDA/OptiX targets to address this. The pass operates as follows: 1. Identifies temporary IR variables created for 'inout' parameters, which are now decorated with 'TempCallArgVarDecoration'. 2. Maps these temporary variables back to their original parameter storage (e.g., the OptiX payload pointer). 3. Replaces all uses of the temporary variable directly with the original parameter pointer. 4. Removes the temporary variable declaration and its initializing store (which copied from the original parameter to the temporary). By transforming the IR to operate directly on the original parameter storage before any potential call to IgnoreHit(), this fix ensures that all modifications are preserved, correctly resolving issue #6326. The pass is integrated into the compilation flow for relevant targets. * Refactor(IR): Optimize GetOptiXRayPayloadPtr for better DCE/CSE To allow for more effective dead code elimination (DCE) and common subexpression elimination (CSE) of `getOptiXRayPayloadPtr` instructions, this commit: - Marks `kIROp_GetOptiXRayPayloadPtr` as side-effect-free within `IRInst::mightHaveSideEffects` (in `slang-ir.cpp`). - Flags `GetOptiXRayPayloadPtr` as `HOISTABLE` in its definition within `slang-ir-inst-defs.h`. This addresses scenarios where multiple, potentially redundant, calls to `getOptiXRayPayloadPtr` might appear in the IR, allowing optimizers to produce cleaner and potentially more efficient code for OptiX targets. This change supports efforts to refine IR handling for ray-tracing shader stages. * Remove debugging code * Refactor UndoParameterCopyVisitor for improved performance - Optimized IR traversal by combining multiple passes into a single scan - Removed unnecessary dictionary, immediately replace uses when a temp var is found - Reduced duplicate code paths by checking for both temp vars and redundant stores in one loop - Better handling of the 'changed' flag to ensure DCE only runs when needed - Results in fewer instruction traversals and improved efficiency for large functions * Add Test