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 <[email protected]> Date: Thu, 12 Feb 2026 15:41:35 +0900 Subject: [PATCH 1/2] [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 <[email protected]> Date: Fri, 13 Feb 2026 01:10:13 +0900 Subject: [PATCH 2/2] 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 + } +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
