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

Reply via email to