From 6b44630afe4ff180ba608142e9515abcd369775e Mon Sep 17 00:00:00 2001 From: Ronan Date: Thu, 3 Apr 2025 06:17:15 +0200 Subject: Fixed generic interface specialization crashes (#6601): (#6688) * Fixed generic interface specialization crashes: - Add an export decoration to specialized generic interfaces. * Fixed generic interface specialization crashes: - Add an export decoration to specialized generic interfaces. - Use getTypeNameHint(...) instead of a manual mangler. * In cloneInstDecorationsAndChildren: specialize all linkage decorations, not just the exports. - If a linkage decoration is already present, it is not specialized and replaced by the specialized one. - If a specialization uses the TypeNameHint, sanitize it to be used as an identifier. - Use the identifier name sanitizer from slang-mangle. * Added tests/generics/generic-interface-linkage.slang - See #6601 and #6688 --- source/slang/slang-ir-clone.cpp | 63 +++++++++++++++++ source/slang/slang-mangle.cpp | 17 +++-- source/slang/slang-mangle.h | 2 + .../generics/generic-interface-linkage.slang | 81 ++++++++++++++++++++++ 4 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 tests/language-feature/generics/generic-interface-linkage.slang diff --git a/source/slang/slang-ir-clone.cpp b/source/slang/slang-ir-clone.cpp index 66af405d6..5bb1c1210 100644 --- a/source/slang/slang-ir-clone.cpp +++ b/source/slang/slang-ir-clone.cpp @@ -2,7 +2,10 @@ #include "slang-ir-clone.h" #include "slang-ir-insts.h" +#include "slang-ir-util.h" #include "slang-ir.h" +#include "slang-mangle.h" + namespace Slang { @@ -104,6 +107,61 @@ IRInst* cloneInstAndOperands(IRCloneEnv* env, IRBuilder* builder, IRInst* oldIns return newInst; } +// Copy the linkage decoration of oldInst (if present) and specialize it for target. +static void specializeLinkageDecoration(IRInst* target, IRSpecialize* oldInst, IRBuilder* builder) +{ + auto gen = as(oldInst->getBase()); + if (gen) + { + auto genLinkage = gen->findDecoration(); + if (genLinkage) + { + bool isExport = as(genLinkage); + StringBuilder sb; + sb.append(genLinkage->getMangledName()); + sb.append("G"); + IRSpecialize* specializationProvider = oldInst; + if (auto targetAsSpec = as(target)) + { + specializationProvider = targetAsSpec; + } + for (UInt i = 0; i < specializationProvider->getArgCount(); ++i) + { + auto arg = specializationProvider->getArg(i); + sb.append(i); + if (auto typeLinkage = arg->findDecoration()) + { + sb.append(typeLinkage->getMangledName()); + } + else + { + // getTypeNameHint may produce a name with characters that can't + // be part of an identifier, so we need to filter it afterward. + StringBuilder tmp; + getTypeNameHint(tmp, arg); + emitNameForLinkage(sb, tmp.getUnownedSlice()); + } + } + if (auto previousLinkage = target->findDecoration()) + { + // Overwrite the previous linkage decoration, since it was not specialized + previousLinkage->setOperand(0, builder->getStringValue(sb.getUnownedSlice())); + } + else + { + if (isExport) + { + builder->addExportDecoration(target, sb.getUnownedSlice()); + } + else + { + builder->addImportDecoration(target, sb.getUnownedSlice()); + } + } + } + } +} + // The complexity of the second phase of cloning (the // one that deals with decorations and children) comes // from the fact that it needs to sequence the two phases @@ -226,6 +284,11 @@ static void _cloneInstDecorationsAndChildren( newParam->setFullType(newType); newParam->sourceLoc = oldParam->sourceLoc; } + + if (auto oldAsSpec = as(oldInst)) + { + specializeLinkageDecoration(newInst, oldAsSpec, builder); + } } // The public version of `cloneInstDecorationsAndChildren` is then diff --git a/source/slang/slang-mangle.cpp b/source/slang/slang-mangle.cpp index 12e185c8b..63dfffb94 100644 --- a/source/slang/slang-mangle.cpp +++ b/source/slang/slang-mangle.cpp @@ -31,7 +31,7 @@ void emit(ManglingContext* context, String const& value) context->sb.append(value); } -void emitNameImpl(ManglingContext* context, UnownedStringSlice str) +void emitNameForLinkage(StringBuilder& sb, UnownedStringSlice str) { Index length = str.getLength(); // If the name consists of only traditional "identifer characters" @@ -61,8 +61,8 @@ void emitNameImpl(ManglingContext* context, UnownedStringSlice str) // code points and the number of extended grapheme clusters, // since the entire name is within the ASCII subset. // - emit(context, length); - context->sb.append(str); + sb.append(length); + sb.append(str); } else { @@ -98,9 +98,9 @@ void emitNameImpl(ManglingContext* context, UnownedStringSlice str) } } - context->sb.append("R"); - emit(context, encoded.getLength()); - context->sb.append(encoded); + sb.append("R"); + sb.append(encoded.getLength()); + sb.append(encoded); } // TODO: This logic does not rule out consecutive underscores, @@ -111,6 +111,11 @@ void emitNameImpl(ManglingContext* context, UnownedStringSlice str) // target, rather than adding complexity here. } +void emitNameImpl(ManglingContext* context, UnownedStringSlice str) +{ + emitNameForLinkage(context->sb, str); +} + void emitName(ManglingContext* context, Name* name) { String str = getText(name); diff --git a/source/slang/slang-mangle.h b/source/slang/slang-mangle.h index cfdbe461b..079f7c02d 100644 --- a/source/slang/slang-mangle.h +++ b/source/slang/slang-mangle.h @@ -10,6 +10,8 @@ namespace Slang { struct IRSpecialize; +void emitNameForLinkage(StringBuilder& sb, UnownedStringSlice str); + String getMangledName(ASTBuilder* astBuilder, Decl* decl); String getMangledName(ASTBuilder* astBuilder, DeclRefBase* declRef); String getMangledNameFromNameString(const UnownedStringSlice& name); diff --git a/tests/language-feature/generics/generic-interface-linkage.slang b/tests/language-feature/generics/generic-interface-linkage.slang new file mode 100644 index 000000000..657ac3d48 --- /dev/null +++ b/tests/language-feature/generics/generic-interface-linkage.slang @@ -0,0 +1,81 @@ + +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-slang -compute -shaderobj -output-using-type +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=CHECK):-vk -compute -shaderobj -output-using-type + +//TEST_INPUT:ubuffer(data=[0 0 0 0 0], stride=4):out,name=outputBuffer +RWStructuredBuffer outputBuffer; + +interface IGetter +{ + float get(uint id); +} + +struct GetterImpl : IGetter +{ + float[8] data; + + __init(float[8] data) + { this.data = data; } + + float get(uint id) + { + return data[id]; + } +} +interface IFoo +{ + associatedtype Params : IGetter; + + Params bar(); +} + +struct FooImpl1: IFoo<8> +{ + typealias Params = GetterImpl; + + __init() + { } + + Params bar() + { + float x = outputBuffer[0]; + return GetterImpl({x, x+1, x+2, x+3, x+4, x+5, x+6, x+7}); + } +} + +struct FooImpl2: IFoo<8> +{ + typealias Params = GetterImpl; + + __init() + { } + + Params bar() + { + float x = 2 * outputBuffer[0]; + return GetterImpl({x+3, x+5, x+7, x+9, x+11, x+13, x+15, x+17}); + } +} + +IFoo<8> getFoo(uint id) +{ + if (id == 0) + return FooImpl1(); + else + return FooImpl2(); +} + +float doThing(uint id) +{ + IFoo<8> foo = getFoo(id); + return foo.bar().get(0); +} + +[shader("compute")] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + // CHECK: 0 + outputBuffer[0] = doThing(0); + // CHECK: 3 + outputBuffer[1] = doThing(1); +} \ No newline at end of file -- cgit v1.2.3