[llvm-branch-commits] [clang] [CIR] Add calling_conv attribute to FuncOp with lowering support (PR #189345)

Rana Pratap Reddy via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Mar 30 02:41:13 PDT 2026


https://github.com/ranapratap55 updated https://github.com/llvm/llvm-project/pull/189345

>From 3bc8c5760ca0ac9268142f90d3558fa8c5c20814 Mon Sep 17 00:00:00 2001
From: ranapratap55 <RanaPratapReddy.Nimmakayala at amd.com>
Date: Mon, 30 Mar 2026 14:54:22 +0530
Subject: [PATCH] [CIR] Add calling_conv attribute to FuncOp with lowering
 support

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 17 ++++++++-
 clang/include/clang/CIR/MissingFeatures.h     |  1 -
 clang/lib/CIR/CodeGen/CIRGenCall.cpp          |  4 +-
 clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp |  6 +--
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        | 11 +++---
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       | 25 +++++++++++-
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 28 +++++++++++---
 clang/test/CIR/IR/calling-conv.cir            | 38 +++++++++++++++++++
 clang/test/CIR/Lowering/calling-conv.cir      | 34 +++++++++++++++++
 9 files changed, 143 insertions(+), 21 deletions(-)
 create mode 100644 clang/test/CIR/IR/calling-conv.cir
 create mode 100644 clang/test/CIR/Lowering/calling-conv.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 0f3d2c10cf678..310aa78fb0bbf 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3586,6 +3586,8 @@ def CIR_OptionalPriorityAttr : OptionalAttr<
   >
 >;
 
+// The enumeration values are not necessarily in sync with `clang::CallingConv`
+// or `llvm::CallingConv`.
 def CIR_CallingConv : CIR_I32EnumAttr<"CallingConv", "calling convention", [
   I32EnumAttrCase<"C", 1, "c">,
   I32EnumAttrCase<"SpirKernel", 2, "spir_kernel">,
@@ -3609,6 +3611,11 @@ def CIR_FuncOp : CIR_Op<"func", [
     The function linkage information is specified by `linkage`, as defined by
     `GlobalLinkageKind` attribute.
 
+    The `calling_conv` attribute specifies the calling convention of the function.
+    By default calling convention is `CallingConv::C`. When printed, C calling
+    convention is omitted. Other calling conventions are printed as `cc(<mnemonic>)`,
+    e.g. `cc(amdgpu_kernel)`.
+
     A compiler builtin function must be marked as `builtin` for further
     processing when lowering from CIR.
 
@@ -3650,6 +3657,9 @@ def CIR_FuncOp : CIR_Op<"func", [
     // Linkage information
     cir.func linkonce_odr @some_method(...)
 
+    // Calling convention information
+    cir.func @func1(...) cc(amdgpu_kernel)
+
     // Inline information
     cir.func no_inline @some_method(...)
     
@@ -3678,6 +3688,10 @@ def CIR_FuncOp : CIR_Op<"func", [
       CIR_GlobalLinkageKind,
       "cir::GlobalLinkageKind::ExternalLinkage"
     >:$linkage,
+    DefaultValuedAttr<
+      CIR_CallingConv,
+      "cir::CallingConv::C"
+    >:$calling_conv,
     OptionalAttr<StrAttr>:$sym_visibility,
     UnitAttr:$comdat,
     OptionalAttr<DictArrayAttr>:$arg_attrs,
@@ -3696,7 +3710,8 @@ def CIR_FuncOp : CIR_Op<"func", [
 
   let builders = [OpBuilder<(ins
     "llvm::StringRef":$sym_name, "FuncType":$type,
-    CArg<"cir::GlobalLinkageKind", "cir::GlobalLinkageKind::ExternalLinkage">:$linkage)
+    CArg<"cir::GlobalLinkageKind", "cir::GlobalLinkageKind::ExternalLinkage">:$linkage,
+    CArg<"cir::CallingConv", "cir::CallingConv::C">:$callingConv)
   >];
 
   let extraClassDeclaration = [{
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 68db08a5580ca..a69c63a638568 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -71,7 +71,6 @@ struct MissingFeatures {
   static bool opFuncArmNewAttr() { return false; }
   static bool opFuncArmStreamingAttr() { return false; }
   static bool opFuncAstDeclAttr() { return false; }
-  static bool opFuncCallingConv() { return false; }
   static bool opFuncColdHotAttr() { return false; }
   static bool opFuncCPUAndFeaturesAttributes() { return false; }
   static bool opFuncExceptions() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 35479fa8097ce..ccb73a36209c5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -1293,8 +1293,8 @@ mlir::Value CIRGenFunction::emitRuntimeCall(mlir::Location loc,
                                             cir::FuncOp callee,
                                             ArrayRef<mlir::Value> args,
                                             mlir::NamedAttrList attrs) {
-  // TODO(cir): set the calling convention to this runtime call.
-  assert(!cir::MissingFeatures::opFuncCallingConv());
+
+  // TODO(cir): set the calling convention of the runtime function.
 
   cir::CallOp call = builder.createCallOp(loc, callee, args);
   assert(call->getNumResults() <= 1 &&
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index cafb2c049d063..f8ec2f1c3e9d4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -2075,7 +2075,6 @@ static cir::FuncOp getBadCastFn(CIRGenFunction &cgf) {
   // Prototype: void __cxa_bad_cast();
 
   // TODO(cir): set the calling convention of the runtime function.
-  assert(!cir::MissingFeatures::opFuncCallingConv());
 
   cir::FuncType fnTy =
       cgf.getBuilder().getFuncType({}, cgf.getBuilder().getVoidTy());
@@ -2083,8 +2082,7 @@ static cir::FuncOp getBadCastFn(CIRGenFunction &cgf) {
 }
 
 static void emitCallToBadCast(CIRGenFunction &cgf, mlir::Location loc) {
-  // TODO(cir): set the calling convention to the runtime function.
-  assert(!cir::MissingFeatures::opFuncCallingConv());
+  // TODO(cir): set the calling convention of the runtime function.
 
   cgf.emitRuntimeCall(loc, getBadCastFn(cgf));
   cir::UnreachableOp::create(cgf.getBuilder(), loc);
@@ -2164,8 +2162,6 @@ static cir::FuncOp getItaniumDynamicCastFn(CIRGenFunction &cgf) {
   assert(!cir::MissingFeatures::opFuncReadOnly());
 
   // TODO(cir): set the calling convention of the runtime function.
-  assert(!cir::MissingFeatures::opFuncCallingConv());
-
   cir::FuncType FTy = cgf.getBuilder().getFuncType(
       {voidPtrTy, rttiPtrTy, rttiPtrTy, ptrDiffTy}, voidPtrTy);
   cir::FuncOp fn = cgf.cgm.createRuntimeFunction(FTy, "__dynamic_cast");
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 6f6cd43a5ca09..2cd720b8f6536 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2545,11 +2545,9 @@ void CIRGenModule::setCIRFunctionAttributes(GlobalDecl globalDecl,
 
   // TODO(cir): Check X86_VectorCall incompatibility wiht WinARM64EC
 
-  // TODO(cir): typically the calling conv is set right here, but since
-  // cir::CallingConv is empty and we've not yet added calling-conv to FuncOop,
-  // this isn't really useful here.  This should call func.setCallingConv/etc
-  // later.
-  assert(!cir::MissingFeatures::opFuncCallingConv());
+  // TODO(cir): Apply the calling convention computed by constructAttributeList
+  // to the function. Requires CodeGen target wiring (e.g., AMDGPU sets
+  // AMDGPUKernel). See CIRGenModule::setCIRFunctionAttributes in OG.
 }
 
 void CIRGenModule::setFunctionAttributes(GlobalDecl globalDecl,
@@ -2991,7 +2989,8 @@ cir::FuncOp CIRGenModule::createRuntimeFunction(cir::FuncType ty,
   if (entry) {
     // TODO(cir): set the attributes of the function.
     assert(!cir::MissingFeatures::setLLVMFunctionFEnvAttributes());
-    assert(!cir::MissingFeatures::opFuncCallingConv());
+    // TODO(cir): Set the calling convention on runtime functions if needed.
+    // Runtime functions typically use CallingConv::C (the default).
     setWindowsItaniumDLLImport(*this, isLocal, entry, name);
     entry.setDSOLocal(true);
   }
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index eb322d135a804..bed5444e516b6 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -138,6 +138,7 @@ template <typename Ty> struct EnumTraits {};
 REGISTER_ENUM_TYPE(GlobalLinkageKind);
 REGISTER_ENUM_TYPE(VisibilityKind);
 REGISTER_ENUM_TYPE(SideEffect);
+REGISTER_ENUM_TYPE(CallingConv);
 } // namespace
 
 /// Parse an enum from the keyword, or default to the provided default value.
@@ -2045,7 +2046,7 @@ static llvm::StringRef getLinkageAttrNameString() { return "linkage"; }
 
 void cir::FuncOp::build(OpBuilder &builder, OperationState &result,
                         StringRef name, FuncType type,
-                        GlobalLinkageKind linkage) {
+                        GlobalLinkageKind linkage, CallingConv callingConv) {
   result.addRegion();
   result.addAttribute(SymbolTable::getSymbolAttrName(),
                       builder.getStringAttr(name));
@@ -2054,6 +2055,8 @@ void cir::FuncOp::build(OpBuilder &builder, OperationState &result,
   result.addAttribute(
       getLinkageAttrNameString(),
       GlobalLinkageKindAttr::get(builder.getContext(), linkage));
+  result.addAttribute(getCallingConvAttrName(result.name),
+                      CallingConvAttr::get(builder.getContext(), callingConv));
   result.addAttribute(getGlobalVisibilityAttrName(result.name),
                       cir::VisibilityAttr::get(builder.getContext()));
 }
@@ -2192,6 +2195,20 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
       return failure();
   }
 
+  // Default to C calling convention if no keyword is provided.
+  mlir::StringAttr callConvNameAttr = getCallingConvAttrName(state.name);
+  cir::CallingConv callConv = cir::CallingConv::C;
+  if (parser.parseOptionalKeyword("cc").succeeded()) {
+    if (parser.parseLParen().failed())
+      return failure();
+    if (parseCIRKeyword<cir::CallingConv>(parser, callConv).failed())
+      return parser.emitError(loc) << "unknown calling convention";
+    if (parser.parseRParen().failed())
+      return failure();
+  }
+  state.addAttribute(callConvNameAttr,
+                     cir::CallingConvAttr::get(parser.getContext(), callConv));
+
   auto parseGlobalDtorCtor =
       [&](StringRef keyword,
           llvm::function_ref<void(std::optional<int> prio)> createAttr)
@@ -2406,6 +2423,12 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
     p << ")";
   }
 
+  if (getCallingConv() != cir::CallingConv::C) {
+    p << " cc(";
+    p << stringifyCallingConv(getCallingConv());
+    p << ")";
+  }
+
   if (std::optional<StringRef> personalityName = getPersonality()) {
     p << " personality(";
     p.printSymbolName(*personalityName);
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 9fa0e720e1591..367adac0e59b1 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -182,6 +182,27 @@ mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage) {
   llvm_unreachable("Unknown CIR linkage type");
 }
 
+static mlir::LLVM::CConv convertCallingConv(cir::CallingConv callingConv) {
+  using CIR = cir::CallingConv;
+  using LLVM = mlir::LLVM::CConv;
+
+  switch (callingConv) {
+  case CIR::C:
+    return LLVM::C;
+  case CIR::SpirKernel:
+    return LLVM::SPIR_KERNEL;
+  case CIR::SpirFunction:
+    return LLVM::SPIR_FUNC;
+  case CIR::OpenCLKernel:
+    llvm_unreachable("NYI");
+  case CIR::PTXKernel:
+    return LLVM::PTX_Kernel;
+  case CIR::AMDGPUKernel:
+    return LLVM::AMDGPU_KERNEL;
+  }
+  llvm_unreachable("Unknown calling convention");
+}
+
 mlir::LogicalResult CIRToLLVMCopyOpLowering::matchAndRewrite(
     cir::CopyOp op, OpAdaptor adaptor,
     mlir::ConversionPatternRewriter &rewriter) const {
@@ -1885,7 +1906,6 @@ mlir::LogicalResult CIRToLLVMRotateOpLowering::matchAndRewrite(
 static void lowerCallAttributes(cir::CIRCallOpInterface op,
                                 SmallVectorImpl<mlir::NamedAttribute> &result) {
   for (mlir::NamedAttribute attr : op->getAttrs()) {
-    assert(!cir::MissingFeatures::opFuncCallingConv());
     if (attr.getName() == CIRDialect::getCalleeAttrName() ||
         attr.getName() == CIRDialect::getSideEffectAttrName() ||
         attr.getName() == CIRDialect::getNoThrowAttrName() ||
@@ -2382,12 +2402,11 @@ mlir::LogicalResult CIRToLLVMAbsOpLowering::matchAndRewrite(
 void CIRToLLVMFuncOpLowering::lowerFuncAttributes(
     cir::FuncOp func, bool filterArgAndResAttrs,
     SmallVectorImpl<mlir::NamedAttribute> &result) const {
-  assert(!cir::MissingFeatures::opFuncCallingConv());
   for (mlir::NamedAttribute attr : func->getAttrs()) {
-    assert(!cir::MissingFeatures::opFuncCallingConv());
     if (attr.getName() == mlir::SymbolTable::getSymbolAttrName() ||
         attr.getName() == func.getFunctionTypeAttrName() ||
         attr.getName() == getLinkageAttrNameString() ||
+        attr.getName() == func.getCallingConvAttrName() ||
         attr.getName() == func.getGlobalVisibilityAttrName() ||
         attr.getName() == func.getDsoLocalAttrName() ||
         attr.getName() == func.getInlineKindAttrName() ||
@@ -2466,8 +2485,7 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
          "expected single location or unknown location here");
 
   mlir::LLVM::Linkage linkage = convertLinkage(op.getLinkage());
-  assert(!cir::MissingFeatures::opFuncCallingConv());
-  mlir::LLVM::CConv cconv = mlir::LLVM::CConv::C;
+  mlir::LLVM::CConv cconv = convertCallingConv(op.getCallingConv());
   SmallVector<mlir::NamedAttribute, 4> attributes;
   lowerFuncAttributes(op, /*filterArgAndResAttrs=*/false, attributes);
 
diff --git a/clang/test/CIR/IR/calling-conv.cir b/clang/test/CIR/IR/calling-conv.cir
new file mode 100644
index 0000000000000..3f191aa647206
--- /dev/null
+++ b/clang/test/CIR/IR/calling-conv.cir
@@ -0,0 +1,38 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+
+module {
+  // CHECK: cir.func @default_cc(%arg0: !s32i) -> !s32i
+  cir.func @default_cc(%arg0: !s32i) -> !s32i {
+    cir.return %arg0 : !s32i
+  }
+
+  // CHECK: cir.func @amdgpu_kernel_func() cc(amdgpu_kernel)
+  cir.func @amdgpu_kernel_func() cc(amdgpu_kernel) {
+    cir.return
+  }
+
+  // CHECK: cir.func @spir_kernel_func() cc(spir_kernel)
+  cir.func @spir_kernel_func() cc(spir_kernel) {
+    cir.return
+  }
+
+  // CHECK: cir.func @spir_function_func() cc(spir_function)
+  cir.func @spir_function_func() cc(spir_function) {
+    cir.return
+  }
+
+  // CHECK: cir.func @ptx_kernel_func() cc(ptx_kernel)
+  cir.func @ptx_kernel_func() cc(ptx_kernel) {
+    cir.return
+  }
+
+  // CHECK: cir.func no_inline dso_local @amdgpu_noinline() cc(amdgpu_kernel)
+  cir.func no_inline dso_local @amdgpu_noinline() cc(amdgpu_kernel) {
+    cir.return
+  }
+
+  // CHECK: cir.func private @amdgpu_decl() cc(amdgpu_kernel)
+  cir.func private @amdgpu_decl() cc(amdgpu_kernel)
+}
diff --git a/clang/test/CIR/Lowering/calling-conv.cir b/clang/test/CIR/Lowering/calling-conv.cir
new file mode 100644
index 0000000000000..092050e83780d
--- /dev/null
+++ b/clang/test/CIR/Lowering/calling-conv.cir
@@ -0,0 +1,34 @@
+// RUN: cir-opt %s -cir-to-llvm -o - | FileCheck %s -check-prefix=MLIR
+// RUN: cir-translate %s -cir-to-llvmir --disable-cc-lowering | FileCheck %s -check-prefix=LLVM
+
+module {
+  // MLIR: llvm.func @default_cc()
+  // LLVM: define void @default_cc()
+  cir.func @default_cc() {
+    cir.return
+  }
+
+  // MLIR: llvm.func amdgpu_kernelcc @amdgpu_kernel_func()
+  // LLVM: define amdgpu_kernel void @amdgpu_kernel_func()
+  cir.func @amdgpu_kernel_func() cc(amdgpu_kernel) {
+    cir.return
+  }
+
+  // MLIR: llvm.func spir_kernelcc @spir_kernel_func()
+  // LLVM: define spir_kernel void @spir_kernel_func()
+  cir.func @spir_kernel_func() cc(spir_kernel) {
+    cir.return
+  }
+
+  // MLIR: llvm.func spir_funccc @spir_function_func()
+  // LLVM: define spir_func void @spir_function_func()
+  cir.func @spir_function_func() cc(spir_function) {
+    cir.return
+  }
+
+  // MLIR: llvm.func ptx_kernelcc @ptx_kernel_func()
+  // LLVM: define ptx_kernel void @ptx_kernel_func()
+  cir.func @ptx_kernel_func() cc(ptx_kernel) {
+    cir.return
+  }
+}



More information about the llvm-branch-commits mailing list