Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[clang] Add spir_kernel attribute #137882

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft

[clang] Add spir_kernel attribute #137882

wants to merge 1 commit into from

Conversation

sarnex
Copy link
Member

@sarnex sarnex commented Apr 29, 2025

SPIR kernels have a specific calling convention, so add an attribute to explicitly specify this calling convention when targetting pure SPIR/SPIRV.

@sarnex sarnex closed this Apr 29, 2025
@sarnex sarnex reopened this Apr 29, 2025
@sarnex sarnex force-pushed the spirattr branch 3 times, most recently from 711a62e to 20498a3 Compare April 30, 2025 17:26
Copy link

github-actions bot commented Apr 30, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@sarnex sarnex marked this pull request as ready for review May 1, 2025 14:14
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen IR generation bugs: mangling, exceptions, etc. debuginfo llvm:binary-utilities labels May 1, 2025
@llvmbot
Copy link
Member

llvmbot commented May 1, 2025

@llvm/pr-subscribers-llvm-binary-utilities
@llvm/pr-subscribers-debuginfo
@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-clang

Author: Nick Sarnie (sarnex)

Changes

SPIR kernels have a specific calling convention, so add an attribute to explicitly specify this calling convention when targetting pure SPIR/SPIRV.


Full diff: https://github.com/llvm/llvm-project/pull/137882.diff

14 Files Affected:

  • (modified) clang/include/clang/Basic/Attr.td (+8)
  • (modified) clang/include/clang/Basic/Specifiers.h (+1)
  • (modified) clang/lib/AST/ItaniumMangle.cpp (+1)
  • (modified) clang/lib/AST/Type.cpp (+2)
  • (modified) clang/lib/AST/TypePrinter.cpp (+3)
  • (modified) clang/lib/Basic/Targets/SPIR.h (+4-2)
  • (modified) clang/lib/CodeGen/CGCall.cpp (+2)
  • (modified) clang/lib/CodeGen/CGDebugInfo.cpp (+2)
  • (modified) clang/lib/CodeGen/Targets/SPIR.cpp (+17)
  • (modified) clang/lib/Sema/SemaDeclAttr.cpp (+6)
  • (modified) clang/test/Misc/pragma-attribute-supported-attributes-list.test (+1)
  • (added) clang/test/Misc/spir-kernel-attr.c (+10)
  • (modified) llvm/include/llvm/BinaryFormat/Dwarf.def (+1)
  • (modified) llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h (+3)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index a734eb6658c3d..c3a3db095bb81 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -472,7 +472,9 @@ def TargetRISCV : TargetArch<["riscv32", "riscv64"]>;
 def TargetX86 : TargetArch<["x86"]>;
 def TargetX86_64 : TargetArch<["x86_64"]>;
 def TargetAnyX86 : TargetArch<["x86", "x86_64"]>;
+def TargetSPIR : TargetArch<["spir", "spir64"]>;
 def TargetSPIRV : TargetArch<["spirv", "spirv32", "spirv64"]>;
