summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-ir.cpp
Commit message (Collapse)AuthorAge
* Clean up Slang IR representation of undefined values (#8708)Theresa Foley2025-10-15
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Prior to this change, the Slang IR used a single opcode (`kIROp_Undefined`) to encode all cases of undefined values. The particular motivation for this change was a need to distinguish those undefined values that represent a load from an uninitialized memory location versus other sorts of undefined values. If transforming a variable into SSA form results in `undefined` values in cases where the a `load` was executed without a prior `store`, that represents an error on the programmer's part, and should be diagnosed. However, other cases of undefined values can arise during program transformation and optimization, and should not typically result in diagnostics being emitted. While it was not the original motivation for this change, it is also worth noting that the LLVM project has transitioned from initially using only a single `undef` instruction to having a more nuanced model, and the same factors that motivated their shift also apply to the Slang IR. Counter-intuitively, the semantics of undefined values actually need to be carefully defined. Concretely, this change splits the pre-existing `undefined` opcode into two sub-cases: - `kIROp_LoadFromUninitializedMemory`, to represent the case of loading from a memory location (such as a local variable) that has not been initialized. - `kIROp_Poison`, corresponding to the LLVM `poison` value. Our poison instruction is intended to have semantics comparable to LLVM's equivalent. Conceptually, any operation that is invoked with a poison value as input will (with a few exceptions) produce a poison value as output. One can think of the behavior of `poison` as similar to how not-a-number values propagate in floating-point computations: by default they "infect" the result of any computation they are involved in. This semantic choice helps to ensure that many optimizations end up being correct in the presence of undefined values, even if they did not specifically account for them. The `kIROp_LoadFromUninitializedMemory` case is comparable to the combination of `freeze` and `undef` in LLVM. An LLVM `undef` value has semantics that allow *each* use of that value to be replaced with a *different* arbitrary value; these semantics cause many optimizations to only be correct in the absence of undefined values. An LLVM `freeze` instruction can take an undefined value as input, and produces a single value that is still arbitrary, but must be consistent across all uses. The latter semantics are what we want, since a given `load` from an uninitialized memory location will yield an arbitrary-but-fixed value. Note that we intentionally do not have a direct analogue to LLVM's `undef` instruction, because of the way that `undef` causes so many complications when trying to write optimizations. We also do not add a `kIROp_Freeze` instruction in this change, but that is simply because we currently have no need for it. Existing code that was creating `IRUndefined` values has been updated to create either `IRPoison` or `IRLoadFromUninitializedMemory` values, as appropriate to the use case. Code that was checking for the `kIROp_Undefined` opcode has been updated to either check for both of the new opcodes (in the case of `switch` statements), or to use `as<IRUndefined>` to perform a dynamic cast to the common base type of the two new instructions. Note that this change does not alter the way that instructions representing undefined values are typically emitted as ordinary instructions in the block that produces an undefined value. While emitting `IRLoadFromUninitializedMemory` as an ordinary instruction is exactly what we want, the `IRPoison` case would actually be better represented in Slang IR as a "hoistable" instruction, so that there would only be a singular `poison` value of each type. Changing `IRPoison` to be hoistable would be a good follow-up change, but might run into more challenges depending on what assumptions (if any) the codebase is making about where undefined values get emitted. --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
* Addition of `Load`/`Store` coherent operations (#8395)16-Bit-Dog2025-10-10
| | | | | | | | | | | | | | | | | | | | | | | | Fixes: https://github.com/shader-slang/slang/issues/7634 Duplicate of PR https://github.com/shader-slang/slang/pull/8052 Primary Changes: * Added `storeCoherent` and `loadCoherent` for coherent load/store via pointers. This is backed by `IRMemoryScopeAttr` which is an `IRAttr` attached to `IRLoad` and `IRStore` * Logic in `source\slang\slang-emit-spirv.cpp` for load/store emitting has been reworked to be less messy and more maintainable * Add to `hlsl.meta.slang` coop vector and coop matrix coherent load/store operations Secondary Changes: * Added a missing load/store test for coop matrix: `tests\cooperative-matrix\load-store-pointer.slang` --------- Co-authored-by: ArielG-NV <aglasroth@nvidia.com> Co-authored-by: ArielG-NV <159081215+ArielG-NV@users.noreply.github.com> Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> Co-authored-by: Nathan V. Morrical <natemorrical@gmail.com>
* Specialize interfaces in DebugFunction (#8617)Julius Ikkala2025-10-10
| | | | | | | | | | | | | | | | | | | | | | | | | | | E.g. in [generic-extension-2.slang](https://github.com/shader-slang/slang/blob/master/tests/language-feature/extensions/generic-extension-2.slang), incorrect DebugFunctions are generated for `getFirstOuter`: ``` let %33 : Void = DebugFunction("getFirstOuter", 18 : UInt, 3 : UInt, %26, Func(Int, 0 : Int)) ``` This happens because specialization passes are leaving a `%IFoo` in the function type, instead of replacing with a concrete type: ``` let %34 : Void = DebugFunction("getFirstOuter", 18 : UInt, 3 : UInt, %26, Func(Int, %IFoo)) ``` and later, `cleanUpInterfaceTypes()` just replaces all interfaces with the literal zero. So now we have a parameter type which isn't actually a type at all, but an IntLit instead. I'm not sure if the approach I picked is good, though. Some other options that crossed my mind were: * Make `fixUpFuncType` also update related DebugFunctions - But is there a reason why DebugFunctions separately carry a function type in the first place? * Make `cleanUpInterfaceTypes` less aggressive or at least replace types with a type instead of a value - But this will still make the debug info incorrect :(
* Defer `IRCastStorageToLogicalDeref` in lowerBufferElementType pass. (#8668)Yong He2025-10-10
| | | | | | | | | | | | Fix a regression on metal test. In `lowerBufferElementTypeToStorageType` pass, not only we want to defer an argument that is `CastStorageToLogical` to the callee, but also apply the same defer logic to `CastStorageToLogicalDeref` as well. Because `CastStorageToLogicalDeref` will appear as argumnet if `lowerBufferElementTypeToStorageType` is run before we apply the `in->borrow` transformation pass, which is the case for metal parameter block legalization.
* Use symbol alias instead of wrapper synthesis to implement link-time types. ↵Yong He2025-10-07
| | | | | | | | | | | | | | | | | | | | | | | | | | (#8603) This change achieves link-time type resolution with a different mechanism. For `extern struct Foo : IFoo = FooImpl;`, instead of synthesizing a wrapper type `Foo` that has a `FooImpl inner` field and dispatches all interface method calls to `inner.method()`, this PR completely removes this synthesis step, and instead just lower such `extern`/`export` types as `IRSymbolAlias` instructions that is just a reference to the type being wrapped. Then we extend the linker logic to clone the referenced symbol instead of the SymbolAlias insts itself during linking. By doing so, we greatly simply the logic need to support link-time types, and achieves higher robustness by not having to deal with many AST synthesis scenarios. Closes #8554. --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
* Fix legalization crash when processing metal parameter blocks. (#8591)Yong He2025-10-03
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Closes #7606. When Slang compile for a bindful target, we will run the resource type legalization pass to hoist resource typed struct fields outside of the struct type and define them as global parameters and passing them around via dedicated function parameters. When we compile for a bindless target, we don't run this pass. However, Metal is a hybrid bindful and bindless target. We need to run type legalization for the constant buffer, but skip type legalization for parameter block. The previous attempt to support this behavior is to hack the type legalization pass to return `LegalVal::simple` when it sees a `ParameterBlock<T>`. However, whenever the code is accessing `parameterBlock.someNestedField`, the type of the nested field may get a `LegalType::tuple`, and now we will run into inconsistent scenarios where we have a `LegalVal::simple` on the operand val, and but the legalization logic is expecting that val to be a `LegalType::tuple`. This breaks a lot of assumptions and invariants in the type legalization pass, resulting unstable/fragile behavior. To systematically solve this problem, this change generalizes the existing legalize buffer element type pass to translate `ParameterBlock<Texture2D>` (and similar cases) to `ParameterBlock<Texture2D.Handle>`. So that such parameter block will always be legalized to `LegalType:::simple` during type legalization, and we will never run into any inconsistent cases. This allowed us to get rid of the hacky logic in the type legalization pass to try to workaround the inconsistencies.
* Rename some symbols related to pointers types (#8592)Theresa Foley2025-10-03
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Note that while this change touched a large numer of files, there are no changes to functionality being made here. The only things being done are renaming various symbols and, in a few cases, updating or adding comments for consistency with the new names. The core of the naming changes are: * Most things named to refer to `OutType` (e.g., `IROutType`, `IRBuilder::getOutType()`, etc.) have been consistently renamed to refer to `OutParamType`, to emphasize that the relevant AST/IR node types are only intended for use to represent `out` parameters. * The same change as described above for `OutType` is also made for `RefType`, which becomes `RefParamType` in most cases. One mess that this exposes is the way that the `ExplicitRef<T>` type in the core module currently lowers to `IRRefParamType`. This change sticks to the rule of not making functional changes, so that mess is left as-is for now. * Names referring to `InOutType` have been changed to instead refer to `BorrowInOutType`. The intention with this naming change is to emphasize that the Slang rules for `inout` are semantically those of a borrow (or at least our interpretation of what a borrow means). * Names referring to `ConstRefType` have been changed to instead refer to `BorrowInType`. This change starts work on clarifying that the existing `__constref` modifier was never intended to be a read-only analogue of `__ref`, and instead is the input-only analogue of `inout`. * The `ParameterDirection` enum type has been changed to `ParamPassingMode`, to reflect the fact that the concept of "direction" fails to capture what is actually being encoded, particularly once we have modes beyond simple `in`/`out`/`inout`. While this change does not alter behavior in any case (the user-exposed Slang language is unchanged), it is intended to set up subsequence changes that will work to make the handling of these types in the compiler more nuanced and correct. Breaking this part of the change out separately is primarily motivated by a desire to minimize the effort for reviewers. --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
* Relax the inst definition order rule (#8588)kaizhangNV2025-10-02
| | | | | | | | | | | | Close #8572. The root cause of the issue is that in `_replaceInstUsesWith` call, if the use of the inst is a generic parameter, and the inst is the data type of that generic parameter, we could end up of moving the data type before the generic parameter. This will break the layout of generic parameters, where all the generic parameters should be laid consecutively at the beginning of the first block of the generic. Therefore, we don't make that relocation for such case.
* 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>
* Add SPV_NV_bindless_texture support (#8534)Lujin Wang2025-09-26
| | | | | | | | | | | | | Treat DescriptorHandle as uint64_t instead of uint2. Implement target-specific SPIR-V emission with the bindless texture support. For OpImageTexelPointer, Image must have a type of OpTypePointer with Type OpTypeImage. Fix the issue by using [constref] in __subscript. Add a test coverage for various texture/sampler handle types. --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
* Fix DebugCompilationUnit to reference main shader file instead of header ↵Lujin Wang2025-09-18
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | files (#7957) This PR implements the requested fix for issue #7923 where DebugCompilationUnit incorrectly referenced header files instead of the main shader file. ## Summary - Modified IRDebugSource to include isIncludedFile flag as third operand - Updated emitDebugSource function to accept and pass the included file flag - Updated call sites to use source->isIncludedFile() from SourceFile class - Modified SPIR-V emission to only create DebugCompilationUnit for non-included files ## Test Results The fix has been verified with the provided reproducer code. The SPIR-V output now correctly shows DebugCompilationUnit referencing the main shader file instead of header files. Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Lujin Wang <lujinwangnv@users.noreply.github.com> Co-authored-by: Claude Code <claude@anthropic.com> Co-authored-by: slangbot <ellieh+slangbot@nvidia.com> Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
* Diagnose on structured buffers containing resources (#8222)Ellie Hermaszewska2025-09-03
| | | closes https://github.com/shader-slang/slang/issues/3313
* [CBP] Pointer frontend changes + groupshared pointer support (#7848)ArielG-NV2025-08-29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Resolves #7628 Resolves: #8197 Primary Goals: 1. Add `Access` to pointer 2. AddressSpace::GroupShared support for pointers (SPIR-V) 3. Add `__getAddress()` to replace `&` * `&` is not updated to `require(cpu)` since slangpy uses `&`. This means we must: (1) merge PR; (2) replace `&` with `__getAddress()`; (3) add `require(cpu)` to `&` Changes: * Added to `Ptr` the `Access` generic argument & logic (for `Access::Read`). * Moved the generic argument `AddressSpace` from `Ptr` to the end of the type. * Added pointer casting support between any `Ptr` as long as the `AddressSpace` is the same * Disallow globallycoherent T* and coherent T* * Disallow const T*, T const*, and const T* * Fixed .natvis display of `ConstantValue` `ValOperandNode` * Support generic resolution of type-casted integers * Added `VariablePointer` emitting for spirv + other minor logic needed for groupshared pointers Breaking Changes: * Anyone using the `AddressSpace` of `Ptr` will now have to account for the `Access` argument * we disallow various syntax paired with `Ptr` and `T*` --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
* Initial copy elision pass (#8042)ArielG-NV2025-08-07
| | | | | | | | | | | | | | | | | | | | | | Fixes #7574 Changes: * Add an initial (fairly simple) optimization pass which is able to eliminate redundant copies. * Our current existing optimizer passes remove redundant load/store very robustly, this pass will focus on other cases of copy elimination * Primary approach is to make all functions which are `in T` and `T` is trivial to copy into a `__constref T`. We then (depending on scenario) manually insert a variable+load if a pass-by-reference is not possible; otherwise we pass by `constref`. * Added optimizations to eliminate redundant code which causes `constref` to fail to compile --------- Co-authored-by: Harsh Aggarwal <haaggarwal@nvidia.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: slangbot <ellieh+slangbot@nvidia.com> Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
* Add matrix select intrinsic (#7566)venkataram-nv2025-07-31
| | | | | | | | | | | | | | | | | | | | | | | * Add matrix select intrinsic * Fix hlsl test * Restrict matrix select to HLSL * Better test for HLSL side * Select route for GLSL/SPIRV * Exclude matrices from select legalization * Exclude CUDA from select test * Inline and move * format code --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
* Add utility to trace creation of problematic IRInsts to assist LLM in ↵Copilot2025-07-21
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | debugging (#7820) * Initial plan * Add SLANG_DEBUG_IR_BREAK environment variable support Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * Apply code formatting to SLANG_DEBUG_IR_BREAK implementation Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * Improve stack trace debugging with -rdynamic flag and backtrace_symbols Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * Address PR feedback: use PlatformUtil::getEnvironmentVariable, remove -rdynamic flag, and delete fallback branch Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * Address PR feedback: simplify env var parsing, move backtrace to PlatformUtil, use #if for SLANG_LINUX_FAMILY Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * Address PR feedback: remove unneeded include, make backtrace() more generic by removing uid parameter Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * Fix and clone source tracking. * Add python script to dump traces. * Update instructions. * Batch calls to addr2line * Cleanup claude instructions. * update claude action. * Remove duplicated build instructions from claude.yml workflow Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> * fix build error. * Fix build errors --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: csyonghe <2652293+csyonghe@users.noreply.github.com> Co-authored-by: Yong He <yonghe@outlook.com> Co-authored-by: Gangzheng Tong <tonggangzheng@gmail.com>
* Debugging code improvement for IR tracking (#7770)Jay Kwak2025-07-15
| | | | | | | | | | | | | | When you track IR creation with `_debugUID` or `_slangIRAllocBreak`, you had to set a break point near `_slangIRAllocBreak` is used and unset it after changing the value of `_slangIRAllocBreak`. Then, when you re-run slangc for more debuggin, you have to set the break-point again, because it was unset. With this change, you can keep the break-point in the new line, and it will hit only once per debugging session. Not a big improvement, but I found this useful because it requires less manual handling of the break-points.
* Defer immutable buffer loads when emitting spirv. (#7579)Yong He2025-07-02
| | | | | | | | | | | | | * Defer immutable buffer loads when emitting spirv. * Fix. * Fix. * Fix. * Fix tests. * Fix test.
* extend fiddle to allow custom lua splices in more places (#7559)Ellie Hermaszewska2025-07-01
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * Add fkYAML submodule * Generate slang-ir-inst-defs.h from slang-ir-inst-defs.yaml * generate ir-inst-defs.h * neaten things * neaten inst def parser * add rapidyaml submodule * remove fkyaml * remove fkyaml submodule * remove use of ir-inst-defs.h * format and warnings * fix wasm build * tidy * remove rapidyaml * Extend fiddle to allow custom splices in more places * Use lua to describe ir insts * fix * neaten * neaten * neaten * spelling * neaten * comment comment out assert * merge
* Add arguments for controlling floating point denormal mode (#7461)aidanfnv2025-07-01
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * Implement -fp-denorm-mode slangc arg * Split fp-denorm-mode into 3 args for fp16/32/64 * Remove redundant option categories * Use emitInst for multiple of the same OpExecutionMode * Fix formatting * Remove -denorm any * Re-add option categories * emitinst for ftz * Use enums for type text * Remove extra categories again * Add tests for denorm mode * Move denorm mode to post linking * format code (#8) Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> * regenerate command line reference (#9) Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> * Clean up tests * Fix option text * format code (#10) Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> * Add tests for "any" mode * Return "any" enum if option not set * Simplify emission logic * Add support for generic entrypoints * Move denorm modes to end of CompilerOptionName enum * format code (#11) Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> * Move new enum members to before CountOf * Add not checks to tests, fix generic test, add functionality tests * Rename denorm to fpDenormal * Clean up functional test * Rename denorm test dir * Fix formatting, regenerate cmdline ref * Fold simple tests into functional tests, add more dxil checks * Remove no-op DX tests, make tests more consistent * Disable VK functionality tests that will fail on the CI configs * Fix formatting * Add comments to disabled tests explaining why --------- Co-authored-by: slangbot <ellieh+slangbot@nvidia.com> Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
* Don't call destructor, it causes UB when reading inst afterwards (#7544)Julius Ikkala2025-06-27
|
* Legalise out parameters for vertex shaders on metal (#6943)Ellie Hermaszewska2025-06-10
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * Handle pointer types when getting type cast style Closes https://github.com/shader-slang/slang/issues/6025 * Move vertex shader out parameters to return type for Metal Closes https://github.com/shader-slang/slang/issues/6025 * More asserts * Make struct instead of tuple * More layout preservation * Handle same function result * more layout * remove layout * a * more debug code * more debug code * a * layout working * refactored * more tests * more tests * fuse loops * remove unused comments * Correct filecheck usage * debug code * correct name and order of filecheck vars * simplify * Address review comments fix warning * simplify handling of simple vertex shaders
* Add command line option for separate debug info (#7178)jarcherNV2025-06-06
| | | | | | | | | | | | | * Add command line option for separate debug info Add command line arg -separate-debug-info which, if provided, produces both a .spv and a .dbg.spv file. The .dbg.spv file contains full debug info and the .spv file has all debug info stripped out. Also add a DebugBuildIdentifier instruction to store a unique hash in both the output files, so they can be more easily matched together. A matching API is provided to allow using the Slang API to retrieve a base and debug SPIRV as well as the debug build identifier string.
* Make interface types non c-style in Slang2026. (#7260)Yong He2025-06-04
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * Make interface types non c-style. * Make Optional<T> work with autodiff and existential types. * Fix. * patch behind slang 2026. * Fix warnings. * cleanup. * Fix tests. * Fix. * Fix com interface lowering. * Add comment to test. * regenerate command line reference * Add test for passing `none` to autodiff function. * Fix recording of `getDynamicObjectRTTIBytes`. * Fix nested Optional types. --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
* Fix SPIRV `OpSpecConstantOp` emit (#7158)Darren Wihandi2025-05-29
| | | | | | | | | | | | | * Fix SPIRV specialization constant with floating-point operations * Improve test * WIP * Restrict `OpSpecConstantOp` allowed operations based on SPIRV specifications * Fix typo on floating type check * Emit error on float to int spec cosnt int val casts
* Implement MapElement for CoopMat (#7159)Jay Kwak2025-05-29
| | | | | | | | | With this PR, MapElement works for the following signatures: - CoopMat<...>::MapElement(functype(...)); - CoopMat<...>::MapElement(capturing-lambda); - CoopMat<...>::MapElement(not-capturing-lambda); - Tuple<CoopMat<...>,...>::MapElement(functype(...)); - Tuple<CoopMat<...>,...>::MapElement(capturing-lambda); - Tuple<CoopMat<...>,...>::MapElement(not-capturing-lambda);
* Implement throw & catch statements (#6916)Julius Ikkala2025-05-23
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * Implement throw statement It already existed in the IR, so only parsing, checking and lowering was missing. * Initial catch implementation Likely very broken. * Error out when catch() isn't last in scope * Prevent accessing variables from scope preceding catch As those may actually not be available at that point. * Add IError and use it in Result type lowering * Add diagnostic tests * Allow caught throws in non-throw functions * Fix catch propagating between functions & SPIR-V merge issue * Add test for non-trivial error types * Fix MSVC build * Fix invalid value type from Result lowering * Also lower error handling in templates * Lower result types only after specialization * Attempt to disambiguate error enums by witness table * Revert matching by witness, types should be distinct too * Don't assert valueField when getting Result's error value It may not exist if the function returns void, but getting the error value is still legitimate. * Update tests for new error numbers & get rid of expected.txt * Change catch lowering to resemble breaking a loop ... To make SPIR-V happy. * Fix dead catch blocks and invalid cached dominator tree * More SPIR-V adjustment * Lower catch as two nested loops * Add defer interaction test and revert broken defer changes * Fix enum type when throwing literals * Cleanup and bikeshedding * Document error handling mechanism * Fix table of contents * Use boolean tag in Result<T, E> * Use anyValue storage for Result<T,E> * Remove IError * Fix formatting * Eradicate success values from docs and tests * Use parseModernParamDecl for catch parameter * Implement do-catch syntax * Implement catch-all * Fix formatting * Fix marshalling native calls that throw --------- Co-authored-by: Yong He <yonghe@outlook.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
* Implement spec const for generic parameter (#7121)kaizhangNV2025-05-15
| | | | | | | | | | | | | | | | | | Close #6840. This PR add supports to use specialize constant in generic parameter, and that parameter can also be used as array size, e.g. following code should work: ``` struct MyStruct<let N: int> { float buffer[N]; } MyStruct<SpecConstVar> s; ``` - Loose the restriction from Link-Time to SpecializationConstant when extract generic argument - Tweak the logic of how we decide whether a inst is hoistable. Besides checking existing hoistable flag of each IRInst, when we detect a IRInst's type is SpecConstRateType, we will treat that inst hoistable. Because IRInst in global scope can be deduplicated, and every SpecConstRateType inst should be in the global scope or IRGeneric scope (which will be at global scope after specialization). - Remove the SpecConstIntVal to IRInst map in IR lowering logic, because we already have way to deduplicate the global scope IR.
* support specialization constant sized array (#6871)kaizhangNV2025-05-14
| | | | | | | | | | | | | | | | | | | | | Close #6859 Goal of this PR We want to support an array whose size can be specialization constant for shared/global variable e.g. layout (constant_id = 0) const uint BLOCK_SIZE = 64; shared float buf_a[(BLOCK_SIZE + 5) * 4]; Overview of the solution: During IndexExpr check, we will loose the restriction to allow SpecConst passing, but the size parameter will not be a constant value because it cannot be folded into a constant, so we will make it follow the same logic as generic parameter value, and the size will be represented by FuncCallIntVal/PolynomialIntVal/DeclRefIntVal. During IR lowering, we will detect whether there is spec constant in the IntVal, and wrap the IRInst with a SpecConstRateType, and propagate the type though the lowering logic, such that the IntVal representing the array size will have SpecConstRateType. During spirv emit stage, if we detect that a IRInst has SpecConstRateType, we will emit it as SpecConstantOp. We have to implement new logic to emit OpSpecConstantOp, the existing emit logic doesn't support emitting OpSpecConstantOp, especially this op can embed arithmetic operation at global scope, where we can only emit arithmetic instruct at local. But there are only few instructs we need to support. Overview of the solution: This PR doesn't support generic, and we will create a separate PR to extend that, tracked in #6840.
* Add debug information for slang inling (#6621)Mukund Keshava2025-05-10
|
* Fix SPIRV unsigned to signed widening casts (#7051)Darren Wihandi2025-05-09
| | | | | | | * Fix unsigned to signed casts for SPIRV * Add test * Fix ICE crash
* Fix various intptr_t issues by defining its width in `getIntTypeInfo` (#6786)Julius Ikkala2025-05-09
| | | | | | | | | | | | | | | | | | | | | | | | | * Define a bit size for the intptr types * Fix intptr_t sign * Extend intptr test to check for previously broken operations * Fix intptr vector test on CUDA * Handle intptr size in getAnyValueSize * Fix formatting * Try with __ARM_ARCH_ISA_64 * On macs, int64_t != intptr_t Yikes * Move define to prelude header * Also check apple in host-prelude * Fix define location
* Add IREnumType to distinguish enums from ints and each other (#6973)Julius Ikkala2025-05-03
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * Add IREnumType to distinguish enums from ints and each other * Add issue example as test * format code * Add expected test output * Fix peephole optimization hanging No idea why this PR triggered this, but there seems to have been a clear bug here anyway, so may just as well fix it now. * Move enum lowering later * Add linkage decoration to enum type * Use filecheck-buffer instead of expected.txt * Fix comment * Make enum casts actually use IR enum casts They were all BuiltinCasts by accident * Lower enum type before VM * Deal with rate-qualified types in enum cast * Allow any value marshalling for enum types * Handle new enum instructions in a couple more switches * Fix formatting --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
* Add cooperative matrix 1 support (#6565)Darren Wihandi2025-04-15
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * initial wip for spirv * working tiled example * clean up store and load * minor fixes * fix loadAny name * add initial tests, including broken/unimplemented intrinsics * fix subscript * run tests at 16x16, remove not supported arithmetic tests * minor fixups on implementation * rename CoopMatMatrixUse * Update tests to pass validation layers locally * Add mat-mul-add test and minor fixes * Add more tests * Remove dead code * Add coopMatLoad function and tests, enforce constexpr for matrix layout * Use getVectorOrCoopMatrixElementType in place of getVectorElementType
* Add defer statement (#6619)Julius Ikkala2025-04-06
|
* Add a loop analysis step to infer the exit values of loop phi parameters. ↵Sai Praveen Bangaru2025-04-04
| | | | | | | | | | | | | | | | | | | | | | | | | (#6696) * Initial loop analysis pass * More changes for a single-pass implication propagation * Update slang-ir-autodiff-loop-analysis.cpp * Cleanup + new system for loop analysis * Fixup bugs in loop analysis * Remove some relation types to simplify the analysis. Add test * Remove unused * Address comments * Fix issue with continue loops * Update reverse-loop-exit-value-inference-1.slang * Update reverse-continue-loop.slang
* Make IRWitnessTable HOISTABLE (#6417)Jay Kwak2025-04-01
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | # Make `IRWitnessTable` Hoistable ## Intention of the PR This commit makes `IRWitnessTable` Hoistable so that we can avoid duplicated `IRWitnessTable`. ## Problems This commit tries to address the following issues arise after turning `IRWitnessTable` into Hoistable: 1. A Hoistable instance is immutable. 2. When tries to create a duplicated child, you will get a previously created instance of `IRWitnessTable`, instead of a new one. 3. We don't actually want to hoist `IRWitnessTable`. 4. There can be only one instance of Hoistable and it cannot appear as childs multiple times. 5. Different import/export mangled names were used for the same Witness-table when its type is "enum" interface. ## Implementation ### Solution for "1. A Hoistable instance is immutable." `IRWitnessTable::setConcreteType()` is removed, because when an `IRInst` is Hoistable, it is treated as immutable. Any `IRInst::setXXX()` methods don't work anymore. There were two places calling `setConcreteType()` and their logic had to change little bit. `DeclLoweringVisitor::visitInheritanceDecl()` in `source/slang/slang-lower-to-ir.cpp` was calling `setConcreteType()`. It had a little strange logic around `lowerType()`. The `IRWitnessTable` was added with `context->setGlobalValue()` first and its `concreteType` was changed later. This commit works around in a way that it sets the parent of `IRWitnessTable` temporarily and reset it with the correct `IRWitnessTable`. Without this logic, it went into an infinite recursion. `AutoDiffPass::fillDifferentialTypeImplementation()` in `source/slang/slang-ir-autodiff.cpp` was calling `setConcreteType()`. It was changing the concreteType of `innerResult.diffWitness`. This commit creates a new `IRWitnessTable` and copies its `IRWitnessTableEntry`. ### Solution for "2. When tries to create a duplicated child, you will get a previously created instance of IRWitnessTable, instead of a new one" After a call to `IRBuilder::createWitnessTable()`, this commit checks if the returned `IRWitnessTable` is a brand new or not. If it is not a new one, we have to avoid adding the decorations and children. This commit decides when to add decorations and children based on whether `IRWitnessTable` has any of decorations or children already. It doesn't seem like a proper way to check. But when I tried, it was difficult to find a bottleneck point where the decorations and children are added to `IRWitnessTable` first time. Note that we are not trying to find when `IRWitnessTable` is created for the first time; we need to find if the decorations and children were added once. It might be fine to have duplicated `IRWitnessTableEntry` in most of the cases, but I noticed that it fails an assertion check when `shouldDeepCloneWitnessTable()` returns false in `cloneWitnessTableImpl()`. ### Solution for "3. We don't actually want to hoist IRWitnessTable." The reason why this commit makes `IRWitnessTable` is to prevent the duplicated instances of `IRInst`. But we don't really want to "Hoist" them. When an `IRWitnessTable` gets Hoisted out, it causes unexpected problems and the specialization process fails due to the missing `IRWitnessTable` in the input. This commit prevent from hoisting `IRWitnessTable` in `_replaceInstUsesWith()`. The way this is implemented feel little hack but we discussed on Slack and decided to go with this. One of the proper approaches could be to add a new flag in `IROpFlags` and have a new one like `kIROpFlag_Deduplicate`, which is different from just `kIROpFlag_Hoistable`. ### Solution for "4. There can be only one instance of Hoistable and it cannot appear as childs multiple times." When `IRWitnessTable` is Hoistable, there can be only a unique set of instances. And we cannot have an instance as a duplicated childs. It is because `IRInst` has only one set of `IRInst* next` and `IRInst* prev`. Before this commit, an instance of `IRGeneral` could have duplicated instances of `IRWitnessTable`. As an example, `IInteger` interface inherits two other interfaces, `IArithmetic` and `ILogical`. And they both inherits from `IComparable`. ``` interface IInteger : IArithmetic, ILogical {} interface IArithmetic : IComparable {} interface ILogical : IComparable ``` When we specialize it in `specializeGenericImpl()`, an `IRBlock` gets the following list of children: - IRWitnessTable for IComparable, - IRWitnessTable for IArithmetic, - IRWitnessTable for IComparable, - IRWitnessTable for ILogical, For the cloning during the specialize, "IRWitnessTable for `IComparable`" must be cloned before the cloning of "IRWitnessTable for `IArithmetic`". Because "IRWitnessTable for `IArithmetic`" refers "IRWitnessTable for `IComparable`" as its `IRWitnessTableEntry`. The order they appear in the `IRBlock` as children decides which instances will be cloned first. And "IRWitnessTable for `IComparable`" must appear before "IRWitnessTable for `IArithmetic`". Note that "IRWitnessTable for `IComparable`" appears twice, The first one was added for "IRWitnessTable for `IArithmetic`". And the second one is added for "IRWitnessTable for `ILogical`". With this commit "IRWitnessTable for `IComparable`" can appear as a child only once in `IRBlock`. So it causes an error if it gets the following list: - IRWitnessTable for IArithmetic, - IRWitnessTable for IComparable, - IRWitnessTable for ILogical, In order to resolve the problem, "IRWitnessTable for `IComparable`" must appear before both "IRWitnessTable for `IArithmetic`" and "IRWitnessTable for `ILogical`" as following: - IRWitnessTable for IComparable, - IRWitnessTable for IArithmetic, - IRWitnessTable for ILogical, To address the problem, the instances of `IRWitnessTable` is always added to the end of the children list. If it is already added to the list, we don't move. This works out because the AST tree is built based on the dependencies. ### Solution for "5. Different import/export mangled names were used for the same Witness-table when its type is "enum" interface." This issue was found while testing with Falcor tests where it uses Conformance-type feature of Slang. We are using different import and export mangled names for a same Witness-table when the witness-table is for "Enum" interface. The way we simplify the implementation of "Enum" causes a problem when it comes to generate export/import for the witness-table. And the exact repro step is still unclear. There were two suggested solutions for the problem and this PR adopted the first option for now. Maybe we want to improve it with the second option later. option 1, when we produce mangled names for those witness-table, we can use a mangled name with the underlying "int" type instead of the name of the enum type. In this way, all witness-tables for enum types whose underlying type is same will get the same mangled name. It will allow us to deduplicate the witness-table during the linking. option 2, we can preserve type info for enum type when generating IR. We can still erase all other uses of the type info of enum types for now. But when we generate the witness-table, instead of filling the conforming type operand to IntType, we fill it as EnumType(IntType) where EnumType is a new global IROp code to represent all enum types (like InterfaceType/StructType). This way the operands for the two witness-tables will be different. "option 1" is more quick and dirty and "option 2" is more proper way to address it. I should go with "option 1" and improve it with "option 2" approach later.
* Add -dump-module command to slangc (#6638)cheneym22025-03-20
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | * Add -dump-module command to slangc The new -dump-module command to slangc will load and disassemble a slang module, similar to what would be seen by the -dump-ir command, except that -dump-ir tells slangc to print IR as it performs some compilation command. That is, -dump-ir requires some larger compilation task. -dump-module on the otherhand requires no additional goal and will simply load a module and print its IR to stdout independently from other compilation steps. Its intended purpose is to inspect .slang-module files on disk. It can also be used on .slang files which will be parsed and lowered if slang does not find an associated ".slang-module" version of the module on disk. The compilation API is extended with a new IModule::disassemble() method which retrieves the string representation of the dumped IR. Closes #6599 * format code * Use FileStream not FILE * format code --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> Co-authored-by: Ellie Hermaszewska <ellieh@nvidia.com>
* IR: Add SPIR-V disassembly for embedded downstream IR dumps (#6529)Mukund Keshava2025-03-11
| | | | | | | | | | | | | | | | | | | | | | | | | | | | * IR: Add SPIR-V disassembly for embedded downstream IR dumps When dumping IR that contains embedded downstream SPIR-V code (via EmbeddedDownstreamIR instructions), display the disassembled SPIR-V instead of just showing "<binary blob>". This CL also does: - Adds a new interface for disassembly and get result. - Modify export-library-generics.slang test test to check for the disassembled SPIR-V Fixes #6513 * Add module-dual-target-verify test Fixes #6517 Adds a new test to verify that dxil and spirv targets are stored separately in the precompiled blob. * Fix review comments from cheneym2 * format code --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
* Update SPIRV-Tools and fix new validation errors. (#6511)Yong He2025-03-06
| | | | | | | * Update SPIRV-Tools and fix new validation errors. * Implement pointers for glsl target. * Reworked packStorage/unpackStorage code gen to operate on pointers rather than values.
* Add Slang-specific intrinsics for integer pack/unpack (#6459)Darren Wihandi2025-02-28
| | | | | | | | | | | | | | | | | | | | | * update hlsl meta * update test * use slang syntax in meta file * improve meta file * fix pack clamp u8 * remove builtin packed types, use typealias instead * fix wgsl pack clamp * fix formatting --------- Co-authored-by: Yong He <yonghe@outlook.com>
* Fix regression in float to bool conversion. (#6497)Yong He2025-02-27
|
* Fix a bug with hoisting 'IRVar' insts that are used outside the loop (#6446)Sai Praveen Bangaru2025-02-25
| | | | | | | | | | | * Fix a bug with hoisting 'IRVar' insts that are used outside the loop - We introduce a 'CheckpointObject' inst and use that to split loop state insts into two pieces (one for within-loop uses and one for outside-loop uses. - This allows the two kinds of uses to be handled separately by the hoisting mechanism - CheckpointObject is then lowered to a no-op after hoisting is complete. * Update slang-ir-autodiff-primal-hoist.cpp * Update slang-ir-autodiff-primal-hoist.cpp
* Improve performance when compiling small shaders. (#6396)Yong He2025-02-23
| | | | | | | Improve performance when compiling small shaders. Avoid copying witness table entries that are not getting used during linking. Avoid copying auto-diff related decorations and derivative functions during linking, if the user modules doesn't use autodiff. Cache operator overload resolution results on global session, so each new Session doesn't need to repetitively run through overload resolution from scratch.
* Simplify implicit cast ctors for vector & matrix. (#6408)Yong He2025-02-20
| | | | | | | | | | | * Simplify implicit cast ctors for vector & matrix. * Fix formatting. * Fix tests. * Fix Falcor test. * Mark __builtin_cast as internal.
* Force inline functions that takes InputPatch and OutputPatch (#6407)Jay Kwak2025-02-19
| | | | | | This commit inlines functions that takes InputPatch and OutputPatch as the function parameter. Co-authored-by: Yong He <yonghe@outlook.com>
* Use and() and or() functions for logical-AND and OR (#6310)Jay Kwak2025-02-07
| | | | | | | | | | | | * Use and() and or() functions for logical-AND and OR With this commit, Slang will emit function calls to `and()` and `or()` for the logical-AND and logical-OR when the operands are non-scalar and the target profile is SM6.0 and above. This is required change from SM6.0. For WGSL, there is no operator overloadings of `&&` and `||` when the operands are non-scalar. Unlike HLSL, WGSL also don't have `and()` nor `or()`. Alternatively, we can use `select()`.
* Support stage_switch. (#6311)Yong He2025-02-06
| | | | | | | | | * Support stage_switch. * Update proposal status. * Fix gl_InstanceID. * Fix.
* maxtessfactor attribute should take a floating point value (#6289)Jay Kwak2025-02-05
| | | | | * maxtessfactor attribute should take a floating point value * Support integer value on maxtessfactor