From 138efb9c25aadd319db6e6300a263574d90e3391 Mon Sep 17 00:00:00 2001 From: Jay Kwak <82421531+jkwak-work@users.noreply.github.com> Date: Thu, 24 Jul 2025 16:47:21 -0700 Subject: Avoid early specialization for witness tables in SimplifyIR (#7636) * Avoid early specialization for witness tables in SimplifyIR Prevents SimplifyIR from prematurely specializing witness tables before the main specialization pass. Witness tables are hoistable immutable objects that must maintain consistent signatures to avoid incorrect deduplication. SimplifyIR was incorrectly transforming expressions like "witness_table_t(%IFoo)(specialize(%7, %GenericValue4))" into "witness_table_t(%IFoo)(%Foo)" even when %GenericValue4 was unused. Fixes #7233 * Add a missing test file --- .../slang/slang-ir-remove-unused-generic-param.cpp | 30 +++++++++++++-- .../generic-value-parameter-via-interface.slang | 44 ++++++++++++++++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 tests/language-feature/interfaces/generic-value-parameter-via-interface.slang diff --git a/source/slang/slang-ir-remove-unused-generic-param.cpp b/source/slang/slang-ir-remove-unused-generic-param.cpp index dd4efeddd..c79e236b8 100644 --- a/source/slang/slang-ir-remove-unused-generic-param.cpp +++ b/source/slang/slang-ir-remove-unused-generic-param.cpp @@ -72,17 +72,38 @@ struct RemoveUnusedGenericParamContext : InstPassBase child = next; } SLANG_ASSERT(returnVal); - List uses; + + // Collect all specialize uses that we might optimize + List specializeUses; for (auto use = genInst->firstUse; use; use = use->nextUse) - uses.add(use); - for (auto use : uses) { if (use->getUser()->getOp() == kIROp_Specialize && use == use->getUser()->getOperands()) { - use->getUser()->replaceUsesWith(returnVal); + specializeUses.add(use); + } + } + + // Check if any of these specialize uses are used by witness tables + // If so, we cannot apply the optimization because witness tables are immutable + for (auto use : specializeUses) + { + for (auto specializeUse = use->getUser()->firstUse; specializeUse; + specializeUse = specializeUse->nextUse) + { + auto userOp = specializeUse->getUser()->getOp(); + if (userOp == kIROp_WitnessTable) + { + goto skipOptimization; + } } } + + // Apply the optimization: replace all specialize uses with the return value + for (auto use : specializeUses) + { + use->getUser()->replaceUsesWith(returnVal); + } genInst->replaceUsesWith(returnVal); genInst->removeAndDeallocate(); } @@ -118,6 +139,7 @@ struct RemoveUnusedGenericParamContext : InstPassBase for (auto param : paramsToRemove) param->removeAndDeallocate(); } + skipOptimization:; } } return changed; diff --git a/tests/language-feature/interfaces/generic-value-parameter-via-interface.slang b/tests/language-feature/interfaces/generic-value-parameter-via-interface.slang new file mode 100644 index 000000000..abc1131e8 --- /dev/null +++ b/tests/language-feature/interfaces/generic-value-parameter-via-interface.slang @@ -0,0 +1,44 @@ +//TEST:INTERPRET(filecheck=CHECK): + +interface IFoo +{ + void test(); +}; + +struct Foo : IFoo +{ + void test() + { + printf("GenericValue=%d, value=%d\n", GenericValue, value); + } + + uint value; +}; + +void testInterfaceAsParameter(IFoo foo) +{ + foo.test(); +} + +void testInterfaceAsGeneric(T foo) +{ + foo.test(); +} + +void main() +{ + // CHECK-COUNT-2:GenericValue=0, value=0 + Foo<0> foo0 = {0}; + testInterfaceAsParameter(foo0); + testInterfaceAsGeneric(foo0); + + // CHECK-COUNT-2:GenericValue=1, value=1 + Foo<1> foo1 = {1}; + testInterfaceAsParameter(foo1); + testInterfaceAsGeneric(foo1); + + // CHECK-COUNT-2:GenericValue=2, value=2 + Foo<2> foo2 = {2}; + testInterfaceAsParameter(foo2); + testInterfaceAsGeneric(foo2); +} -- cgit v1.2.3