https://github.com/luciechoi created
https://github.com/llvm/llvm-project/pull/187632
This PR adds support for parsing and lowering the
`[[vk::ext_extension("...")]]` attribute in HLSL.
Each `vk::ext_extension` attribute found in the AST will be collected and
emitted as a string operand within a single `!spirv.ext` named metadata node in
the generated LLVM IR.
The attribute is supported across all expected subjects, including global/local
variables, fields, structs, typedefs, cbuffers, functions, and parameters.
```
!spirv.ext = !{!0}
!0 = !{!"my_extension"}
```
>From 804ba64bacdd7c30164a9010a2de669d7cb5c9c6 Mon Sep 17 00:00:00 2001
From: luciechoi <[email protected]>
Date: Fri, 20 Mar 2026 05:01:13 +0000
Subject: [PATCH] Parsing vk::ext_extension
---
clang/include/clang/Basic/Attr.td | 8 ++++
clang/include/clang/Basic/AttrDocs.td | 7 +++
clang/include/clang/Sema/SemaHLSL.h | 1 +
clang/lib/CodeGen/CGHLSLRuntime.cpp | 33 +++++++++++++
clang/lib/CodeGen/CGHLSLRuntime.h | 1 +
clang/lib/Sema/SemaDeclAttr.cpp | 3 ++
clang/lib/Sema/SemaHLSL.cpp | 8 ++++
.../inline-spirv/spv.inline.extension.hlsl | 48 +++++++++++++++++++
8 files changed, 109 insertions(+)
create mode 100644
clang/test/CodeGenHLSL/vk-features/inline-spirv/spv.inline.extension.hlsl
diff --git a/clang/include/clang/Basic/Attr.td
b/clang/include/clang/Basic/Attr.td
index 5023011a68147..15006b24fae78 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4991,6 +4991,14 @@ def HLSLVkBinding : InheritableAttr {
let Documentation = [HLSLVkBindingDocs];
}
+def HLSLVkExtExtension : InheritableAttr {
+ let Spellings = [CXX11<"vk", "ext_extension">];
+ let Subjects = SubjectList<[Function, Var, Field, TypedefName, Tag,
HLSLBufferObj], ErrorDiag>;
+ let Args = [StringArgument<"ExtensionName">];
+ let LangOpts = [HLSL];
+ let Documentation = [HLSLVkExtExtensionDocs];
+}
+
def HLSLResourceBinding: InheritableAttr {
let Spellings = [HLSLAnnotation<"register">];
let Subjects = SubjectList<[HLSLBufferObj, ExternalGlobalVar], ErrorDiag>;
diff --git a/clang/include/clang/Basic/AttrDocs.td
b/clang/include/clang/Basic/AttrDocs.td
index 555a54fd51a89..961ae54938baf 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -9181,6 +9181,13 @@ The descriptor set is optional and defaults to 0 if not
provided.
}];
}
+def HLSLVkExtExtensionDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+The ``[[vk::ext_extension]]`` attribute allows you to explicitly specify the
required SPIR-V extensions for a shader. This is particularly useful for
ensuring that the generated SPIR-V code includes the necessary extensions for
certain features or capabilities.
+ }];
+}
+
def HLSLVkLocationDocs : Documentation {
let Category = DocCatVariable;
let Content = [{
diff --git a/clang/include/clang/Sema/SemaHLSL.h
b/clang/include/clang/Sema/SemaHLSL.h
index cf9b8c39b109e..75fb70d953855 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -170,6 +170,7 @@ class SemaHLSL : public SemaBase {
void handleWaveSizeAttr(Decl *D, const ParsedAttr &AL);
void handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL);
void handleVkBindingAttr(Decl *D, const ParsedAttr &AL);
+ void handleVkExtExtensionAttr(Decl *D, const ParsedAttr &AL);
void handleVkLocationAttr(Decl *D, const ParsedAttr &AL);
void handlePackOffsetAttr(Decl *D, const ParsedAttr &AL);
void handleShaderAttr(Decl *D, const ParsedAttr &AL);
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index c8a0ab34ae848..d55a93451494f 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -32,6 +32,7 @@
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/Frontend/HLSL/RootSignatureMetadata.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
@@ -468,12 +469,44 @@ void CGHLSLRuntime::addHLSLBufferLayoutType(const
RecordType *StructType,
LayoutTypes[StructType] = LayoutTy;
}
+class VkExtExtensionVisitor
+ : public RecursiveASTVisitor<VkExtExtensionVisitor> {
+ llvm::StringSet<> &Extensions;
+
+public:
+ VkExtExtensionVisitor(llvm::StringSet<> &Exts) : Extensions(Exts) {}
+ bool VisitDecl(Decl *D) {
+ for (const auto *ExtAttr : D->specific_attrs<HLSLVkExtExtensionAttr>()) {
+ Extensions.insert(ExtAttr->getExtensionName());
+ }
+ return true;
+ }
+};
+
void CGHLSLRuntime::finishCodeGen() {
auto &TargetOpts = CGM.getTarget().getTargetOpts();
auto &CodeGenOpts = CGM.getCodeGenOpts();
auto &LangOpts = CGM.getLangOpts();
llvm::Module &M = CGM.getModule();
Triple T(M.getTargetTriple());
+
+ if (T.isSPIRV()) {
+ llvm::StringSet<> SPIRVExtensions;
+ VkExtExtensionVisitor Visitor(SPIRVExtensions);
+ Visitor.TraverseDecl(CGM.getContext().getTranslationUnitDecl());
+
+ if (!SPIRVExtensions.empty()) {
+ llvm::LLVMContext &Ctx = CGM.getLLVMContext();
+ llvm::NamedMDNode *SpirvExtMD =
+ CGM.getModule().getOrInsertNamedMetadata("spirv.ext");
+
+ for (const auto &Ext : SPIRVExtensions) {
+ auto *MStr = llvm::MDString::get(Ctx, Ext.getKey());
+ SpirvExtMD->addOperand(llvm::MDNode::get(Ctx, MStr));
+ }
+ }
+ }
+
if (T.getArch() == Triple::ArchType::dxil)
addDxilValVersion(TargetOpts.DxilValidatorVersion, M);
if (CodeGenOpts.ResMayAlias)
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h
b/clang/lib/CodeGen/CGHLSLRuntime.h
index d76cc18c9a259..f1f10425af4c7 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -65,6 +65,7 @@ class InitListExpr;
class HLSLBufferDecl;
class HLSLRootSignatureDecl;
class HLSLVkBindingAttr;
+class HLSLVkExtExtensionAttr;
class HLSLResourceBindingAttr;
class Type;
class RecordType;
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 8c2577e78b231..929b8aec5b78a 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7965,6 +7965,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
const ParsedAttr &AL,
case ParsedAttr::AT_HLSLVkBinding:
S.HLSL().handleVkBindingAttr(D, AL);
break;
+ case ParsedAttr::AT_HLSLVkExtExtension:
+ S.HLSL().handleVkExtExtensionAttr(D, AL);
+ break;
case ParsedAttr::AT_HLSLGroupSharedAddressSpace:
handleSimpleAttribute<HLSLGroupSharedAddressSpaceAttr>(S, D, AL);
break;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 0619295cd2fbb..7bd931a1fcda1 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1842,6 +1842,14 @@ void SemaHLSL::handleVkBindingAttr(Decl *D, const
ParsedAttr &AL) {
HLSLVkBindingAttr(getASTContext(), AL, Binding, Set));
}
+void SemaHLSL::handleVkExtExtensionAttr(Decl *D, const ParsedAttr &AL) {
+ StringRef Str;
+ if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str))
+ return;
+ D->addAttr(::new (getASTContext())
+ HLSLVkExtExtensionAttr(getASTContext(), AL, Str));
+}
+
void SemaHLSL::handleVkLocationAttr(Decl *D, const ParsedAttr &AL) {
uint32_t Location;
if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), Location))
diff --git
a/clang/test/CodeGenHLSL/vk-features/inline-spirv/spv.inline.extension.hlsl
b/clang/test/CodeGenHLSL/vk-features/inline-spirv/spv.inline.extension.hlsl
new file mode 100644
index 0000000000000..6124ed267195b
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/inline-spirv/spv.inline.extension.hlsl
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -triple spirv1.6-unknown-vulkan1.3-compute -emit-llvm -o -
-O0 %s | FileCheck %s --check-prefixes=CHECK
+
+[[vk::ext_extension("ext_on_global_var")]]
+int global_val;
+
+struct T
+{
+ [[vk::ext_extension("ext_on_field")]]
+ int val;
+};
+
+struct [[vk::ext_extension("ext_on_struct")]] T2
+{
+ int val;
+};
+
+[[vk::ext_extension("ext_on_typedef")]]
+typedef T MyTYpe;
+
+[[vk::ext_extension("ext_on_cbuffer")]]
+cbuffer cb {
+ int cb_val;
+};
+
+[[vk::ext_extension("ext_on_function")]]
+void foo([[vk::ext_extension("ext_on_param")]] int p) {}
+
+[[vk::ext_extension("ext_on_entry_point")]]
+[numthreads(1,1,1)]
+void main() {
+ T t;
+ T2 t2;
+ MyTYpe my_t;
+ [[vk::ext_extension("ext_on_local_var")]]
+ int local = global_val+t.val+t2.val+my_t.val+cb_val;
+ foo(local);
+}
+
+// CHECK: !spirv.ext = !{!2, !3, !4, !5, !6, !7, !8, !9, !10}
+// CHECK-DAG: !2 = !{!"ext_on_local_var"}
+// CHECK-DAG: !3 = !{!"ext_on_field"}
+// CHECK-DAG: !4 = !{!"ext_on_entry_point"}
+// CHECK-DAG: !5 = !{!"ext_on_param"}
+// CHECK-DAG: !6 = !{!"ext_on_function"}
+// CHECK-DAG: !7 = !{!"ext_on_typedef"}
+// CHECK-DAG: !8 = !{!"ext_on_struct"}
+// CHECK-DAG: !9 = !{!"ext_on_cbuffer"}
+// CHECK-DAG: !10 = !{!"ext_on_global_var"}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits