[clang] [CIR] Upstream support for function/call calling conventions (PR #181170)

Akimasa Watanuki via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 12 08:35:05 PST 2026


https://github.com/Men-cotton updated https://github.com/llvm/llvm-project/pull/181170

>From 248e6a103e6946fd2b42794e5cbce2f3c9ff65bf Mon Sep 17 00:00:00 2001
From: mencotton <mencotton0410 at gmail.com>
Date: Thu, 12 Feb 2026 15:41:35 +0900
Subject: [PATCH 1/3] [CIR] Upstream support for function/call calling
 conventions

---
 .../clang/CIR/Dialect/IR/CIRDialect.td        |  1 +
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 20 +++++-
 .../clang/CIR/Interfaces/CIROpInterfaces.td   |  3 +
 clang/include/clang/CIR/MissingFeatures.h     |  4 +-
 clang/lib/CIR/CodeGen/CIRGenCall.cpp          | 27 +++-----
 clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h    | 32 ++++++++-
 clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp |  9 ---
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        |  7 +-
 clang/lib/CIR/CodeGen/CIRGenTypes.cpp         | 25 ++++++-
 clang/lib/CIR/CodeGen/CIRGenTypes.h           |  3 +
 clang/lib/CIR/CodeGen/TargetInfo.cpp          |  4 ++
 clang/lib/CIR/CodeGen/TargetInfo.h            |  5 ++
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       | 69 ++++++++++++++++---
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 44 +++++++++---
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h   |  2 -
 .../CIR/CodeGen/call-conv-device-kernel.c     | 15 ++++
 .../test/CIR/CodeGen/call-conv-unsupported.c  |  5 ++
 clang/test/CIR/IR/call-op-call-conv.cir       | 19 +++++
 clang/test/CIR/IR/func-call-conv.cir          | 23 +++++++
 .../CIR/Lowering/call-conv-opencl-kernel.cir  |  9 +++
 clang/test/CIR/Lowering/call-op-call-conv.cir | 19 +++++
 clang/test/CIR/Lowering/func-call-conv.cir    | 18 +++++
 22 files changed, 300 insertions(+), 63 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/call-conv-device-kernel.c
 create mode 100644 clang/test/CIR/CodeGen/call-conv-unsupported.c
 create mode 100644 clang/test/CIR/IR/call-op-call-conv.cir
 create mode 100644 clang/test/CIR/IR/func-call-conv.cir
 create mode 100644 clang/test/CIR/Lowering/call-conv-opencl-kernel.cir
 create mode 100644 clang/test/CIR/Lowering/call-op-call-conv.cir
 create mode 100644 clang/test/CIR/Lowering/func-call-conv.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
index 3e134d952b8b5..5d2281be01baf 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
@@ -41,6 +41,7 @@ def CIR_Dialect : Dialect {
     static llvm::StringRef getNoThrowAttrName() { return "nothrow"; }
     static llvm::StringRef getNoReturnAttrName() { return "noreturn"; }
     static llvm::StringRef getSideEffectAttrName() { return "side_effect"; }
+    static llvm::StringRef getCallingConvAttrName() { return "calling_conv"; }
     static llvm::StringRef getReturnsTwiceAttrName() { return "returns_twice"; }
     static llvm::StringRef getColdAttrName() { return "cold"; }
     static llvm::StringRef getHotAttrName() { return "hot"; }
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 4fdffec37c401..eb0552954d3ae 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3091,9 +3091,16 @@ def CIR_OptionalPriorityAttr : OptionalAttr<
   >
 >;
 
-// TODO(CIR): CallingConv is a placeholder here so we can use it in
-// infrastructure calls, but it currently has no values.
-def CIR_CallingConv : CIR_I32EnumAttr<"CallingConv", "calling convention", []>;
+// 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">,
+  I32EnumAttrCase<"SpirFunction", 3, "spir_function">,
+  I32EnumAttrCase<"OpenCLKernel", 4, "opencl_kernel">,
+  I32EnumAttrCase<"PTXKernel", 5, "ptx_kernel">,
+  I32EnumAttrCase<"AMDGPUKernel", 6, "amdgpu_kernel">
+]>;
 
 def CIR_FuncOp : CIR_Op<"func", [
   AutomaticAllocationScope, CallableOpInterface, FunctionOpInterface,
@@ -3109,6 +3116,9 @@ 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. The default calling convention is `CallingConv::C`.
+
     A compiler builtin function must be marked as `builtin` for further
     processing when lowering from CIR.
 
@@ -3178,6 +3188,9 @@ def CIR_FuncOp : CIR_Op<"func", [
       CIR_GlobalLinkageKind,
       "cir::GlobalLinkageKind::ExternalLinkage"
     >:$linkage,
+    DefaultValuedAttr<
+      CIR_CallingConv, "CallingConv::C"
+    >:$calling_conv,
     OptionalAttr<StrAttr>:$sym_visibility,
     UnitAttr:$comdat,
     OptionalAttr<DictArrayAttr>:$arg_attrs,
@@ -3364,6 +3377,7 @@ class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []>
 
   dag commonArgs = (ins OptionalAttr<FlatSymbolRefAttr>:$callee,
       Variadic<CIR_AnyType>:$args,
+      DefaultValuedAttr<CIR_CallingConv, "CallingConv::C">:$calling_conv,
       UnitAttr:$nothrow,
       DefaultValuedAttr<CIR_SideEffect, "SideEffect::All">:$side_effect);
 }
diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
index 4702b4ef08acc..e2255da20866f 100644
--- a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
+++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
@@ -34,6 +34,9 @@ let cppNamespace = "::cir" in {
           "Return the number of operands, accounts for indirect call or "
           "exception info",
           "unsigned", "getNumArgOperands", (ins)>,
+      InterfaceMethod<
+          "Return the calling convention of the call operation",
+          "cir::CallingConv", "getCallingConv", (ins)>,
       InterfaceMethod<"Return whether the callee is nothrow",
                       "bool", "getNothrow", (ins)>,
       InterfaceMethod<"Return the side effects of the call operation",
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 5cb0991326a3c..918a54cd4d571 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -71,7 +71,7 @@ struct MissingFeatures {
   static bool opFuncArmNewAttr() { return false; }
   static bool opFuncArmStreamingAttr() { return false; }
   static bool opFuncAstDeclAttr() { return false; }
-  static bool opFuncCallingConv() { return false; }
+  static bool opFuncCallingConv() { return true; }
   static bool opFuncColdHotAttr() { return false; }
   static bool opFuncCPUAndFeaturesAttributes() { return false; }
   static bool opFuncExceptions() { return false; }
@@ -103,7 +103,7 @@ struct MissingFeatures {
   static bool opCallImplicitObjectSizeArgs() { return false; }
   static bool opCallReturn() { return false; }
   static bool opCallArgEvaluationOrder() { return false; }
-  static bool opCallCallConv() { return false; }
+  static bool opCallCallConv() { return true; }
   static bool opCallSideEffect() { return false; }
   static bool opCallMustTail() { return false; }
   static bool opCallInAlloca() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index cfbba27e12b93..270c2978f52cb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -20,10 +20,9 @@
 using namespace clang;
 using namespace clang::CIRGen;
 
-CIRGenFunctionInfo *
-CIRGenFunctionInfo::create(FunctionType::ExtInfo info, CanQualType resultType,
-                           llvm::ArrayRef<CanQualType> argTypes,
-                           RequiredArgs required) {
+CIRGenFunctionInfo *CIRGenFunctionInfo::create(
+    cir::CallingConv cirCC, FunctionType::ExtInfo info, CanQualType resultType,
+    llvm::ArrayRef<CanQualType> argTypes, RequiredArgs required) {
   // The first slot allocated for arg type slot is for the return value.
   void *buffer = operator new(
       totalSizeToAlloc<CanQualType>(argTypes.size() + 1));
@@ -32,6 +31,9 @@ CIRGenFunctionInfo::create(FunctionType::ExtInfo info, CanQualType resultType,
 
   CIRGenFunctionInfo *fi = new (buffer) CIRGenFunctionInfo();
 
+  fi->callingConvention = cirCC;
+  fi->effectiveCallingConvention = cirCC;
+  fi->astCallingConvention = info.getCC();
   fi->noReturn = info.getNoReturn();
 
   fi->required = required;
@@ -316,7 +318,7 @@ void CIRGenModule::constructAttributeList(llvm::StringRef name,
                                           cir::CallingConv &callingConv,
                                           cir::SideEffect &sideEffect,
                                           bool attrOnCallSite, bool isThunk) {
-  assert(!cir::MissingFeatures::opCallCallConv());
+  callingConv = info.getEffectiveCallingConvention();
   sideEffect = cir::SideEffect::All;
 
   auto addUnitAttr = [&](llvm::StringRef name) {
@@ -799,7 +801,7 @@ emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc,
                cir::FuncType indirectFuncTy, mlir::Value indirectFuncVal,
                cir::FuncOp directFuncOp,
                const SmallVectorImpl<mlir::Value> &cirCallArgs, bool isInvoke,
-               const mlir::NamedAttrList &attrs) {
+               cir::CallingConv callingConv, const mlir::NamedAttrList &attrs) {
   CIRGenBuilderTy &builder = cgf.getBuilder();
 
   assert(!cir::MissingFeatures::opCallSurroundingTry());
@@ -835,6 +837,7 @@ emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc,
 
     callOpWithExceptions =
         builder.createCallOp(callLoc, directFuncOp, cirCallArgs);
+    callOpWithExceptions.setCallingConv(callingConv);
 
     cgf.populateCatchHandlersIfRequired(tryOp);
     return callOpWithExceptions;
@@ -844,13 +847,12 @@ emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc,
 
   cir::CallOp op;
   if (indirectFuncTy) {
-    // TODO(cir): Set calling convention for indirect calls.
-    assert(!cir::MissingFeatures::opCallCallConv());
     op = builder.createIndirectCallOp(callLoc, indirectFuncVal, indirectFuncTy,
                                       cirCallArgs, attrs);
   } else {
     op = builder.createCallOp(callLoc, directFuncOp, cirCallArgs, attrs);
   }
+  op.setCallingConv(callingConv);
 
   return op;
 }
@@ -966,7 +968,6 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
   if (auto calleeFuncOp = dyn_cast<cir::FuncOp>(calleePtr))
     funcName = calleeFuncOp.getName();
 
-  assert(!cir::MissingFeatures::opCallCallConv());
   assert(!cir::MissingFeatures::opCallAttrs());
   cir::CallingConv callingConv;
   cir::SideEffect sideEffect;
@@ -1009,7 +1010,7 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
   mlir::Location callLoc = loc;
   cir::CIRCallOpInterface theCall =
       emitCallLikeOp(*this, loc, indirectFuncTy, indirectFuncVal, directFuncOp,
-                     cirCallArgs, isInvoke, attrs);
+                     cirCallArgs, isInvoke, callingConv, attrs);
 
   if (callOp)
     *callOp = theCall;
@@ -1074,9 +1075,6 @@ void CallArg::copyInto(CIRGenFunction &cgf, Address addr,
 mlir::Value CIRGenFunction::emitRuntimeCall(mlir::Location loc,
                                             cir::FuncOp callee,
                                             ArrayRef<mlir::Value> args) {
-  // TODO(cir): set the calling convention to this runtime call.
-  assert(!cir::MissingFeatures::opFuncCallingConv());
-
   cir::CallOp call = builder.createCallOp(loc, callee, args);
   assert(call->getNumResults() <= 1 &&
          "runtime functions have at most 1 result");
@@ -1149,8 +1147,6 @@ void CIRGenFunction::emitCallArgs(
     AbstractCallee callee, unsigned paramsToSkip) {
   llvm::SmallVector<QualType, 16> argTypes;
 
-  assert(!cir::MissingFeatures::opCallCallConv());
-
   // First, if a prototype was provided, use those argument types.
   bool isVariadic = false;
   if (prototype.p) {
@@ -1158,7 +1154,6 @@ void CIRGenFunction::emitCallArgs(
 
     const auto *fpt = cast<const FunctionProtoType *>(prototype.p);
     isVariadic = fpt->isVariadic();
-    assert(!cir::MissingFeatures::opCallCallConv());
     argTypes.assign(fpt->param_type_begin() + paramsToSkip,
                     fpt->param_type_end());
   }
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
index fb7da9e414139..8ac47757c03ad 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
@@ -16,6 +16,7 @@
 #define LLVM_CLANG_CIR_CIRGENFUNCTIONINFO_H
 
 #include "clang/AST/CanonicalType.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
 #include "clang/CIR/MissingFeatures.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/Support/TrailingObjects.h"
@@ -72,6 +73,16 @@ class RequiredArgs {
 class CIRGenFunctionInfo final
     : public llvm::FoldingSetNode,
       private llvm::TrailingObjects<CIRGenFunctionInfo, CanQualType> {
+  /// The cir::CallingConv to use for this function (as specified by the user).
+  cir::CallingConv callingConvention : 8;
+
+  /// The cir::CallingConv to actually use for this function, which may depend
+  /// on the ABI.
+  cir::CallingConv effectiveCallingConvention : 8;
+
+  /// The clang::CallingConv that this was originally created with.
+  unsigned astCallingConvention : 6;
+
   // Whether this function has noreturn.
   LLVM_PREFERRED_TYPE(bool)
   unsigned noReturn : 1;
@@ -90,13 +101,15 @@ class CIRGenFunctionInfo final
     // here instead of explicit false/0.
     return FunctionType::ExtInfo(
         isNoReturn(), /*getHasRegParm=*/false, /*getRegParm=*/false,
-        /*getASTCallingConvention=*/CallingConv(0), /*isReturnsRetained=*/false,
+        /*getASTCallingConvention=*/getASTCallingConvention(),
+        /*isReturnsRetained=*/false,
         /*isNoCallerSavedRegs=*/false, /*isNoCfCheck=*/false,
         /*isCmseNSCall=*/false);
   }
 
 public:
-  static CIRGenFunctionInfo *create(FunctionType::ExtInfo info,
+  static CIRGenFunctionInfo *create(cir::CallingConv cirCC,
+                                    FunctionType::ExtInfo info,
                                     CanQualType resultType,
                                     llvm::ArrayRef<CanQualType> argTypes,
                                     RequiredArgs required);
@@ -115,6 +128,7 @@ class CIRGenFunctionInfo final
   static void Profile(llvm::FoldingSetNodeID &id, FunctionType::ExtInfo info,
                       RequiredArgs required, CanQualType resultType,
                       llvm::ArrayRef<CanQualType> argTypes) {
+    id.AddInteger(info.getCC());
     id.AddBoolean(info.getNoReturn());
     id.AddBoolean(required.getOpaqueData());
     resultType.Profile(id);
@@ -162,6 +176,20 @@ class CIRGenFunctionInfo final
   }
 
   bool isNoReturn() const { return noReturn; }
+
+  /// getASTCallingConvention - Return the AST-specified calling convention.
+  clang::CallingConv getASTCallingConvention() const {
+    return clang::CallingConv(astCallingConvention);
+  }
+
+  /// getCallingConvention - Return the user specified calling convention.
+  cir::CallingConv getCallingConvention() const { return callingConvention; }
+
+  /// getEffectiveCallingConvention - Return the actual calling convention to
+  /// use, which may depend on the ABI.
+  cir::CallingConv getEffectiveCallingConvention() const {
+    return effectiveCallingConvention;
+  }
 };
 
 } // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index aca2278c3876c..6c59bf1ffadc5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -1891,18 +1891,12 @@ mlir::Value CIRGenItaniumCXXABI::getVirtualBaseClassOffset(
 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());
   return cgf.cgm.createRuntimeFunction(fnTy, "__cxa_bad_cast");
 }
 
 static void emitCallToBadCast(CIRGenFunction &cgf, mlir::Location loc) {
-  // TODO(cir): set the calling convention to the runtime function.
-  assert(!cir::MissingFeatures::opFuncCallingConv());
-
   cgf.emitRuntimeCall(loc, getBadCastFn(cgf));
   cir::UnreachableOp::create(cgf.getBuilder(), loc);
   cgf.getBuilder().clearInsertionPoint();
@@ -1981,9 +1975,6 @@ static cir::FuncOp getItaniumDynamicCastFn(CIRGenFunction &cgf) {
   assert(!cir::MissingFeatures::opFuncWillReturn());
   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);
   return cgf.cgm.createRuntimeFunction(FTy, "__dynamic_cast");
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 007d501f25014..f776206edeabb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2232,11 +2232,7 @@ 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());
+  func.setCallingConv(callingConv);
 }
 
 void CIRGenModule::setFunctionAttributes(GlobalDecl globalDecl,
@@ -2638,7 +2634,6 @@ 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());
     setWindowsItaniumDLLImport(*this, isLocal, entry, name);
     entry.setDSOLocal(true);
   }
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index c4f745b492102..3562e4a0d8223 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -10,6 +10,9 @@
 #include "clang/Basic/TargetInfo.h"
 #include "clang/CIR/Dialect/IR/CIRTypes.h"
 
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/ErrorHandling.h"
+
 #include <cassert>
 
 using namespace clang;
@@ -29,6 +32,23 @@ mlir::MLIRContext &CIRGenTypes::getMLIRContext() const {
   return *builder.getContext();
 }
 
+cir::CallingConv
+CIRGenTypes::ClangCallConvToCIRCallConv(clang::CallingConv CC) {
+  switch (CC) {
+  case CC_C:
+    return cir::CallingConv::C;
+  case CC_DeviceKernel:
+    return cgm.getTargetCIRGenInfo().getDeviceKernelCallingConv();
+  case CC_SpirFunction:
+    return cir::CallingConv::SpirFunction;
+  default:
+    cgm.errorNYI((llvm::Twine("unsupported calling convention: ") +
+                  FunctionType::getNameForCallConv(CC))
+                     .str());
+    return cir::CallingConv::C;
+  }
+}
+
 /// Return true if the specified type in a function parameter or result position
 /// can be converted to a CIR type at this point. This boils down to being
 /// whether it is complete, as well as whether we've temporarily deferred
@@ -683,10 +703,9 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCIRFunctionInfo(
     return *fi;
   }
 
-  assert(!cir::MissingFeatures::opCallCallConv());
-
   // Construction the function info. We co-allocate the ArgInfos.
-  fi = CIRGenFunctionInfo::create(info, returnType, argTypes, required);
+  cir::CallingConv cirCC = ClangCallConvToCIRCallConv(info.getCC());
+  fi = CIRGenFunctionInfo::create(cirCC, info, returnType, argTypes, required);
   functionInfos.InsertNode(fi, insertPos);
 
   return *fi;
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h
index e36e81b07684b..a06fe570d03e2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h
@@ -120,6 +120,9 @@ class CIRGenTypes {
 
   const CIRGenRecordLayout &getCIRGenRecordLayout(const clang::RecordDecl *rd);
 
+  /// Convert clang calling convention to CIR calling convention.
+  cir::CallingConv ClangCallConvToCIRCallConv(clang::CallingConv cc);
+
   /// Convert type T into an mlir::Type. This differs from convertType in that
   /// it is used to convert to the memory representation for a type. For
   /// example, the scalar representation for bool is i1, but the memory
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.cpp b/clang/lib/CIR/CodeGen/TargetInfo.cpp
index 5a0c854db9125..172450fdb7fd3 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CIR/CodeGen/TargetInfo.cpp
@@ -67,6 +67,10 @@ class NVPTXTargetCIRGenInfo : public TargetCIRGenInfo {
 public:
   NVPTXTargetCIRGenInfo(CIRGenTypes &cgt)
       : TargetCIRGenInfo(std::make_unique<NVPTXABIInfo>(cgt)) {}
+
+  cir::CallingConv getDeviceKernelCallingConv() const override {
+    return cir::CallingConv::PTXKernel;
+  }
 };
 } // namespace
 
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h b/clang/lib/CIR/CodeGen/TargetInfo.h
index 79325c2d35c4d..dad0756d352e7 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.h
+++ b/clang/lib/CIR/CodeGen/TargetInfo.h
@@ -52,6 +52,11 @@ class TargetCIRGenInfo {
     return {};
   }
 
+  /// Get CIR calling convention for functions using CC_DeviceKernel.
+  virtual cir::CallingConv getDeviceKernelCallingConv() const {
+    return cir::CallingConv::C;
+  }
+
   /// Determine whether a call to an unprototyped functions under
   /// the given calling convention should use the variadic
   /// convention or the non-variadic convention.
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 9574851a0ae2c..d2a9a9a44052a 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -132,6 +132,7 @@ template <typename Ty> struct EnumTraits {};
 
 REGISTER_ENUM_TYPE(GlobalLinkageKind);
 REGISTER_ENUM_TYPE(VisibilityKind);
+REGISTER_ENUM_TYPE(CallingConv);
 REGISTER_ENUM_TYPE(SideEffect);
 } // namespace
 
@@ -882,16 +883,30 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
   if (parser.resolveOperands(ops, opsFnTy.getInputs(), opsLoc, result.operands))
     return mlir::failure();
 
+  bool hasCallConv = parser.parseOptionalKeyword("cc").succeeded() ||
+                     parser.parseOptionalKeyword("call_conv").succeeded();
+  if (hasCallConv) {
+    if (parser.parseLParen().failed())
+      return failure();
+    cir::CallingConv callingConv;
+    if (parseCIRKeyword<cir::CallingConv>(parser, callingConv).failed())
+      return failure();
+    if (parser.parseRParen().failed())
+      return failure();
+    result.addAttribute(
+        CIRDialect::getCallingConvAttrName(),
+        cir::CallingConvAttr::get(parser.getContext(), callingConv));
+  }
+
   return mlir::success();
 }
 
-static void printCallCommon(mlir::Operation *op,
-                            mlir::FlatSymbolRefAttr calleeSym,
-                            mlir::Value indirectCallee,
-                            mlir::OpAsmPrinter &printer, bool isNothrow,
-                            cir::SideEffect sideEffect,
-                            mlir::Block *normalDest = nullptr,
-                            mlir::Block *unwindDest = nullptr) {
+static void
+printCallCommon(mlir::Operation *op, mlir::FlatSymbolRefAttr calleeSym,
+                mlir::Value indirectCallee, mlir::OpAsmPrinter &printer,
+                bool isNothrow, cir::CallingConv callingConv,
+                cir::SideEffect sideEffect, mlir::Block *normalDest = nullptr,
+                mlir::Block *unwindDest = nullptr) {
   printer << ' ';
 
   auto callLikeOp = mlir::cast<cir::CIRCallOpInterface>(op);
@@ -928,12 +943,18 @@ static void printCallCommon(mlir::Operation *op,
 
   llvm::SmallVector<::llvm::StringRef> elidedAttrs = {
       CIRDialect::getCalleeAttrName(), CIRDialect::getNoThrowAttrName(),
-      CIRDialect::getSideEffectAttrName(),
+      CIRDialect::getSideEffectAttrName(), CIRDialect::getCallingConvAttrName(),
       CIRDialect::getOperandSegmentSizesAttrName()};
   printer.printOptionalAttrDict(op->getAttrs(), elidedAttrs);
   printer << " : ";
   printer.printFunctionalType(op->getOperands().getTypes(),
                               op->getResultTypes());
+
+  if (callingConv != cir::CallingConv::C) {
+    printer << " cc(";
+    printer << stringifyCallingConv(callingConv);
+    printer << ")";
+  }
 }
 
 mlir::ParseResult cir::CallOp::parse(mlir::OpAsmParser &parser,
@@ -945,7 +966,7 @@ void cir::CallOp::print(mlir::OpAsmPrinter &p) {
   mlir::Value indirectCallee = isIndirect() ? getIndirectCall() : nullptr;
   cir::SideEffect sideEffect = getSideEffect();
   printCallCommon(*this, getCalleeAttr(), indirectCallee, p, getNothrow(),
-                  sideEffect);
+                  getCallingConv(), sideEffect);
 }
 
 static LogicalResult
@@ -985,7 +1006,11 @@ verifyCallCommInSymbolUses(mlir::Operation *op,
                << op->getOperand(i).getType() << " for operand number " << i;
   }
 
-  assert(!cir::MissingFeatures::opCallCallConv());
+  // Calling convention must match.
+  if (callIf.getCallingConv() != fn.getCallingConv())
+    return op->emitOpError("calling convention mismatch: expected ")
+           << stringifyCallingConv(fn.getCallingConv()) << ", but provided "
+           << stringifyCallingConv(callIf.getCallingConv());
 
   // Void function must not return any results.
   if (fnType.hasVoidReturn() && op->getNumResults() != 0)
@@ -1061,7 +1086,8 @@ void cir::TryCallOp::print(::mlir::OpAsmPrinter &p) {
   mlir::Value indirectCallee = isIndirect() ? getIndirectCall() : nullptr;
   cir::SideEffect sideEffect = getSideEffect();
   printCallCommon(*this, getCalleeAttr(), indirectCallee, p, getNothrow(),
-                  sideEffect, getNormalDest(), getUnwindDest());
+                  getCallingConv(), sideEffect, getNormalDest(),
+                  getUnwindDest());
 }
 
 //===----------------------------------------------------------------------===//
@@ -2074,6 +2100,21 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
       return failure();
   }
 
+  auto callConvNameAttr = getCallingConvAttrName(state.name);
+  bool hasCallConv = parser.parseOptionalKeyword("cc").succeeded() ||
+                     parser.parseOptionalKeyword("call_conv").succeeded();
+  if (hasCallConv) {
+    CallingConv callConv;
+    if (parser.parseLParen().failed())
+      return failure();
+    if (parseCIRKeyword<CallingConv>(parser, callConv).failed())
+      return parser.emitError(loc) << "unknown calling convention";
+    if (parser.parseRParen().failed())
+      return failure();
+    state.addAttribute(callConvNameAttr,
+                       CallingConvAttr::get(parser.getContext(), callConv));
+  }
+
   auto parseGlobalDtorCtor =
       [&](StringRef keyword,
           llvm::function_ref<void(std::optional<int> prio)> createAttr)
@@ -2293,6 +2334,12 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
     p << ")";
   }
 
+  if (getCallingConv() != CallingConv::C) {
+    p << " cc(";
+    p << stringifyCallingConv(getCallingConv());
+    p << ")";
+  }
+
   if (auto specialMemberAttr = getCxxSpecialMember()) {
     p << " special_member<";
     p.printAttribute(*specialMemberAttr);
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 28b3454d20613..c501f2b4a5f36 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -147,7 +147,7 @@ static mlir::Value emitToMemory(mlir::ConversionPatternRewriter &rewriter,
   return value;
 }
 
-mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage) {
+static mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage) {
   using CIR = cir::GlobalLinkageKind;
   using LLVM = mlir::LLVM::Linkage;
 
@@ -176,6 +176,28 @@ mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage) {
   llvm_unreachable("Unknown CIR linkage type");
 }
 
+static std::optional<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 {
@@ -1815,8 +1837,8 @@ 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::getCallingConvAttrName() ||
         attr.getName() == CIRDialect::getSideEffectAttrName() ||
         attr.getName() == CIRDialect::getNoThrowAttrName() ||
         attr.getName() == CIRDialect::getNoUnwindAttrName() ||
@@ -1840,7 +1862,10 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
   if (converter->convertTypes(cirResults, llvmResults).failed())
     return mlir::failure();
 
-  assert(!cir::MissingFeatures::opCallCallConv());
+  auto cconv = convertCallingConv(call.getCallingConv());
+  if (!cconv)
+    return op->emitOpError("unsupported calling convention for LLVM lowering: ")
+           << stringifyCallingConv(call.getCallingConv());
 
   mlir::LLVM::MemoryEffectsAttr memoryEffects;
   bool noUnwind = false;
@@ -1905,10 +1930,10 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
 
   assert(!cir::MissingFeatures::opCallLandingPad());
   assert(!cir::MissingFeatures::opCallContinueBlock());
-  assert(!cir::MissingFeatures::opCallCallConv());
 
   auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
       op, llvmFnTy, calleeAttr, callOperands);
+  newOp.setCConv(*cconv);
   newOp->setAttrs(attributes);
   if (memoryEffects)
     newOp.setMemoryEffectsAttr(memoryEffects);
@@ -2285,15 +2310,14 @@ mlir::LogicalResult CIRToLLVMFAbsOpLowering::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.getGlobalVisibilityAttrName() ||
         attr.getName() == func.getDsoLocalAttrName() ||
         attr.getName() == func.getInlineKindAttrName() ||
+        attr.getName() == func.getCallingConvAttrName() ||
         attr.getName() == func.getSideEffectAttrName() ||
         attr.getName() == CIRDialect::getNoReturnAttrName() ||
         (filterArgAndResAttrs &&
@@ -2369,13 +2393,15 @@ 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;
+  auto cconv = convertCallingConv(op.getCallingConv());
+  if (!cconv)
+    return op.emitOpError("unsupported calling convention for LLVM lowering: ")
+           << stringifyCallingConv(op.getCallingConv());
   SmallVector<mlir::NamedAttribute, 4> attributes;
   lowerFuncAttributes(op, /*filterArgAndResAttrs=*/false, attributes);
 
   mlir::LLVM::LLVMFuncOp fn = mlir::LLVM::LLVMFuncOp::create(
-      rewriter, loc, op.getName(), llvmFnTy, linkage, isDsoLocal, cconv,
+      rewriter, loc, op.getName(), llvmFnTy, linkage, isDsoLocal, *cconv,
       mlir::SymbolRefAttr(), attributes);
 
   assert(!cir::MissingFeatures::opFuncMultipleReturnVals());
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index 0b5872f963317..726f0bb9e43e8 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -27,8 +27,6 @@ mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp, mlir::Attribute attr,
                                 mlir::ConversionPatternRewriter &rewriter,
                                 const mlir::TypeConverter *converter);
 
-mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage);
-
 void convertSideEffectForCall(mlir::Operation *callOp, bool isNothrow,
                               cir::SideEffect sideEffect,
                               mlir::LLVM::MemoryEffectsAttr &memoryEffect,
diff --git a/clang/test/CIR/CodeGen/call-conv-device-kernel.c b/clang/test/CIR/CodeGen/call-conv-device-kernel.c
new file mode 100644
index 0000000000000..ed4a9f1c45d7d
--- /dev/null
+++ b/clang/test/CIR/CodeGen/call-conv-device-kernel.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -triple nvptx64-nvidia-cuda -x c -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple nvptx64-nvidia-cuda -x c -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -x c -fclangir -emit-cir %s -o %t.x86.cir
+// RUN: FileCheck --input-file=%t.x86.cir %s --check-prefix=X86-CIR --implicit-check-not='cc(spir_kernel)'
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -x c -fclangir -emit-llvm %s -o %t.x86.ll
+// RUN: FileCheck --input-file=%t.x86.ll %s --check-prefix=X86-LLVM --implicit-check-not=spir_kernel
+
+__attribute__((device_kernel)) void k(void) {}
+
+// CIR: cir.func {{.*}} @k() cc(ptx_kernel)
+// LLVM: define{{.*}} ptx_kernel void @k()
+// X86-CIR: cir.func {{.*}} @k()
+// X86-LLVM: define{{.*}} void @k()
diff --git a/clang/test/CIR/CodeGen/call-conv-unsupported.c b/clang/test/CIR/CodeGen/call-conv-unsupported.c
new file mode 100644
index 0000000000000..635513c87853c
--- /dev/null
+++ b/clang/test/CIR/CodeGen/call-conv-unsupported.c
@@ -0,0 +1,5 @@
+// RUN: not %clang_cc1 -triple i686-unknown-linux-gnu -fclangir -emit-cir %s -o /dev/null 2>&1 | FileCheck %s
+
+int __attribute__((stdcall)) foo(int x) { return x; }
+
+// CHECK: error: ClangIR code gen Not Yet Implemented: unsupported calling convention: stdcall
diff --git a/clang/test/CIR/IR/call-op-call-conv.cir b/clang/test/CIR/IR/call-op-call-conv.cir
new file mode 100644
index 0000000000000..e8ae93ae6e731
--- /dev/null
+++ b/clang/test/CIR/IR/call-op-call-conv.cir
@@ -0,0 +1,19 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+!fnptr = !cir.ptr<!cir.func<(!s32i) -> !s32i>>
+
+module {
+  cir.func @my_add(%a: !s32i, %b: !s32i) -> !s32i cc(spir_function) {
+    %c = cir.binop(add, %a, %b) : !s32i
+    cir.return %c : !s32i
+  }
+
+  // CHECK: %{{[0-9]+}} = cir.call %{{.*}}(%{{.*}}) : (!cir.ptr<!cir.func<(!s32i) -> !s32i>>, !s32i) -> !s32i cc(spir_kernel)
+  // CHECK: %{{[0-9]+}} = cir.call %{{.*}}(%{{.*}}) : (!cir.ptr<!cir.func<(!s32i) -> !s32i>>, !s32i) -> !s32i cc(spir_function)
+  cir.func @ind(%fnptr: !fnptr, %a : !s32i) {
+    %1 = cir.call %fnptr(%a) : (!fnptr, !s32i) -> !s32i cc(spir_kernel)
+    %2 = cir.call %fnptr(%a) : (!fnptr, !s32i) -> !s32i cc(spir_function)
+    cir.return
+  }
+}
diff --git a/clang/test/CIR/IR/func-call-conv.cir b/clang/test/CIR/IR/func-call-conv.cir
new file mode 100644
index 0000000000000..e5f18bdd6f971
--- /dev/null
+++ b/clang/test/CIR/IR/func-call-conv.cir
@@ -0,0 +1,23 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+
+module {
+  // CHECK: cir.func @foo()
+  cir.func @foo() cc(c) {
+    cir.return
+  }
+
+  // CHECK: cir.func @bar() cc(spir_kernel)
+  cir.func @bar() cc(spir_kernel) {
+    cir.return
+  }
+
+  // CHECK: cir.func @bar_alias() alias(@bar) cc(spir_kernel)
+  cir.func @bar_alias() alias(@bar) cc(spir_kernel)
+
+  // CHECK: cir.func no_inline @baz() cc(spir_function)
+  cir.func no_inline @baz() cc(spir_function) {
+    cir.return
+  }
+}
diff --git a/clang/test/CIR/Lowering/call-conv-opencl-kernel.cir b/clang/test/CIR/Lowering/call-conv-opencl-kernel.cir
new file mode 100644
index 0000000000000..37948c1b45053
--- /dev/null
+++ b/clang/test/CIR/Lowering/call-conv-opencl-kernel.cir
@@ -0,0 +1,9 @@
+// RUN: cir-opt %s --cir-to-llvm -verify-diagnostics
+
+module {
+  // expected-error @below {{'cir.func' op unsupported calling convention for LLVM lowering: opencl_kernel}}
+  // expected-error @below {{failed to legalize operation 'cir.func' that was explicitly marked illegal}}
+  cir.func @kernel() cc(opencl_kernel) {
+    cir.return
+  }
+}
diff --git a/clang/test/CIR/Lowering/call-op-call-conv.cir b/clang/test/CIR/Lowering/call-op-call-conv.cir
new file mode 100644
index 0000000000000..015b17959e9f9
--- /dev/null
+++ b/clang/test/CIR/Lowering/call-op-call-conv.cir
@@ -0,0 +1,19 @@
+// RUN: cir-translate -cir-to-llvmir --disable-cc-lowering %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
+
+!s32i = !cir.int<s, 32>
+!fnptr = !cir.ptr<!cir.func<(!s32i) -> !s32i>>
+
+module {
+  cir.func private @my_add(%a: !s32i, %b: !s32i) -> !s32i cc(spir_function)
+
+  cir.func @ind(%fnptr: !fnptr, %a : !s32i) {
+    // LLVM: %{{[0-9]+}} = call spir_func i32 %{{[0-9]+}}(i32 %{{[0-9]+}})
+    %1 = cir.call %fnptr(%a) : (!fnptr, !s32i) -> !s32i cc(spir_function)
+
+    // LLVM: %{{[0-9]+}} = call spir_func i32 @my_add(i32 %{{[0-9]+}}, i32 %{{[0-9]+}})
+    %2 = cir.call @my_add(%1, %1) : (!s32i, !s32i) -> !s32i cc(spir_function)
+
+    cir.return
+  }
+}
diff --git a/clang/test/CIR/Lowering/func-call-conv.cir b/clang/test/CIR/Lowering/func-call-conv.cir
new file mode 100644
index 0000000000000..186b6235f4b0d
--- /dev/null
+++ b/clang/test/CIR/Lowering/func-call-conv.cir
@@ -0,0 +1,18 @@
+// RUN: cir-opt %s --cir-to-llvm | FileCheck %s
+
+module {
+  // CHECK: llvm.func @foo()
+  cir.func @foo() cc(c) {
+    cir.return
+  }
+
+  // CHECK: llvm.func{{( spir_kernelcc @bar\(\)| @bar\(\).*spir_kernelcc)}}
+  cir.func @bar() cc(spir_kernel) {
+    cir.return
+  }
+
+  // CHECK: llvm.func{{( spir_funccc @baz\(\)| @baz\(\).*spir_funccc)}}
+  cir.func @baz() cc(spir_function) {
+    cir.return
+  }
+}

>From 9d281cb78bcde92248af69c51db6bb816907fbb2 Mon Sep 17 00:00:00 2001
From: mencotton <mencotton0410 at gmail.com>
Date: Fri, 13 Feb 2026 01:10:13 +0900
Subject: [PATCH 2/3] fix: add a missing tests

---
 clang/test/CIR/IR/invalid-func.cir | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/clang/test/CIR/IR/invalid-func.cir b/clang/test/CIR/IR/invalid-func.cir
index 6c06079ed8fd8..09cc1892863e1 100644
--- a/clang/test/CIR/IR/invalid-func.cir
+++ b/clang/test/CIR/IR/invalid-func.cir
@@ -1,4 +1,4 @@
-// RUN: cir-opt %s -verify-diagnostics
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
 
 module {
   cir.func @l0() {
@@ -9,3 +9,26 @@ module {
     cir.return
   }
 }
+
+// -----
+
+module {
+  cir.func @subroutine() cc(spir_function) {
+    cir.return
+  }
+
+  cir.func @call_conv_mismatch() {
+    // expected-error @+1 {{'cir.call' op calling convention mismatch: expected spir_function, but provided spir_kernel}}
+    cir.call @subroutine() : () -> () cc(spir_kernel)
+    cir.return
+  }
+}
+
+// -----
+
+module {
+  // expected-error @+1 {{unknown calling convention}}
+  cir.func @foo() cc(foobar) {
+    cir.return
+  }
+}

>From 58dc99cd1df395fa0901dcf936a698ba573bc2f5 Mon Sep 17 00:00:00 2001
From: mencotton <mencotton0410 at gmail.com>
Date: Fri, 13 Feb 2026 01:34:17 +0900
Subject: [PATCH 3/3] fix: CI failure

---
 clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index c501f2b4a5f36..18266069223e6 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -189,7 +189,8 @@ convertCallingConv(cir::CallingConv callingConv) {
   case CIR::SpirFunction:
     return LLVM::SPIR_FUNC;
   case CIR::OpenCLKernel:
-    llvm_unreachable("NYI");
+    // TODO(cir): Implement OpenCL kernel calling convention lowering.
+    return std::nullopt;
   case CIR::PTXKernel:
     return LLVM::PTX_Kernel;
   case CIR::AMDGPUKernel:



More information about the cfe-commits mailing list