+def TargetAnySPIR : TargetArch<!listconcat(TargetSPIR.Arches, TargetSPIRV.Arches)>;
 def TargetWebAssembly : TargetArch<["wasm32", "wasm64"]>;
 def TargetNVPTX : TargetArch<["nvptx", "nvptx64"]>;
 def TargetWindows : TargetSpec {
@@ -1504,6 +1506,12 @@ def NVPTXKernel : InheritableAttr, TargetSpecificAttr<TargetNVPTX> {
   let Documentation = [Undocumented];
 }
 
+def SPIRKernel : InheritableAttr, TargetSpecificAttr<TargetAnySPIR> {
+  let Spellings = [Clang<"spir_kernel">];
+  let Subjects = SubjectList<[Function]>;
+  let Documentation = [Undocumented];
+}
+
 def HIPManaged : InheritableAttr {
   let Spellings = [GNU<"managed">, Declspec<"__managed__">];
   let Subjects = SubjectList<[Var]>;
diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h
index 491badcc804e7..f9a72f378490e 100644
--- a/clang/include/clang/Basic/Specifiers.h
+++ b/clang/include/clang/Basic/Specifiers.h
@@ -289,6 +289,7 @@ namespace clang {
     CC_AAPCS_VFP,          // __attribute__((pcs("aapcs-vfp")))
     CC_IntelOclBicc,       // __attribute__((intel_ocl_bicc))
     CC_SpirFunction,       // default for OpenCL functions on SPIR target
+    CC_SpirKernel,         // __attribute__((spir_kernel))
     CC_OpenCLKernel,       // inferred for OpenCL kernels
     CC_Swift,              // __attribute__((swiftcall))
     CC_SwiftAsync,         // __attribute__((swiftasynccall))
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 33a8728728574..c16005ccaf51f 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -3532,6 +3532,7 @@ StringRef CXXNameMangler::getCallingConvQualifierName(CallingConv CC) {
   case CC_AMDGPUKernelCall:
   case CC_IntelOclBicc:
   case CC_SpirFunction:
+  case CC_SpirKernel:
   case CC_OpenCLKernel:
   case CC_PreserveMost:
   case CC_PreserveAll:
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 59369fba2e772..da9ba8e3e971a 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3623,6 +3623,8 @@ StringRef FunctionType::getNameForCallConv(CallingConv CC) {
     return "intel_ocl_bicc";
   case CC_SpirFunction:
     return "spir_function";
+  case CC_SpirKernel:
+    return "spir_kernel";
   case CC_OpenCLKernel:
     return "opencl_kernel";
   case CC_Swift:
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index cba1a2d98d660..5aa37ca9ec17f 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1115,6 +1115,9 @@ void TypePrinter::printFunctionAfter(const FunctionType::ExtInfo &Info,
     case CC_OpenCLKernel:
       // Do nothing. These CCs are not available as attributes.
       break;
+    case CC_SpirKernel:
+      OS << " __attribute__((spir_kernel))";
+      break;
     case CC_Swift:
       OS << " __attribute__((swiftcall))";
       break;
diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h
index bf249e271a870..43c71a1aeb5d5 100644
--- a/clang/lib/Basic/Targets/SPIR.h
+++ b/clang/lib/Basic/Targets/SPIR.h
@@ -191,8 +191,10 @@ class LLVM_LIBRARY_VISIBILITY BaseSPIRTargetInfo : public TargetInfo {
   }
 
   CallingConvCheckResult checkCallingConvention(CallingConv CC) const override {
-    return (CC == CC_SpirFunction || CC == CC_OpenCLKernel) ? CCCR_OK
-                                                            : CCCR_Warning;
+    return (CC == CC_SpirKernel || CC == CC_SpirFunction ||
+            CC == CC_OpenCLKernel)
+               ? CCCR_OK
+               : CCCR_Warning;
   }
 
   CallingConv getDefaultCallingConv() const override {
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 83b0e8e965770..a7bbff116b584 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -84,6 +84,8 @@ unsigned CodeGenTypes::ClangCallConvToLLVMCallConv(CallingConv CC) {
     return llvm::CallingConv::AMDGPU_KERNEL;
   case CC_SpirFunction:
     return llvm::CallingConv::SPIR_FUNC;
+  case CC_SpirKernel:
+    return llvm::CallingConv::SPIR_KERNEL;
   case CC_OpenCLKernel:
     return CGM.getTargetCodeGenInfo().getOpenCLKernelCallingConv();
   case CC_PreserveMost:
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index f3ec498d4064b..10fba7b772486 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1589,6 +1589,8 @@ static unsigned getDwarfCC(CallingConv CC) {
     return llvm::dwarf::DW_CC_LLVM_IntelOclBicc;
   case CC_SpirFunction:
     return llvm::dwarf::DW_CC_LLVM_SpirFunction;
+  case CC_SpirKernel:
+    return llvm::dwarf::DW_CC_LLVM_SpirKernel;
   case CC_OpenCLKernel:
   case CC_AMDGPUKernelCall:
     return llvm::dwarf::DW_CC_LLVM_OpenCLKernel;
diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp b/clang/lib/CodeGen/Targets/SPIR.cpp
index f35c124f50aa0..29a258e457317 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -60,6 +60,8 @@ class CommonSPIRTargetCodeGenInfo : public TargetCodeGenInfo {
       llvm::Type *ElementType, llvm::LLVMContext &Ctx) const;
   void
   setOCLKernelStubCallingConvention(const FunctionType *&FT) const override;
+  void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
+                           CodeGen::CodeGenModule &M) const override;
 };
 class SPIRVTargetCodeGenInfo : public CommonSPIRTargetCodeGenInfo {
 public:
@@ -238,6 +240,20 @@ void CommonSPIRTargetCodeGenInfo::setOCLKernelStubCallingConvention(
       FT, FT->getExtInfo().withCallingConv(CC_SpirFunction));
 }
 
+void CommonSPIRTargetCodeGenInfo::setTargetAttributes(
+    const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &M) const {
+  const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
+  if (!FD)
+    return;
+
+  llvm::Function *F = cast<llvm::Function>(GV);
+
+  // Attach kernel metadata directly if compiling for SPIR.
+  if (FD->hasAttr<SPIRKernelAttr>()) {
+    F->setCallingConv(llvm::CallingConv::SPIR_KERNEL);
+  }
+}
+
 LangAS
 SPIRVTargetCodeGenInfo::getGlobalVarAddressSpace(CodeGenModule &CGM,
                                                  const VarDecl *D) const {
@@ -262,6 +278,7 @@ SPIRVTargetCodeGenInfo::getGlobalVarAddressSpace(CodeGenModule &CGM,
 
 void SPIRVTargetCodeGenInfo::setTargetAttributes(
     const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &M) const {
+  CommonSPIRTargetCodeGenInfo::setTargetAttributes(D, GV, M);
   if (!M.getLangOpts().HIP ||
       M.getTarget().getTriple().getVendor() != llvm::Triple::AMD)
     return;
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index ab66ae860f86b..56aea1e878f23 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5503,6 +5503,9 @@ bool Sema::CheckCallingConvAttr(const ParsedAttr &Attrs, CallingConv &CC,
                                   llvm::Log2_64(ABIVLen) - 5);
     break;
   }
+  case ParsedAttr::AT_SPIRKernel:
+    CC = CC_SpirKernel;
+    break;
   default: llvm_unreachable("unexpected attribute kind");
   }
 
@@ -7152,6 +7155,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
   case ParsedAttr::AT_CUDALaunchBounds:
     handleLaunchBoundsAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_SPIRKernel:
+    handleSimpleAttribute<SPIRKernelAttr>(S, D, AL);
+    break;
   case ParsedAttr::AT_Restrict:
     handleRestrictAttr(S, D, AL);
     break;
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 7affacb1a109a..53a7ea4c8033b 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -181,6 +181,7 @@
 // CHECK-NEXT: ReturnTypestate (SubjectMatchRule_function, SubjectMatchRule_variable_is_parameter)
 // CHECK-NEXT: ReturnsNonNull (SubjectMatchRule_objc_method, SubjectMatchRule_function)
 // CHECK-NEXT: ReturnsTwice (SubjectMatchRule_function)
+// CHECK-NEXT: SPIRKernel (SubjectMatchRule_function)
 // CHECK-NEXT: SYCLKernelEntryPoint (SubjectMatchRule_function)
 // CHECK-NEXT: SYCLSpecialClass (SubjectMatchRule_record)
 // CHECK-NEXT: ScopedLockable (SubjectMatchRule_record)
diff --git a/clang/test/Misc/spir-kernel-attr.c b/clang/test/Misc/spir-kernel-attr.c
new file mode 100644
index 0000000000000..40de980716ff2
--- /dev/null
+++ b/clang/test/Misc/spir-kernel-attr.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -triple spir -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple spir64 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan-compute -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple spirv32 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple spirv64 -emit-llvm %s -o - | FileCheck %s
+__attribute__((spir_kernel)) void foo(void) {}
+
+[[clang::spir_kernel]] void bar(void) {}
+
+// CHECK-COUNT-2: spir_kernel
diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def
index e52324a8ebc12..575ab05f4e3c4 100644
--- a/llvm/include/llvm/BinaryFormat/Dwarf.def
+++ b/llvm/include/llvm/BinaryFormat/Dwarf.def
@@ -1127,6 +1127,7 @@ HANDLE_DW_CC(0xcd, LLVM_PreserveNone)
 HANDLE_DW_CC(0xce, LLVM_RISCVVectorCall)
 HANDLE_DW_CC(0xcf, LLVM_SwiftTail)
 HANDLE_DW_CC(0xd0, LLVM_RISCVVLSCall)
+HANDLE_DW_CC(0xd1, LLVM_SpirKernel)
 // From GCC source code (include/dwarf2.h): This DW_CC_ value is not currently
 // generated by any toolchain.  It is used internally to GDB to indicate OpenCL
 // C functions that have been compiled with the IBM XL C for OpenCL compiler and
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h
index bd25f6c30ebf1..82337774f2396 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h
@@ -741,6 +741,9 @@ void DWARFTypePrinter<DieType>::appendSubroutineNameAfter(
       // instantiated with function types with these calling conventions won't
       // have distinct names - so we'd need to fix that too)
       break;
+    case dwarf::CallingConvention::DW_CC_LLVM_SpirKernel:
+      OS << " __attribute((spir_kernel))";
+      break;
     case dwarf::CallingConvention::DW_CC_LLVM_Swift:
       // SwiftAsync missing
       OS << " __attribute__((swiftcall))";

@@ -5503,6 +5503,9 @@ bool Sema::CheckCallingConvAttr(const ParsedAttr &Attrs, CallingConv &CC,
llvm::Log2_64(ABIVLen) - 5);
break;
}
case ParsedAttr::AT_SPIRKernel:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a silly request, but please sort this as 'not last'. It is too easy to miss when after a big block.

def SPIRKernel : InheritableAttr, TargetSpecificAttr<TargetAnySPIR> {
let Spellings = [Clang<"spir_kernel">];
let Subjects = SubjectList<[Function]>;
let Documentation = [Undocumented];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this non-documented? WE very much don't approve/accept new undocumented attributes without good reason.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I wasn't sure how strict we were with that, will add documentation.

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need yet another calling convention or is there a way we can start to combine some of these for all the offloading languages? It seems we keep re-implementing the same concepts multiple times for each language and it would be nice to share as much of the frontend logic as possible rather than keep doing this dance of adding another case to every switch in the compiler. :-D

@sarnex
Copy link
Member Author

sarnex commented May 1, 2025

Do we need yet another calling convention or is there a way we can start to combine some of these for all the offloading languages? It seems we keep re-implementing the same concepts multiple times for each language and it would be nice to share as much of the frontend logic as possible rather than keep doing this dance of adding another case to every switch in the compiler. :-D

Do you have an idea? Maybe we could have a calling_convention attribute that takes in a string that specifies the calling convention?

@AaronBallman
Copy link
Collaborator

Do we need yet another calling convention or is there a way we can start to combine some of these for all the offloading languages? It seems we keep re-implementing the same concepts multiple times for each language and it would be nice to share as much of the frontend logic as possible rather than keep doing this dance of adding another case to every switch in the compiler. :-D

Do you have an idea? Maybe we could have a calling_convention attribute that takes in a string that specifies the calling convention?

I was thinking that orthogonal calling conventions could be combined so that we need fewer of them. e.g., if spir_kernel cannot be mixed with nvptx_kernel in the same TU, then there's no need for two distinct internal representations for the attributes, just two distinct spellings that are based on the target.

@sarnex
Copy link
Member Author

sarnex commented May 1, 2025

I was thinking that orthogonal calling conventions could be combined so that we need fewer of them. e.g., if spir_kernel cannot be mixed with nvptx_kernel in the same TU, then there's no need for two distinct internal representations for the attributes, just two distinct spellings that are based on the target.

Ah, I see.

Okay, I'll try, but it might take me a while. Thanks for the feedback. Marking this as a draft for now.

@sarnex sarnex marked this pull request as draft May 1, 2025 15:23
@sarnex
Copy link
Member Author

sarnex commented May 7, 2025

@AaronBallman I just pushed a first attempt to combine the attrs, however I'm not sure if it's much cleaner given the attrs have different subjects/allowed cases/expected warnings/etc. Do you mind taking a first look and seeing if you think the general direction is a good idea? Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category debuginfo llvm:binary-utilities
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants