[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:39:26 PDT 2026
https://github.com/ranapratap55 created https://github.com/llvm/llvm-project/pull/189345
Adds `calling_conv` attribute to `FuncOp` with support (`cc(amdgpu_kernel)` syntax) and LLVM lowering.
Continuation of #188715 and a partial upstreaming of [clangir#760](https://github.com/llvm/clangir/pull/760/).
>From 89679edf2c08a69db22fc0677ad52b6c41411045 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 | 15 +++++++-
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 | 12 +++---
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, 142 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..2c9d00bfaca31 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3609,6 +3609,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 +3655,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 +3686,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 +3708,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..dccb9fa099d27 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -1293,9 +1293,9 @@ 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 &&
"runtime functions have at most 1 result");
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..a8ce4e9ddedf9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2545,11 +2545,10 @@ 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 +2990,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..736fe42cdf067 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,8 @@ 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 +2056,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 +2196,19 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
return failure();
}
+ 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..6f2ef7a428e82 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