diff --git a/tools/clang/include/clang/SPIRV/SpirvBuilder.h b/tools/clang/include/clang/SPIRV/SpirvBuilder.h index dba103207c..2e697a94e0 100644 --- a/tools/clang/include/clang/SPIRV/SpirvBuilder.h +++ b/tools/clang/include/clang/SPIRV/SpirvBuilder.h @@ -801,6 +801,12 @@ class SpirvBuilder { void decorateWithLiterals(SpirvInstruction *targetInst, unsigned decorate, llvm::ArrayRef literals, SourceLocation); + /// \brief Decorates the given function with information from VKDecorateExt. + /// Used for [[vk::ext_decorate(...)]] placed directly on a function, which + /// emits an OpDecorate targeting the OpFunction. + void decorateWithLiterals(SpirvFunction *targetFunc, unsigned decorate, + llvm::ArrayRef literals, SourceLocation); + /// \brief Decorates the given target with result ids of SPIR-V /// instructions. void decorateWithIds(SpirvInstruction *targetInst, unsigned decorate, diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp index 1a1078bf0b..dea3569754 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp @@ -1826,6 +1826,17 @@ SpirvFunction *DeclResultIdMapper::getOrRegisterFn(const FunctionDecl *fn) { spv::LinkageType::Export, fn->getLocation()); } + // Honor inline-SPIR-V attributes placed directly on a function. The + // entry-point path handles these only for entry functions, and the + // vk::ext_instruction path only for functions lowered to an instruction, so a + // plain function was previously skipped and these attributes silently + // dropped. These reuse the same helpers as the variable/parameter paths: + // [[vk::ext_decorate(d, ...)]] -> OpDecorate targeting the OpFunction + // [[vk::ext_capability(c)]] -> OpCapability for the module + // [[vk::ext_extension("...")]] -> OpExtension for the module + decorateWithIntrinsicAttrs(fn, spirvFunction); + registerCapabilitiesAndExtensionsForDecl(fn); + // No need to dereference to get the pointer. Function returns that are // stand-alone aliases are already pointers to values. All other cases should // be normal rvalues. @@ -5047,6 +5058,29 @@ void DeclResultIdMapper::decorateWithIntrinsicAttrs( } } +void DeclResultIdMapper::decorateWithIntrinsicAttrs(const NamedDecl *decl, + SpirvFunction *targetFunc) { + if (!decl->hasAttrs()) + return; + + for (auto &attr : decl->getAttrs()) { + if (auto *decoAttr = dyn_cast(attr)) { + spvBuilder.decorateWithLiterals( + targetFunc, decoAttr->getDecorate(), + {decoAttr->literals_begin(), decoAttr->literals_end()}, + decl->getLocation()); + continue; + } + // The id/string forms decorate a SpirvInstruction target; there is no + // SpirvFunction-target equivalent yet, so reject rather than silently drop. + if (isa(attr) || isa(attr)) { + emitError("vk::ext_decorate_id and vk::ext_decorate_string are not " + "supported on functions", + decl->getLocation()); + } + } +} + void DeclResultIdMapper::decorateStageVarWithIntrinsicAttrs( const NamedDecl *decl, StageVar *stageVar, SpirvVariable *varInst) { auto checkBuiltInLocationDecoration = @@ -5136,6 +5170,17 @@ void DeclResultIdMapper::storeOutStageVarsToStorage( } } +void DeclResultIdMapper::registerCapabilitiesAndExtensionsForDecl( + const NamedDecl *decl) { + for (auto *attribute : decl->specific_attrs()) { + spvBuilder.requireExtension(attribute->getName(), decl->getLocation()); + } + for (auto *attribute : decl->specific_attrs()) { + spv::Capability cap = spv::Capability(attribute->getCapability()); + spvBuilder.requireCapability(cap, decl->getLocation()); + } +} + void DeclResultIdMapper::registerCapabilitiesAndExtensionsForType( const TypedefType *type) { for (const auto *decl : typeAliasesWithAttributes) { diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.h b/tools/clang/lib/SPIRV/DeclResultIdMapper.h index 6e8177edf2..adfded231e 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.h +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.h @@ -576,6 +576,13 @@ class DeclResultIdMapper { llvm::function_ref extraFunctionForDecoAttr = [](VKDecorateExtAttr *) {}); + /// Applies [[vk::ext_decorate(...)]] placed directly on a function to the + /// function itself, emitting an OpDecorate that targets the OpFunction. Only + /// the literal form is supported; the id/string variants are diagnosed as + /// unsupported on functions (they lack a SpirvFunction-target decoration). + void decorateWithIntrinsicAttrs(const NamedDecl *decl, + SpirvFunction *targetFunc); + /// \brief Creates instructions to load the value of output stage variable /// defined by outputPatchDecl and store it to ptr. Since the output stage /// variable for OutputPatch is an array whose number of elements is the @@ -589,6 +596,12 @@ class DeclResultIdMapper { spv::ExecutionMode getInterlockExecutionMode(); + /// Records any Spir-V capabilities and extensions declared directly on the + /// given decl via [[vk::ext_capability(...)]] / [[vk::ext_extension(...)]] + /// so they will be added to the SPIR-V module. Shared by the variable and + /// function declaration paths. + void registerCapabilitiesAndExtensionsForDecl(const NamedDecl *decl); + /// Records any Spir-V capabilities and extensions for the given type so /// they will be added to the SPIR-V module. The capabilities and extension /// required for the type will be sourced from the decls that were recorded diff --git a/tools/clang/lib/SPIRV/SpirvBuilder.cpp b/tools/clang/lib/SPIRV/SpirvBuilder.cpp index f61d61ed71..d8eceaccc6 100644 --- a/tools/clang/lib/SPIRV/SpirvBuilder.cpp +++ b/tools/clang/lib/SPIRV/SpirvBuilder.cpp @@ -1949,6 +1949,16 @@ void SpirvBuilder::decorateWithLiterals(SpirvInstruction *targetInst, mod->addDecoration(decor); } +void SpirvBuilder::decorateWithLiterals(SpirvFunction *targetFunc, + unsigned decorate, + llvm::ArrayRef literals, + SourceLocation srcLoc) { + SpirvDecoration *decor = new (context) SpirvDecoration( + srcLoc, targetFunc, static_cast(decorate), literals); + assert(decor != nullptr); + mod->addDecoration(decor); +} + void SpirvBuilder::decorateWithIds(SpirvInstruction *targetInst, unsigned decorate, llvm::ArrayRef ids, diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.cpp b/tools/clang/lib/SPIRV/SpirvEmitter.cpp index b7fd447787..8cd7f85747 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.cpp +++ b/tools/clang/lib/SPIRV/SpirvEmitter.cpp @@ -1924,16 +1924,8 @@ bool SpirvEmitter::validateVKAttributes(const NamedDecl *decl) { void SpirvEmitter::registerCapabilitiesAndExtensionsForVarDecl( const VarDecl *varDecl) { - // First record any extensions that are part of the actual variable - // declaration. - for (auto *attribute : varDecl->specific_attrs()) { - clang::StringRef extensionName = attribute->getName(); - spvBuilder.requireExtension(extensionName, varDecl->getLocation()); - } - for (auto *attribute : varDecl->specific_attrs()) { - spv::Capability cap = spv::Capability(attribute->getCapability()); - spvBuilder.requireCapability(cap, varDecl->getLocation()); - } + // First record any extensions/capabilities declared on the variable itself. + declIdMapper.registerCapabilitiesAndExtensionsForDecl(varDecl); // Now check for any capabilities or extensions that are part of the type. const TypedefType *type = dyn_cast(varDecl->getType()); diff --git a/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.intrinsicDecorate.function.error.hlsl b/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.intrinsicDecorate.function.error.hlsl new file mode 100644 index 0000000000..da92a80b3c --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.intrinsicDecorate.function.error.hlsl @@ -0,0 +1,17 @@ +// RUN: not %dxc -T cs_6_0 -E main -fcgl %s -spirv 2>&1 | FileCheck %s + +// vk::ext_decorate_id and vk::ext_decorate_string decorate a value-id target +// and have no OpFunction-target form, so applying them to a function must be +// diagnosed rather than silently dropped. + +[[vk::ext_decorate_string(/* UserTypeGOOGLE */ 5636, "myType")]] +[noinline] uint Identity(uint x) { return x; } + +// CHECK: error: vk::ext_decorate_id and vk::ext_decorate_string are not supported on functions + +RWStructuredBuffer buf; + +[numthreads(1, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + buf[0] = Identity(tid.x); +} diff --git a/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.intrinsicDecorate.function.hlsl b/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.intrinsicDecorate.function.hlsl new file mode 100644 index 0000000000..a70225f65a --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.intrinsicDecorate.function.hlsl @@ -0,0 +1,28 @@ +// RUN: %dxc -T cs_6_0 -E main -fcgl -Vd %s -spirv | FileCheck %s + +// Inline-SPIR-V attributes placed directly on a function must be honored: +// vk::ext_decorate emits an OpDecorate targeting the OpFunction, and +// vk::ext_capability / vk::ext_extension add the capability / extension to the +// module. Known SPIR-V enums are used here so the disassembler can render them; +// the mechanism is identical for vendor-specific decoration numbers. + +// CHECK-DAG: OpCapability Int8 +// CHECK-DAG: OpExtension "some_extension" + +[[vk::ext_capability(/* Int8 */ 39)]] +[[vk::ext_extension("some_extension")]] +[[vk::ext_decorate(/* RelaxedPrecision */ 0)]] +[[vk::ext_decorate(/* Alignment */ 44, 16)]] +[noinline] uint Identity(uint x) { return x; } + +// CHECK-DAG: OpDecorate %Identity RelaxedPrecision +// CHECK-DAG: OpDecorate %Identity Alignment 16 + +// CHECK: %Identity = OpFunction %uint DontInline + +RWStructuredBuffer buf; + +[numthreads(1, 1, 1)] +void main(uint3 tid : SV_DispatchThreadID) { + buf[0] = Identity(tid.x); +}