https://github.com/koparasy updated https://github.com/llvm/llvm-project/pull/230123
>From df5fca3a2572f053ffcc49329b1d4be17bdf37d7 Mon Sep 17 00:00:00 2001 From: Konstantinos Parasyris <[email protected]> Date: Thu, 8 Oct 2026 05:44:41 -0700 Subject: [PATCH 1/2] [CIR] Emit calling convention on call sites --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 15 +++-- .../clang/CIR/Dialect/IR/CIRDialect.td | 1 + clang/include/clang/CIR/Dialect/IR/CIROps.td | 10 ++- .../clang/CIR/Interfaces/CIROpInterfaces.td | 11 ++++ clang/lib/CIR/CodeGen/CIRGenCXX.cpp | 8 ++- clang/lib/CIR/CodeGen/CIRGenCall.cpp | 29 ++++----- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 5 +- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 1 + clang/lib/CIR/CodeGen/CIRGenVTables.cpp | 5 +- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 19 ++++++ .../CIR/Dialect/Transforms/CXXABILowering.cpp | 11 ++++ .../CIR/Dialect/Transforms/EHABILowering.cpp | 10 +++ .../Dialect/Transforms/LoweringPrepare.cpp | 31 +++++++-- .../TargetLowering/LowerItaniumCXXABI.cpp | 5 ++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 7 +- .../CIR/CodeGen/spir-call-calling-conv.cpp | 65 +++++++++++++++++++ .../CIR/CodeGenOpenCL/call-calling-conv.cl | 31 +++++++++ .../CodeGenSYCL/address-space-conversions.cpp | 34 +++++----- .../CIR/CodeGenSYCL/call-calling-conv.cpp | 38 +++++++++++ .../CIR/CodeGenSYCL/local-temporaries.cpp | 22 +++---- clang/test/CIR/IR/call-calling-conv.cir | 39 +++++++++++ clang/test/CIR/Lowering/call-calling-conv.cir | 40 ++++++++++++ .../CIR/Transforms/flatten-preserve-attrs.cir | 20 ++++++ 23 files changed, 391 insertions(+), 66 deletions(-) create mode 100644 clang/test/CIR/CodeGen/spir-call-calling-conv.cpp create mode 100644 clang/test/CIR/CodeGenOpenCL/call-calling-conv.cl create mode 100644 clang/test/CIR/CodeGenSYCL/call-calling-conv.cpp create mode 100644 clang/test/CIR/IR/call-calling-conv.cir create mode 100644 clang/test/CIR/Lowering/call-calling-conv.cir diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 24c98752bfd640..178d876c655d81 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -613,9 +613,12 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { mlir::Type returnType, mlir::ValueRange operands, llvm::ArrayRef<mlir::NamedAttribute> attrs = {}, llvm::ArrayRef<mlir::NamedAttrList> argAttrs = {}, - llvm::ArrayRef<mlir::NamedAttribute> resAttrs = {}) { + llvm::ArrayRef<mlir::NamedAttribute> resAttrs = {}, + cir::CallingConv callingConv = cir::CallingConv::C) { auto op = cir::CallOp::create(*this, loc, callee, returnType, operands); op->setAttrs(attrs); + if (callingConv != cir::CallingConv::C) + op.setCallingConv(callingConv); if (!argAttrs.empty()) { llvm::SmallVector<mlir::Attribute> argDictAttrs; @@ -641,10 +644,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { mlir::ValueRange operands, llvm::ArrayRef<mlir::NamedAttribute> attrs = {}, llvm::ArrayRef<mlir::NamedAttrList> argAttrs = {}, - llvm::ArrayRef<mlir::NamedAttribute> resAttrs = {}) { + llvm::ArrayRef<mlir::NamedAttribute> resAttrs = {}, + cir::CallingConv callingConv = cir::CallingConv::C) { return createCallOp(loc, mlir::SymbolRefAttr::get(callee), callee.getFunctionType().getReturnType(), operands, - attrs, argAttrs, resAttrs); + attrs, argAttrs, resAttrs, callingConv); } cir::CallOp @@ -652,12 +656,13 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { cir::FuncType funcType, mlir::ValueRange operands, llvm::ArrayRef<mlir::NamedAttribute> attrs = {}, llvm::ArrayRef<mlir::NamedAttrList> argAttrs = {}, - llvm::ArrayRef<mlir::NamedAttribute> resAttrs = {}) { + llvm::ArrayRef<mlir::NamedAttribute> resAttrs = {}, + cir::CallingConv callingConv = cir::CallingConv::C) { llvm::SmallVector<mlir::Value> resOperands{indirectTarget}; resOperands.append(operands.begin(), operands.end()); return createCallOp(loc, mlir::SymbolRefAttr(), funcType.getReturnType(), - resOperands, attrs, argAttrs, resAttrs); + resOperands, attrs, argAttrs, resAttrs, callingConv); } cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee, diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td index 70647fa1943292..2086646949a116 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td @@ -47,6 +47,7 @@ def CIR_Dialect : Dialect { static llvm::StringRef getIntTypeWidthAttrName() { return "cir.int_type_width"; } static llvm::StringRef getOptInfoAttrName() { return "cir.opt_info"; } static llvm::StringRef getCalleeAttrName() { return "callee"; } + static llvm::StringRef getCallingConvAttrName() { return "calling_conv"; } static llvm::StringRef getNoThrowAttrName() { return "nothrow"; } static llvm::StringRef getNoReturnAttrName() { return "noreturn"; } static llvm::StringRef getMemoryEffectsAttrName() { return "memory_effects"; } diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 70f83275e8f6a9..82c651a84c5d73 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4682,7 +4682,9 @@ class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []> UnitAttr:$musttail, OptionalAttr<CIR_MemoryEffectsAttr>:$memory_effects, OptionalAttr<DictArrayAttr>:$arg_attrs, - OptionalAttr<DictArrayAttr>:$res_attrs + OptionalAttr<DictArrayAttr>:$res_attrs, + DefaultValuedAttr<CIR_CallingConvAttr, "cir::CallingConv::C"> + :$calling_conv ); } @@ -4701,10 +4703,16 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> { empty. The first operand of this operation must be a pointer to the callee function. The rest operands are arguments to the callee function. + The `calling_conv` attribute specifies the calling convention used at the + call site. It defaults to `CallingConv::C`, which is omitted when printed. + Other calling conventions are printed as `cc(<mnemonic>)`, e.g. + `cc(spir_function)`. + Example: ``` %0 = cir.call @foo() + cir.call @bar() cc(spir_function) : () -> () ``` }]; diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td index 52aa1b9fe1b434..1563fc5aa00da3 100644 --- a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td +++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td @@ -73,6 +73,17 @@ let cppNamespace = "::cir" in { /*defaultImplementation=*/[{ return $_op.getInlineKindAttrName(); }]>, + InterfaceMethod<"Return the calling convention of the call site", + "cir::CallingConv", "getCallingConv", (ins), [{}], + /*defaultImplementation=*/[{ + return $_op.getCallingConv(); + }]>, + InterfaceMethod<"Set the calling convention of the call site", + "void", "setCallingConv", + (ins "cir::CallingConv":$callingConv), [{}], + /*defaultImplementation=*/[{ + $_op.setCallingConv(callingConv); + }]>, ]; } diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp index eae118ddf11044..e842500e5ba564 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp @@ -206,9 +206,11 @@ static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd, mlir::cast<cir::PointerType>(thisAddr.getType()).getAddrSpace()); if (realPtrTy != thisAddr.getType()) thisAddr = builder.createBitcast(thisAddr.getLoc(), thisAddr, realPtrTy); - builder.createCallOp(cgf.getLoc(vd->getSourceRange()), - mlir::FlatSymbolRefAttr::get(fnOp.getSymNameAttr()), - mlir::ValueRange{thisAddr}); + // Make sure the call and the callee agree on calling convention. + builder.createCallOp(cgf.getLoc(vd->getSourceRange()), fnOp, + mlir::ValueRange{thisAddr}, /*attrs=*/{}, + /*argAttrs=*/{}, /*resAttrs=*/{}, + fnOp.getCallingConv()); assert(fnOp && "expected cir.func"); // TODO(cir): This doesn't do anything but check for unhandled conditions. // What it is meant to do should really be happening in LoweringPrepare. diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 4f4e10aa218be9..6e67dc13722974 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -1165,14 +1165,13 @@ void CIRGenFunction::emitNonNullArgCheck(RValue rv, QualType argType, cgm.errorNYI("non-null arg check is NYI"); } -static cir::CIRCallOpInterface -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, - llvm::ArrayRef<mlir::NamedAttrList> argAttrs, - const mlir::NamedAttrList &retAttrs) { +static cir::CIRCallOpInterface 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, + llvm::ArrayRef<mlir::NamedAttrList> argAttrs, + const mlir::NamedAttrList &retAttrs, cir::CallingConv callingConv) { CIRGenBuilderTy &builder = cgf.getBuilder(); assert(!cir::MissingFeatures::opCallSurroundingTry()); @@ -1181,13 +1180,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, argAttrs, retAttrs); + cirCallArgs, attrs, argAttrs, retAttrs, + callingConv); } else { op = builder.createCallOp(callLoc, directFuncOp, cirCallArgs, attrs, - argAttrs, retAttrs); + argAttrs, retAttrs, callingConv); } return op; @@ -1341,7 +1339,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; cgm.constructAttributeList(funcName, funcInfo, callee.getAbstractInfo(), @@ -1405,9 +1402,9 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, bool isInvoke = !cannotThrow && isCatchOrCleanupRequired(); mlir::Location callLoc = loc; - cir::CIRCallOpInterface theCall = - emitCallLikeOp(*this, loc, indirectFuncTy, indirectFuncVal, directFuncOp, - cirCallArgs, isInvoke, attrs, argAttrs, retAttrs); + cir::CIRCallOpInterface theCall = emitCallLikeOp( + *this, loc, indirectFuncTy, indirectFuncVal, directFuncOp, cirCallArgs, + isInvoke, attrs, argAttrs, retAttrs, callingConv); if (callOp) *callOp = theCall; diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index c03f7b355b6959..8948c0723fbc2c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -2026,7 +2026,10 @@ static void pushTemporaryCleanup(CIRGenFunction &cgf, } else { GlobalDecl gd(referenceTemporaryDtor, Dtor_Complete); cir::FuncOp dtorFn = cgm.getAddrAndTypeOfCXXStructor(gd).second; - builder.createCallOp(loc, dtorFn, mlir::ValueRange{tempAddr}); + // Make sure the call and the callee agree on calling convention. + builder.createCallOp(loc, dtorFn, mlir::ValueRange{tempAddr}, + /*attrs=*/{}, /*argAttrs=*/{}, /*resAttrs=*/{}, + dtorFn.getCallingConv()); } break; } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index ff4ab629e598cd..bb5b89f78fc74c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -2157,6 +2157,7 @@ void CIRGenModule::replaceUsesOfNonProtoTypeWithRealFunction( realCallOp = builder.createIndirectCallOp( noProtoCallOp.getLoc(), casted, callFnType, callOperands); } + realCallOp.setCallingConv(noProtoCallOp.getCallingConv()); // Replace old no proto call with fixed call. noProtoCallOp.replaceAllUsesWith(realCallOp); diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp index 219ee924c0bf3c..270b8a6a539ba9 100644 --- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp @@ -830,7 +830,10 @@ void CIRGenFunction::emitMustTailThunk(GlobalDecl gd, cir::FuncType calleeTy = callee.getFunctionType(); mlir::Type retTy = calleeTy.getReturnType(); - cir::CallOp call = builder.createCallOp(loc, callee, args); + assert(!cir::MissingFeatures::opCallAttrs()); + cir::CallOp call = + builder.createCallOp(loc, callee, args, /*attrs=*/{}, /*argAttrs=*/{}, + /*resAttrs=*/{}, curFnInfo->getCallingConvention()); call->setAttr(cir::CIRDialect::getMustTailAttrName(), mlir::UnitAttr::get(builder.getContext())); diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 2c5b785d5fc349..a376a668f157bb 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -1388,6 +1388,20 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser, return ::mlir::failure(); } + if (parser.parseOptionalKeyword("cc").succeeded()) { + cir::CallingConv callingConv; + if (parser.parseLParen().failed()) + return failure(); + if (parseCIRKeyword<cir::CallingConv>(parser, callingConv).failed()) + return parser.emitError(parser.getCurrentLocation(), + "unknown calling convention"); + if (parser.parseRParen().failed()) + return failure(); + result.addAttribute( + CIRDialect::getCallingConvAttrName(), + cir::CallingConvAttr::get(parser.getContext(), callingConv)); + } + if (parser.parseOptionalKeyword("musttail").succeeded()) result.addAttribute(CIRDialect::getMustTailAttrName(), mlir::UnitAttr::get(parser.getContext())); @@ -1495,6 +1509,10 @@ static void printCallCommon(mlir::Operation *op, printer << tryCall.getUnwindDest(); } + cir::CallingConv callingConv = callLikeOp.getCallingConv(); + if (callingConv != cir::CallingConv::C) + printer << " cc(" << stringifyCallingConv(callingConv) << ")"; + if (op->hasAttr(CIRDialect::getMustTailAttrName())) printer << " musttail"; @@ -1509,6 +1527,7 @@ static void printCallCommon(mlir::Operation *op, llvm::StringRef elidedAttrs[] = { CIRDialect::getCalleeAttrName(), + CIRDialect::getCallingConvAttrName(), CIRDialect::getMustTailAttrName(), CIRDialect::getNoThrowAttrName(), CIRDialect::getNoUnwindAttrName(), diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp index 69f0975566cd25..fd4e1863de5e52 100644 --- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp @@ -703,6 +703,15 @@ mlir::LogicalResult CIRDeleteArrayOpABILowering::matchAndRewrite( // exception handling flow will be connected to the cleanup region here to // call the delete operator on the exception path. mlir::FlatSymbolRefAttr dtorFn = op.getElementDtorAttr(); + + // Make sure the calls and the callees agree on calling convention. + auto getCalleeCallingConv = [&](mlir::FlatSymbolRefAttr callee) { + if (auto fn = + mlir::SymbolTable::lookupNearestSymbolFrom<cir::FuncOp>(op, callee)) + return fn.getCallingConv(); + return cir::CallingConv::C; + }; + cir::CleanupKind cleanupKind = op.getDtorMayThrow() ? cir::CleanupKind::All : cir::CleanupKind::Normal; cir::CleanupScopeOp::create( @@ -718,6 +727,7 @@ mlir::LogicalResult CIRDeleteArrayOpABILowering::matchAndRewrite( bb.getInsertionBlock()->addArgument(eltPtrTy, ll); auto dtorCall = cir::CallOp::create( bb, ll, dtorFn, cir::VoidType(), mlir::ValueRange{arg}); + dtorCall.setCallingConv(getCalleeCallingConv(dtorFn)); if (!op.getDtorMayThrow()) dtorCall.setNothrowAttr(bb.getUnitAttr()); cir::YieldOp::create(bb, ll); @@ -751,6 +761,7 @@ mlir::LogicalResult CIRDeleteArrayOpABILowering::matchAndRewrite( auto deleteCall = cir::CallOp::create(b, l, deleteFn, cir::VoidType(), callArgs); + deleteCall.setCallingConv(getCalleeCallingConv(deleteFn)); // operator delete[] is implicitly nothrow per [basic.stc.dynamic], // matching classic CodeGen's `nounwind` attribute on the call. deleteCall.setNothrowAttr(b.getUnitAttr()); diff --git a/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp index 72a8cffc3b0606..4b765b1f75fca7 100644 --- a/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp +++ b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp @@ -221,6 +221,8 @@ void ItaniumEHLowering::ensureRuntimeDecls(mlir::Location loc) { /// unreachable; /// } void ItaniumEHLowering::ensureClangCallTerminate(mlir::Location loc) { + // TODO(cir): set the runtime calling convention on the runtime calls below. + assert(!cir::MissingFeatures::opFuncCallingConv()); if (clangCallTerminateFunc) return; @@ -302,6 +304,8 @@ void ItaniumEHLowering::ensureCxaCallUnexpectedDecl(mlir::Location loc) { /// Create a terminate landing pad block at the end of the specified function. mlir::Block *ItaniumEHLowering::buildTerminateBlock(cir::FuncOp funcOp, mlir::Location loc) { + // TODO(cir): set the runtime calling convention on the runtime calls below. + assert(!cir::MissingFeatures::opFuncCallingConv()); assert(clangCallTerminateFunc && "ensureClangCallTerminate must run before buildTerminateBlock"); mlir::Region &body = funcOp.getRegion(); @@ -495,6 +499,8 @@ mlir::LogicalResult ItaniumEHLowering::lowerEhInitiate( cir::EhInitiateOp initiateOp, llvm::ArrayRef<cir::EhDispatchOp> reachedDispatches, bool reachesCleanup, EhTokenMap &ehTokenMap) { + // TODO(cir): set the runtime calling convention on the runtime calls below. + assert(!cir::MissingFeatures::opFuncCallingConv()); mlir::Value rootToken = initiateOp.getEhToken(); // The catch clauses for this landing pad come from the dispatches its @@ -826,6 +832,8 @@ ItaniumEHLowering::resolveCatchCopyThunk(cir::ConstructCatchParamOp op) { mlir::LogicalResult ItaniumEHLowering::lowerConstructCatchParam(cir::ConstructCatchParamOp op, mlir::Value exnPtr) { + // TODO(cir): set the runtime calling convention on the runtime calls below. + assert(!cir::MissingFeatures::opFuncCallingConv()); mlir::Location loc = op.getLoc(); mlir::Value paramAddr = op.getParamAddr(); cir::PointerType paramAddrType = @@ -913,6 +921,8 @@ ItaniumEHLowering::lowerConstructCatchParam(cir::ConstructCatchParamOp op, /// type_info and dtor pointers from their symbol attributes, bitcasting /// each to !cir.ptr<!void> as required by the runtime function signature. mlir::LogicalResult ItaniumEHLowering::lowerTryThrow(cir::TryThrowOp op) { + // TODO(cir): set the runtime calling convention on the runtime calls below. + assert(!cir::MissingFeatures::opFuncCallingConv()); mlir::Location loc = op.getLoc(); mlir::Block *normalDest = op.getNormalDest(); mlir::Block *unwindDest = op.getUnwindDest(); diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index 43e381b7e25c50..adb6c72516b7a9 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -402,6 +402,9 @@ struct LoweringPreparePass cir::GlobalOp global, mlir::Region &dtorRegion, bool tls, mlir::Block &entryBB) { + // TODO(cir): set the runtime calling convention on the __cxa_atexit call. + assert(!cir::MissingFeatures::opFuncCallingConv()); + // Create a variable that binds the atexit to this shared object. builder.setInsertionPointToStart(&mlirModule.getBodyRegion().front()); cir::GlobalOp handle = getOrCreateRuntimeVariable( @@ -468,6 +471,9 @@ struct LoweringPreparePass cir::PointerType guardPtrTy, bool threadsafe) { auto loc = globalOp->getLoc(); + // TODO(cir): set the runtime calling convention on the __cxa_guard_* calls. + assert(!cir::MissingFeatures::opFuncCallingConv()); + // The semantics of dynamic initialization of variables with static or // thread storage duration depends on whether they are declared at // block-scope. The initialization of such variables at block-scope can be @@ -739,6 +745,8 @@ static mlir::Value buildComplexBinOpLibCall( libFunc = pass.buildRuntimeFunction(builder, libFuncName, loc, libFuncTy); } + // TODO(cir): set the runtime calling convention to this call. + assert(!cir::MissingFeatures::opFuncCallingConv()); cir::CallOp call = builder.createCallOp(loc, libFunc, {lhsReal, lhsImag, rhsReal, rhsImag}); return call.getResult(); @@ -1277,7 +1285,9 @@ cir::FuncOp LoweringPreparePass::getOrCreateDtorFunc(CIRBaseBuilderTy &builder, mlir::cast<cir::GetGlobalOp>(dtorBlock.getOperations().front()); builder.setInsertionPointAfter(origGGop); mlir::Value ggopResult = origGGop.getResult(); - dtorCall = builder.createCallOp(op.getLoc(), dtorFunc, ggopResult); + dtorCall = builder.createCallOp(op.getLoc(), dtorFunc, ggopResult, + /*attrs=*/{}, /*argAttrs=*/{}, + /*resAttrs=*/{}, dtorFunc.getCallingConv()); // Add a yield after the call. auto finalYield = cir::YieldOp::create(builder, op.getLoc()); @@ -1760,13 +1770,18 @@ void LoweringPreparePass::defineGlobalThreadLocalWrapper(cir::GlobalOp op, builder, aliasLoc, cir::CmpOpKind::ne, funcLoad, nullCheck); cir::IfOp::create(builder, aliasLoc, cmp, /*withElseRegion=*/false, [&](mlir::OpBuilder &, mlir::Location loc) { - builder.createCallOp(aliasLoc, initAlias, {}); + builder.createCallOp(aliasLoc, initAlias, {}, + /*attrs=*/{}, + /*argAttrs=*/{}, /*resAttrs=*/{}, + initAlias.getCallingConv()); cir::YieldOp::create(builder, aliasLoc); }); } else { // If this IS a definition, we know the alias exists, so we can just emit // a call to it. - builder.createCallOp(aliasLoc, initAlias, {}); + builder.createCallOp(aliasLoc, initAlias, {}, /*attrs=*/{}, + /*argAttrs=*/{}, /*resAttrs=*/{}, + initAlias.getCallingConv()); } } cir::GetGlobalOp get = builder.createGetGlobal(op, /*tls=*/true); @@ -1946,7 +1961,8 @@ void LoweringPreparePass::lowerGetGlobalOp(GetGlobalOp op) { cir::CallOp call = builder.createCallOp( wrapperFunc.getLoc(), mlir::FlatSymbolRefAttr::get(wrapperFunc.getSymNameAttr()), - wrapperFunc.getFunctionType().getReturnType(), {}); + wrapperFunc.getFunctionType().getReturnType(), {}, /*attrs=*/{}, + /*argAttrs=*/{}, /*resAttrs=*/{}, wrapperFunc.getCallingConv()); op->replaceAllUsesWith(call); op.erase(); } @@ -2099,7 +2115,8 @@ void LoweringPreparePass::buildCXXGlobalTlsFunc() { // Emit the body of the guarded spot. builder.setInsertionPointToEnd(&ifOperation.getThenRegion().front()); for (cir::FuncOp initFunc : globalThreadLocalInitializers) - builder.createCallOp(loc, initFunc, {}); + builder.createCallOp(loc, initFunc, {}, /*attrs=*/{}, /*argAttrs=*/{}, + /*resAttrs=*/{}, initFunc.getCallingConv()); cir::YieldOp::create(builder, loc); builder.setInsertionPointAfter(ifOperation); @@ -2127,7 +2144,9 @@ cir::FuncOp LoweringPreparePass::buildGlobalInitCallerFunc( fnType, linkage); builder.setInsertionPointToStart(fn.addEntryBlock()); for (cir::FuncOp init : initializers) - builder.createCallOp(init.getLoc(), init, {}); + builder.createCallOp(init.getLoc(), init, {}, /*attrs=*/{}, + /*argAttrs=*/{}, /*resAttrs=*/{}, + init.getCallingConv()); cir::ReturnOp::create(builder, fn.getLoc()); globalCtorList.emplace_back(fnName, priority); return fn; diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp index 26dbe40c2c93fa..aef66b04684908 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp @@ -705,6 +705,8 @@ mlir::Value LowerItaniumCXXABI::lowerMethodToBoolCast( static void buildBadCastCall(mlir::OpBuilder &builder, mlir::Location loc, mlir::FlatSymbolRefAttr badCastFuncRef) { + // TODO(cir): set the runtime calling convention to this call. + assert(!cir::MissingFeatures::opFuncCallingConv()); auto callOp = cir::CallOp::create(builder, loc, badCastFuncRef, /*resType=*/cir::VoidType(), /*operands=*/mlir::ValueRange{}); @@ -739,6 +741,9 @@ static mlir::Value buildDynamicCastAfterNullCheck(cir::DynamicCastOp op, mlir::FlatSymbolRefAttr dynCastFuncRef = castInfo.getRuntimeFunc(); mlir::Value dynCastFuncArgs[4] = {srcPtr, srcRtti, destRtti, offsetHint}; + // TODO(cir): set the runtime calling convention to this call. + assert(!cir::MissingFeatures::opFuncCallingConv()); + mlir::Value castedPtr = cir::CallOp::create(builder, loc, dynCastFuncRef, voidPtrTy, dynCastFuncArgs) .getResult(); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 88df2da54b9568..da952a39aced09 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -2300,8 +2300,6 @@ static mlir::LogicalResult rewriteCallOrInvoke( if (converter->convertTypes(cirResults, llvmResults).failed()) return mlir::failure(); - assert(!cir::MissingFeatures::opCallCallConv()); - mlir::LLVM::MemoryEffectsAttr memoryEffects; bool noUnwind = false; bool willReturn = false; @@ -2365,17 +2363,19 @@ static mlir::LogicalResult rewriteCallOrInvoke( converter->convertType(calleeFuncTy)); } - assert(!cir::MissingFeatures::opCallCallConv()); + mlir::LLVM::CConv cconv = convertCallingConv(call.getCallingConv()); if (landingPadBlock) { assert(!cir::MissingFeatures::opCallInvokeAttrs()); auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::InvokeOp>( op, llvmFnTy, calleeAttr, callOperands, continueBlock, mlir::ValueRange{}, landingPadBlock, mlir::ValueRange{}); + newOp.setCConv(cconv); setLoweredCallAttributes(newOp, attributes); } else { auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( op, llvmFnTy, calleeAttr, callOperands); + newOp.setCConv(cconv); setLoweredCallAttributes(newOp, attributes); if (memoryEffects) newOp.setMemoryEffectsAttr(memoryEffects); @@ -2406,7 +2406,6 @@ mlir::LogicalResult CIRToLLVMCallOpLowering::matchAndRewrite( mlir::LogicalResult CIRToLLVMTryCallOpLowering::matchAndRewrite( cir::TryCallOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { - assert(!cir::MissingFeatures::opCallCallConv()); return rewriteCallOrInvoke(op.getOperation(), adaptor.getOperands(), rewriter, getTypeConverter(), dataLayout, symbolTables, op.getCalleeAttr(), op.getNormalDest(), diff --git a/clang/test/CIR/CodeGen/spir-call-calling-conv.cpp b/clang/test/CIR/CodeGen/spir-call-calling-conv.cpp new file mode 100644 index 00000000000000..af36535e440cbd --- /dev/null +++ b/clang/test/CIR/CodeGen/spir-call-calling-conv.cpp @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -triple spir64 -disable-llvm-passes -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s --check-prefix=CIR-BEFORE-LPP +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple spir64 -disable-llvm-passes -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefixes=LLVM,LLVM-CIR +// RUN: %clang_cc1 -triple spir64 -disable-llvm-passes -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefixes=LLVM,OGCG + +// Calls that ClangIR builds directly to C++ functions on SPIR use the callee's +// spir_func calling convention. + +struct S { S(); ~S(); int x; }; + +// Global variable and reference temporary destructors. +S g; +const S &r = S(); + +// CIR-BEFORE-LPP: cir.global external @g = ctor : !rec_S { +// CIR-BEFORE-LPP: cir.call @_ZN1SC1Ev(%{{.*}}) cc(spir_function) +// CIR-BEFORE-LPP: } dtor { +// CIR-BEFORE-LPP: cir.call @_ZN1SD1Ev(%{{.*}}) cc(spir_function) +// CIR-BEFORE-LPP: cir.global external @r = ctor : !cir.ptr<!rec_S> { +// CIR-BEFORE-LPP: cir.call @_ZN1SC1Ev(%{{.*}}) cc(spir_function) +// CIR-BEFORE-LPP: } dtor { +// CIR-BEFORE-LPP: cir.call @_ZN1SD1Ev(%{{.*}}) cc(spir_function) + +// TODO(cir): The global init functions and the __cxa_atexit call do not use +// the runtime calling convention yet. +// LLVM-CIR: define internal void @__cxx_global_var_init() +// OGCG: define internal spir_func void @__cxx_global_var_init() +// LLVM: call spir_func void @_ZN1SC1Ev(ptr noundef nonnull align 4 dereferenceable(4) @g) +// LLVM-CIR: call i32 @__cxa_atexit(ptr @_ZN1SD1Ev, ptr @g, ptr @__dso_handle) +// OGCG: call spir_func i32 @__cxa_atexit(ptr @_ZN1SD1Ev, ptr @g, ptr @__dso_handle) +// LLVM-CIR: define internal void @__cxx_global_var_init.1() +// OGCG: define internal spir_func void @__cxx_global_var_init.1() +// LLVM: call spir_func void @_ZN1SC1Ev(ptr noundef nonnull align 4 dereferenceable(4) @_ZGR1r_) +// LLVM-CIR: call i32 @__cxa_atexit(ptr @_ZN1SD1Ev, ptr @_ZGR1r_, ptr @__dso_handle) +// OGCG: call spir_func i32 @__cxa_atexit(ptr @_ZN1SD1Ev, ptr @_ZGR1r_, ptr @__dso_handle) + +// Array delete: the element destructor and operator delete[]. +void del(S *p) { delete[] p; } + +// CIR: cir.func {{.*}}@_Z3delP1S({{.*}}) cc(spir_function) +// CIR: cir.call @_ZN1SD1Ev(%{{.*}}) cc(spir_function) nothrow +// CIR: cir.call @_ZdaPvm(%{{.*}}, %{{.*}}) cc(spir_function) nothrow + +// LLVM: define dso_local spir_func void @_Z3delP1S( +// LLVM: call spir_func void @_ZN1SD1Ev(ptr {{.*}}%{{.*}}) +// LLVM: call spir_func void @_ZdaPvm(ptr {{.*}}%{{.*}}, i64 {{.*}}%{{.*}}) + +// This-adjusting thunk. +struct A { virtual void f(); int a; }; +struct D { virtual void g(); int d; }; +struct E : A, D { void g() override; }; +void E::g() {} + +// CIR: cir.func {{.*}}@_ZThn16_N1E1gEv({{.*}}) cc(spir_function) +// CIR: cir.call @_ZN1E1gEv(%{{.*}}) cc(spir_function) + +// LLVM: define dso_local spir_func void @_ZThn16_N1E1gEv( +// LLVM: call spir_func void @_ZN1E1gEv(ptr noundef nonnull align 8 dereferenceable(28) %{{.*}}) + +// LLVM-CIR: define internal void @_GLOBAL__sub_I_spir_call_calling_conv.cpp() +// OGCG: define internal spir_func void @_GLOBAL__sub_I_spir_call_calling_conv.cpp() +// LLVM-CIR: call void @__cxx_global_var_init() +// OGCG: call spir_func void @__cxx_global_var_init() diff --git a/clang/test/CIR/CodeGenOpenCL/call-calling-conv.cl b/clang/test/CIR/CodeGenOpenCL/call-calling-conv.cl new file mode 100644 index 00000000000000..f1b19bea416991 --- /dev/null +++ b/clang/test/CIR/CodeGenOpenCL/call-calling-conv.cl @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -triple spir64 -cl-std=CL2.0 -disable-llvm-passes -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple spir64 -cl-std=CL2.0 -disable-llvm-passes -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM +// RUN: %clang_cc1 -triple spir64 -cl-std=CL2.0 -disable-llvm-passes -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefixes=LLVM,OGCG + +// Calls to non-kernel functions on SPIR use the spir_func calling convention. + +int helper(global int *p) { return *p + 1; } +void store(global int *p, int v) { *p = v; } + +kernel void k(global int *p) { store(p, helper(p)); } + +// CIR: cir.func {{.*}}@helper({{.*}}) -> !s32i cc(spir_function) +// CIR: cir.func {{.*}}@store({{.*}}) cc(spir_function) +// CIR: cir.func {{.*}}@k({{.*}}) cc(spir_kernel) +// CIR: %[[R:.*]] = cir.call @helper(%{{.*}}) cc(spir_function) +// CIR: cir.call @store(%{{.*}}, %[[R]]) cc(spir_function) + +// LLVM: define dso_local spir_func i32 @helper( +// LLVM: define dso_local spir_func void @store( +// LLVM: define dso_local spir_kernel void @k( + +// TODO(cir): CIR does not yet emit the __clang_ocl_kern_imp_ kernel stub, so +// classic CodeGen calls helper/store from there. +// OGCG: call spir_func void @__clang_ocl_kern_imp_k( +// OGCG: define dso_local spir_func void @__clang_ocl_kern_imp_k( + +// LLVM: %[[R:.*]] = call spir_func i32 @helper(ptr addrspace(1) noundef %{{.*}}) +// LLVM: call spir_func void @store(ptr addrspace(1) noundef %{{.*}}, i32 noundef %[[R]]) diff --git a/clang/test/CIR/CodeGenSYCL/address-space-conversions.cpp b/clang/test/CIR/CodeGenSYCL/address-space-conversions.cpp index b2a5468adaa61f..6519caebfbd98a 100644 --- a/clang/test/CIR/CodeGenSYCL/address-space-conversions.cpp +++ b/clang/test/CIR/CodeGenSYCL/address-space-conversions.cpp @@ -8,8 +8,6 @@ // Port of clang/test/CodeGenSYCL/address-space-conversions.cpp. Local // variables and parameters are allocated in the private address space and // accessed through the generic address space. -// -// TODO(cir): Calls are missing the spir_func calling convention. void bar(int &Data) {} void bar2(int &Data) {} @@ -148,42 +146,42 @@ void tmpl(T t) {} // LLVM-CIR-NEXT: store ptr addrspace(1) %[[TMP15]], ptr addrspace(4) %[[GLOB_ASCAST]], align 8 // LLVM-CIR-NEXT: %[[TMP16:.*]] = load ptr addrspace(1), ptr addrspace(4) %[[GLOB_ASCAST]], align 8 // LLVM-CIR-NEXT: %[[TMP17:.*]] = addrspacecast ptr addrspace(1) %[[TMP16]] to ptr addrspace(4) -// LLVM-CIR-NEXT: call void @_Z3barRi(ptr addrspace(4) noundef align 4 dereferenceable(4) %[[TMP17]]) +// LLVM-CIR-NEXT: call spir_func void @_Z3barRi(ptr addrspace(4) noundef align 4 dereferenceable(4) %[[TMP17]]) // LLVM-CIR-NEXT: %[[TMP18:.*]] = load ptr addrspace(1), ptr addrspace(4) %[[GLOB_ASCAST]], align 8 // LLVM-CIR-NEXT: %[[TMP19:.*]] = addrspacecast ptr addrspace(1) %[[TMP18]] to ptr addrspace(4) -// LLVM-CIR-NEXT: call void @_Z4bar2Ri(ptr addrspace(4) noundef align 4 dereferenceable(4) %[[TMP19]]) +// LLVM-CIR-NEXT: call spir_func void @_Z4bar2Ri(ptr addrspace(4) noundef align 4 dereferenceable(4) %[[TMP19]]) // LLVM-CIR-NEXT: %[[TMP20:.*]] = load ptr addrspace(3), ptr addrspace(4) %[[LOC_ASCAST]], align 8 -// LLVM-CIR-NEXT: call void @_Z3barRU3AS3i(ptr addrspace(3) noundef align 4 dereferenceable(4) %[[TMP20]]) +// LLVM-CIR-NEXT: call spir_func void @_Z3barRU3AS3i(ptr addrspace(3) noundef align 4 dereferenceable(4) %[[TMP20]]) // LLVM-CIR-NEXT: %[[TMP21:.*]] = load ptr addrspace(3), ptr addrspace(4) %[[LOC_ASCAST]], align 8 // LLVM-CIR-NEXT: %[[TMP22:.*]] = addrspacecast ptr addrspace(3) %[[TMP21]] to ptr addrspace(4) -// LLVM-CIR-NEXT: call void @_Z4bar2Ri(ptr addrspace(4) noundef align 4 dereferenceable(4) %[[TMP22]]) +// LLVM-CIR-NEXT: call spir_func void @_Z4bar2Ri(ptr addrspace(4) noundef align 4 dereferenceable(4) %[[TMP22]]) // LLVM-CIR-NEXT: %[[TMP23:.*]] = load ptr addrspace(4), ptr addrspace(4) %[[NOAS_ASCAST]], align 8 -// LLVM-CIR-NEXT: call void @_Z3barRi(ptr addrspace(4) noundef align 4 dereferenceable(4) %[[TMP23]]) +// LLVM-CIR-NEXT: call spir_func void @_Z3barRi(ptr addrspace(4) noundef align 4 dereferenceable(4) %[[TMP23]]) // LLVM-CIR-NEXT: %[[TMP24:.*]] = load ptr addrspace(4), ptr addrspace(4) %[[NOAS_ASCAST]], align 8 -// LLVM-CIR-NEXT: call void @_Z4bar2Ri(ptr addrspace(4) noundef align 4 dereferenceable(4) %[[TMP24]]) +// LLVM-CIR-NEXT: call spir_func void @_Z4bar2Ri(ptr addrspace(4) noundef align 4 dereferenceable(4) %[[TMP24]]) // LLVM-CIR-NEXT: %[[TMP25:.*]] = load ptr addrspace(1), ptr addrspace(4) %[[GLOB_ASCAST]], align 8 // LLVM-CIR-NEXT: %[[TMP26:.*]] = addrspacecast ptr addrspace(1) %[[TMP25]] to ptr addrspace(4) -// LLVM-CIR-NEXT: call void @_Z3fooPi(ptr addrspace(4) noundef %[[TMP26]]) +// LLVM-CIR-NEXT: call spir_func void @_Z3fooPi(ptr addrspace(4) noundef %[[TMP26]]) // LLVM-CIR-NEXT: %[[TMP27:.*]] = load ptr addrspace(1), ptr addrspace(4) %[[GLOB_ASCAST]], align 8 // LLVM-CIR-NEXT: %[[TMP28:.*]] = addrspacecast ptr addrspace(1) %[[TMP27]] to ptr addrspace(4) -// LLVM-CIR-NEXT: call void @_Z4foo2Pi(ptr addrspace(4) noundef %[[TMP28]]) +// LLVM-CIR-NEXT: call spir_func void @_Z4foo2Pi(ptr addrspace(4) noundef %[[TMP28]]) // LLVM-CIR-NEXT: %[[TMP29:.*]] = load ptr addrspace(3), ptr addrspace(4) %[[LOC_ASCAST]], align 8 -// LLVM-CIR-NEXT: call void @_Z3fooPU3AS3i(ptr addrspace(3) noundef %[[TMP29]]) +// LLVM-CIR-NEXT: call spir_func void @_Z3fooPU3AS3i(ptr addrspace(3) noundef %[[TMP29]]) // LLVM-CIR-NEXT: %[[TMP30:.*]] = load ptr addrspace(3), ptr addrspace(4) %[[LOC_ASCAST]], align 8 // LLVM-CIR-NEXT: %[[TMP31:.*]] = addrspacecast ptr addrspace(3) %[[TMP30]] to ptr addrspace(4) -// LLVM-CIR-NEXT: call void @_Z4foo2Pi(ptr addrspace(4) noundef %[[TMP31]]) +// LLVM-CIR-NEXT: call spir_func void @_Z4foo2Pi(ptr addrspace(4) noundef %[[TMP31]]) // LLVM-CIR-NEXT: %[[TMP32:.*]] = load ptr addrspace(4), ptr addrspace(4) %[[NOAS_ASCAST]], align 8 -// LLVM-CIR-NEXT: call void @_Z3fooPi(ptr addrspace(4) noundef %[[TMP32]]) +// LLVM-CIR-NEXT: call spir_func void @_Z3fooPi(ptr addrspace(4) noundef %[[TMP32]]) // LLVM-CIR-NEXT: %[[TMP33:.*]] = load ptr addrspace(4), ptr addrspace(4) %[[NOAS_ASCAST]], align 8 -// LLVM-CIR-NEXT: call void @_Z4foo2Pi(ptr addrspace(4) noundef %[[TMP33]]) +// LLVM-CIR-NEXT: call spir_func void @_Z4foo2Pi(ptr addrspace(4) noundef %[[TMP33]]) // LLVM-CIR-NEXT: %[[TMP34:.*]] = load ptr addrspace(1), ptr addrspace(4) %[[GLOB_ASCAST]], align 8 -// LLVM-CIR-NEXT: call void @_Z4tmplIPU3AS1iEvT_(ptr addrspace(1) noundef %[[TMP34]]) +// LLVM-CIR-NEXT: call spir_func void @_Z4tmplIPU3AS1iEvT_(ptr addrspace(1) noundef %[[TMP34]]) // LLVM-CIR-NEXT: %[[TMP35:.*]] = load ptr addrspace(3), ptr addrspace(4) %[[LOC_ASCAST]], align 8 -// LLVM-CIR-NEXT: call void @_Z4tmplIPU3AS3iEvT_(ptr addrspace(3) noundef %[[TMP35]]) +// LLVM-CIR-NEXT: call spir_func void @_Z4tmplIPU3AS3iEvT_(ptr addrspace(3) noundef %[[TMP35]]) // LLVM-CIR-NEXT: %[[TMP36:.*]] = load ptr, ptr addrspace(4) %[[PRIV_ASCAST]], align 8 -// LLVM-CIR-NEXT: call void @_Z4tmplIPU3AS0iEvT_(ptr noundef %[[TMP36]]) +// LLVM-CIR-NEXT: call spir_func void @_Z4tmplIPU3AS0iEvT_(ptr noundef %[[TMP36]]) // LLVM-CIR-NEXT: %[[TMP37:.*]] = load ptr addrspace(4), ptr addrspace(4) %[[NOAS_ASCAST]], align 8 -// LLVM-CIR-NEXT: call void @_Z4tmplIPiEvT_(ptr addrspace(4) noundef %[[TMP37]]) +// LLVM-CIR-NEXT: call spir_func void @_Z4tmplIPiEvT_(ptr addrspace(4) noundef %[[TMP37]]) // LLVM-CIR-NEXT: ret void // OGCG-LABEL: define {{.*}}spir_func void @_Z6usagesv( diff --git a/clang/test/CIR/CodeGenSYCL/call-calling-conv.cpp b/clang/test/CIR/CodeGenSYCL/call-calling-conv.cpp new file mode 100644 index 00000000000000..4e0d257f71bcb0 --- /dev/null +++ b/clang/test/CIR/CodeGenSYCL/call-calling-conv.cpp @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -triple spir64 -fsycl-is-device -disable-llvm-passes -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple spir64 -fsycl-is-device -disable-llvm-passes -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefixes=LLVM,LLVM-CIR +// RUN: %clang_cc1 -triple spir64 -fsycl-is-device -disable-llvm-passes -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefixes=LLVM,OGCG + +// Calls in SYCL device code use the spir_func calling convention, matching the +// calling convention of the callee. + +template <typename KN, typename F> +[[clang::sycl_kernel_entry_point(KN)]] void kernel(F f) { f(); } + +template <typename KN, typename... Ts> +void sycl_kernel_launch(const char *, Ts...) {} + +int run2() { return 1; } +void run() { (void)run2(); } + +struct KN; +void use() { kernel<KN>([] { run(); }); } + +// CIR: cir.func {{.*}}@_ZTS2KN({{.*}}) cc(spir_kernel) +// CIR: cir.call @_ZZ3usevENKUlvE_clEv(%{{.*}}) cc(spir_function) +// CIR: cir.func {{.*}}@_ZZ3usevENKUlvE_clEv({{.*}}) cc(spir_function) +// CIR: cir.call @_Z3runv() cc(spir_function) +// CIR: cir.func {{.*}}@_Z3runv() cc(spir_function) +// CIR: cir.call @_Z4run2v() cc(spir_function) +// CIR: cir.func {{.*}}@_Z4run2v() -> {{.*}} cc(spir_function) + +// LLVM-CIR: define dso_local spir_kernel void @_ZTS2KN( +// OGCG: define dso_local spir_kernel void @_ZTS2KN( +// LLVM: call spir_func void @_ZZ3usevENKUlvE_clEv(ptr addrspace(4) noundef align 1 dereferenceable_or_null(1) %{{.*}}) +// LLVM: define internal spir_func void @_ZZ3usevENKUlvE_clEv( +// LLVM: call spir_func void @_Z3runv() +// LLVM: define dso_local spir_func void @_Z3runv() +// LLVM: call spir_func noundef i32 @_Z4run2v() +// LLVM: define dso_local spir_func noundef i32 @_Z4run2v() diff --git a/clang/test/CIR/CodeGenSYCL/local-temporaries.cpp b/clang/test/CIR/CodeGenSYCL/local-temporaries.cpp index 0c4649d0751763..868745b3dd4344 100644 --- a/clang/test/CIR/CodeGenSYCL/local-temporaries.cpp +++ b/clang/test/CIR/CodeGenSYCL/local-temporaries.cpp @@ -53,7 +53,7 @@ void take_ref(const S &s); // CIR-NEXT: cir.store align(4) %[[TMP0]], %[[REF_TMP0_ASCAST]] : !s32i, !cir.ptr<!s32i, target_address_space(4)> // CIR-NEXT: cir.store align(8) %[[REF_TMP0_ASCAST]], %[[R_ASCAST]] : !cir.ptr<!s32i, target_address_space(4)>, !cir.ptr<!cir.ptr<!s32i, target_address_space(4)>, target_address_space(4)> // CIR-NEXT: %[[TMP1:.*]] = cir.load %[[R_ASCAST]] : !cir.ptr<!cir.ptr<!s32i, target_address_space(4)>, target_address_space(4)>, !cir.ptr<!s32i, target_address_space(4)> -// CIR-NEXT: cir.call @_Z9use_constPKi(%[[TMP1]]) nothrow nounwind {convergent} : (!cir.ptr<!s32i, target_address_space(4)> {llvm.noundef}) -> () +// CIR-NEXT: cir.call @_Z9use_constPKi(%[[TMP1]]) cc(spir_function) nothrow nounwind {convergent} : (!cir.ptr<!s32i, target_address_space(4)> {llvm.noundef}) -> () // CIR-NEXT: cir.return // CIR-LABEL: cir.func {{.*}}@_Z8agg_tempv( @@ -65,7 +65,7 @@ void take_ref(const S &s); // CIR-NEXT: %[[TMP2:.*]] = cir.get_member %[[REF_TMP0_ASCAST]][1] {name = "b"} : !cir.ptr<!rec_S, target_address_space(4)> -> !cir.ptr<!s32i, target_address_space(4)> // CIR-NEXT: %[[TMP3:.*]] = cir.const #cir.int<2> : !s32i // CIR-NEXT: cir.store align(4) %[[TMP3]], %[[TMP2]] : !s32i, !cir.ptr<!s32i, target_address_space(4)> -// CIR-NEXT: cir.call @_Z8take_refRK1S(%[[REF_TMP0_ASCAST]]) nothrow nounwind {convergent} : (!cir.ptr<!rec_S, target_address_space(4)> {llvm.align = 4 : i64, llvm.dereferenceable = 8 : i64, llvm.noundef}) -> () +// CIR-NEXT: cir.call @_Z8take_refRK1S(%[[REF_TMP0_ASCAST]]) cc(spir_function) nothrow nounwind {convergent} : (!cir.ptr<!rec_S, target_address_space(4)> {llvm.align = 4 : i64, llvm.dereferenceable = 8 : i64, llvm.noundef}) -> () // CIR-NEXT: cir.return // CIR-LABEL: cir.func {{.*}}@_Z10array_tempv( @@ -79,7 +79,7 @@ void take_ref(const S &s); // CIR-NEXT: %[[TMP4:.*]] = cir.const #cir.int<4> : !s32i // CIR-NEXT: cir.store align(4) %[[TMP4]], %[[TMP3]] : !s32i, !cir.ptr<!s32i, target_address_space(4)> // CIR-NEXT: %[[TMP5:.*]] = cir.cast array_to_ptrdecay %[[REF_TMP0_ASCAST]] : !cir.ptr<!cir.array<!s32i x 2>, target_address_space(4)> -> !cir.ptr<!s32i, target_address_space(4)> -// CIR-NEXT: cir.call @_Z3usePi(%[[TMP5]]) nothrow nounwind {convergent} : (!cir.ptr<!s32i, target_address_space(4)> {llvm.noundef}) -> () +// CIR-NEXT: cir.call @_Z3usePi(%[[TMP5]]) cc(spir_function) nothrow nounwind {convergent} : (!cir.ptr<!s32i, target_address_space(4)> {llvm.noundef}) -> () // CIR-NEXT: cir.return // CIR-LABEL: cir.func {{.*}}@_Z10lambda_refv( @@ -91,7 +91,7 @@ void take_ref(const S &s); // CIR-NEXT: cir.store align(4) %[[TMP0]], %[[X_ASCAST]] : !s32i, !cir.ptr<!s32i, target_address_space(4)> // CIR-NEXT: %[[TMP1:.*]] = cir.get_member %[[L_ASCAST]][0] {name = "x"} : !cir.ptr<!rec_anon2E0, target_address_space(4)> -> !cir.ptr<!cir.ptr<!s32i, target_address_space(4)>, target_address_space(4)> // CIR-NEXT: cir.store align(8) %[[X_ASCAST]], %[[TMP1]] : !cir.ptr<!s32i, target_address_space(4)>, !cir.ptr<!cir.ptr<!s32i, target_address_space(4)>, target_address_space(4)> -// CIR-NEXT: cir.call @_ZZ10lambda_refvENKUlvE_clEv(%[[L_ASCAST]]) nothrow nounwind {convergent} : (!cir.ptr<!rec_anon2E0, target_address_space(4)> {llvm.align = 8 : i64, llvm.dereferenceable_or_null = 8 : i64, llvm.noundef}) -> () +// CIR-NEXT: cir.call @_ZZ10lambda_refvENKUlvE_clEv(%[[L_ASCAST]]) cc(spir_function) nothrow nounwind {convergent} : (!cir.ptr<!rec_anon2E0, target_address_space(4)> {llvm.align = 8 : i64, llvm.dereferenceable_or_null = 8 : i64, llvm.noundef}) -> () // CIR-NEXT: cir.return // CIR-LABEL: cir.func {{.*}}@_Z4nrvov( @@ -100,7 +100,7 @@ void take_ref(const S &s); // CIR-NEXT: %[[TMP0:.*]] = cir.const #cir.zero : !rec_S // CIR-NEXT: cir.store align(4) %[[TMP0]], %[[__RETVAL_ASCAST]] : !rec_S, !cir.ptr<!rec_S, target_address_space(4)> // CIR-NEXT: %[[TMP1:.*]] = cir.get_member %[[__RETVAL_ASCAST]][0] {name = "a"} : !cir.ptr<!rec_S, target_address_space(4)> -> !cir.ptr<!s32i, target_address_space(4)> -// CIR-NEXT: cir.call @_Z3usePi(%[[TMP1]]) nothrow nounwind {convergent} : (!cir.ptr<!s32i, target_address_space(4)> {llvm.noundef}) -> () +// CIR-NEXT: cir.call @_Z3usePi(%[[TMP1]]) cc(spir_function) nothrow nounwind {convergent} : (!cir.ptr<!s32i, target_address_space(4)> {llvm.noundef}) -> () // CIR-NEXT: %[[TMP2:.*]] = cir.load %[[__RETVAL]] : !cir.ptr<!rec_S>, !rec_S // CIR-NEXT: cir.return %[[TMP2]] : !rec_S @@ -112,7 +112,7 @@ void take_ref(const S &s); // LLVM-NEXT: store i32 42, ptr addrspace(4) %[[R_ASCAST]], align 4 // LLVM-NEXT: store ptr addrspace(4) %[[R_ASCAST]], ptr addrspace(4) %[[REF_TMP_ASCAST]], align 8 // LLVM-NEXT: %[[TMP0:.*]] = load ptr addrspace(4), ptr addrspace(4) %[[REF_TMP_ASCAST]], align 8 -// LLVM-NEXT: call void @_Z9use_constPKi(ptr addrspace(4) noundef %[[TMP0]]) +// LLVM-NEXT: call spir_func void @_Z9use_constPKi(ptr addrspace(4) noundef %[[TMP0]]) // LLVM-NEXT: ret void // LLVM-LABEL: define {{.*}}spir_func void @_Z8agg_tempv( @@ -122,7 +122,7 @@ void take_ref(const S &s); // LLVM-NEXT: store i32 1, ptr addrspace(4) %[[TMP1]], align 4 // LLVM-NEXT: %[[TMP2:.*]] = getelementptr inbounds nuw %[[TMP0]], ptr addrspace(4) %[[REF_TMP_ASCAST]], i32 0, i32 1 // LLVM-NEXT: store i32 2, ptr addrspace(4) %[[TMP2]], align 4 -// LLVM-NEXT: call void @_Z8take_refRK1S(ptr addrspace(4) noundef align 4 dereferenceable(8) %[[REF_TMP_ASCAST]]) +// LLVM-NEXT: call spir_func void @_Z8take_refRK1S(ptr addrspace(4) noundef align 4 dereferenceable(8) %[[REF_TMP_ASCAST]]) // LLVM-NEXT: ret void // LLVM-LABEL: define {{.*}}spir_func void @_Z10array_tempv( @@ -133,7 +133,7 @@ void take_ref(const S &s); // LLVM-NEXT: %[[TMP1:.*]] = getelementptr i32, ptr addrspace(4) %[[TMP0]], i64 1 // LLVM-NEXT: store i32 4, ptr addrspace(4) %[[TMP1]], align 4 // LLVM-NEXT: %[[TMP2:.*]] = getelementptr i32, ptr addrspace(4) %[[REF_TMP_ASCAST]], i32 0 -// LLVM-NEXT: call void @_Z3usePi(ptr addrspace(4) noundef %[[TMP2]]) +// LLVM-NEXT: call spir_func void @_Z3usePi(ptr addrspace(4) noundef %[[TMP2]]) // LLVM-NEXT: ret void // LLVM-LABEL: define {{.*}}spir_func void @_Z10lambda_refv( @@ -144,7 +144,7 @@ void take_ref(const S &s); // LLVM-NEXT: store i32 0, ptr addrspace(4) %[[X_ASCAST]], align 4 // LLVM-NEXT: %[[TMP1:.*]] = getelementptr inbounds nuw %[[TMP0]], ptr addrspace(4) %[[L_ASCAST]], i32 0, i32 0 // LLVM-NEXT: store ptr addrspace(4) %[[X_ASCAST]], ptr addrspace(4) %[[TMP1]], align 8 -// LLVM-NEXT: call void @_ZZ10lambda_refvENKUlvE_clEv(ptr addrspace(4) noundef align 8 dereferenceable_or_null(8) %[[L_ASCAST]]) +// LLVM-NEXT: call spir_func void @_ZZ10lambda_refvENKUlvE_clEv(ptr addrspace(4) noundef align 8 dereferenceable_or_null(8) %[[L_ASCAST]]) // LLVM-NEXT: ret void // LLVM-LABEL: define {{.*}}spir_func void @_ZZ10lambda_refvENKUlvE_clEv( @@ -155,7 +155,7 @@ void take_ref(const S &s); // LLVM-NEXT: %[[TMP0:.*]] = load ptr addrspace(4), ptr addrspace(4) %[[THIS_ADDR_ASCAST]], align 8 // LLVM-NEXT: %[[TMP1:.*]] = getelementptr inbounds nuw %[[TMP2:.*]], ptr addrspace(4) %[[TMP0]], i32 0, i32 0 // LLVM-NEXT: %[[TMP3:.*]] = load ptr addrspace(4), ptr addrspace(4) %[[TMP1]], align 8 -// LLVM-NEXT: call void @_Z3usePi(ptr addrspace(4) noundef %[[TMP3]]) +// LLVM-NEXT: call spir_func void @_Z3usePi(ptr addrspace(4) noundef %[[TMP3]]) // LLVM-NEXT: ret void // LLVM-LABEL: define {{.*}}spir_func %struct.S @_Z4nrvov( @@ -163,7 +163,7 @@ void take_ref(const S &s); // LLVM-NEXT: %[[TMP2:.*]] = addrspacecast ptr %[[TMP0]] to ptr addrspace(4) // LLVM-NEXT: store %[[TMP1]] zeroinitializer, ptr addrspace(4) %[[TMP2]], align 4 // LLVM-NEXT: %[[TMP3:.*]] = getelementptr inbounds nuw %[[TMP1]], ptr addrspace(4) %[[TMP2]], i32 0, i32 0 -// LLVM-NEXT: call void @_Z3usePi(ptr addrspace(4) noundef %[[TMP3]]) +// LLVM-NEXT: call spir_func void @_Z3usePi(ptr addrspace(4) noundef %[[TMP3]]) // LLVM-NEXT: %[[TMP4:.*]] = load %[[TMP1]], ptr %[[TMP0]], align 4 // LLVM-NEXT: ret %[[TMP1]] %[[TMP4]] diff --git a/clang/test/CIR/IR/call-calling-conv.cir b/clang/test/CIR/IR/call-calling-conv.cir new file mode 100644 index 00000000000000..753daaaf7530c1 --- /dev/null +++ b/clang/test/CIR/IR/call-calling-conv.cir @@ -0,0 +1,39 @@ +// RUN: cir-opt %s --verify-roundtrip | FileCheck %s + +!s32i = !cir.int<s, 32> + +module { + cir.func private @spir_callee(!s32i) -> !s32i cc(spir_function) + cir.func private @c_callee() + + cir.func @direct(%arg0: !s32i) -> !s32i cc(spir_function) { + %0 = cir.call @spir_callee(%arg0) cc(spir_function) nothrow : (!s32i) -> !s32i + cir.call @c_callee() : () -> () + cir.return %0 : !s32i + } + + // CHECK: cir.func @direct(%arg0: !s32i) -> !s32i cc(spir_function) { + // CHECK: %[[R:.*]] = cir.call @spir_callee(%arg0) cc(spir_function) nothrow : (!s32i) -> !s32i + // CHECK: cir.call @c_callee() : () -> () + + cir.func @indirect(%fn: !cir.ptr<!cir.func<(!s32i) -> !s32i>>, %arg0: !s32i) -> !s32i { + %0 = cir.call %fn(%arg0) cc(spir_function) : (!cir.ptr<!cir.func<(!s32i) -> !s32i>>, !s32i) -> !s32i + cir.return %0 : !s32i + } + + // CHECK: cir.func @indirect(%arg0: !cir.ptr<!cir.func<(!s32i) -> !s32i>>, %arg1: !s32i) -> !s32i { + // CHECK: %{{.*}} = cir.call %arg0(%arg1) cc(spir_function) : (!cir.ptr<!cir.func<(!s32i) -> !s32i>>, !s32i) -> !s32i + + cir.func @try_call(%arg0: !s32i) { + %0 = cir.try_call @spir_callee(%arg0) ^normal, ^unwind cc(spir_function) : (!s32i) -> !s32i + ^normal: + cir.br ^end + ^unwind: + cir.br ^end + ^end: + cir.return + } + + // CHECK: cir.func @try_call(%arg0: !s32i) { + // CHECK: %{{.*}} = cir.try_call @spir_callee(%arg0) ^bb1, ^bb2 cc(spir_function) : (!s32i) -> !s32i +} diff --git a/clang/test/CIR/Lowering/call-calling-conv.cir b/clang/test/CIR/Lowering/call-calling-conv.cir new file mode 100644 index 00000000000000..dc067fe9941944 --- /dev/null +++ b/clang/test/CIR/Lowering/call-calling-conv.cir @@ -0,0 +1,40 @@ +// RUN: cir-opt %s -cir-to-llvm -o %t.mlir +// RUN: FileCheck --input-file=%t.mlir %s + +!s32i = !cir.int<s, 32> + +module attributes {cir.triple = "x86_64-unknown-linux-gnu"} { + cir.func private @__gxx_personality_v0(...) -> !s32i + cir.func private @spir_callee(!s32i) -> !s32i cc(spir_function) + cir.func private @c_callee() + + cir.func @direct(%arg0: !s32i) -> !s32i cc(spir_function) { + %0 = cir.call @spir_callee(%arg0) cc(spir_function) : (!s32i) -> !s32i + cir.call @c_callee() : () -> () + cir.return %0 : !s32i + } + + // CHECK: llvm.func spir_funccc @direct( + // CHECK: llvm.call spir_funccc @spir_callee(%{{.*}}) : (i32) -> i32 + // CHECK: llvm.call @c_callee() : () -> () + + cir.func @indirect(%fn: !cir.ptr<!cir.func<(!s32i) -> !s32i>>, %arg0: !s32i) -> !s32i { + %0 = cir.call %fn(%arg0) cc(spir_function) : (!cir.ptr<!cir.func<(!s32i) -> !s32i>>, !s32i) -> !s32i + cir.return %0 : !s32i + } + + // CHECK: llvm.func @indirect( + // CHECK: llvm.call spir_funccc %{{.*}}(%{{.*}}) : !llvm.ptr, (i32) -> i32 + + cir.func @try_call(%arg0: !s32i) personality(@__gxx_personality_v0) { + %0 = cir.try_call @spir_callee(%arg0) ^normal, ^unwind cc(spir_function) : (!s32i) -> !s32i + ^normal: + cir.return + ^unwind: + %exception_ptr, %type_id = cir.eh.inflight_exception cleanup + cir.return + } + + // CHECK: llvm.func @try_call( + // CHECK: llvm.invoke spir_funccc @spir_callee(%{{.*}}) to ^{{.*}} unwind ^{{.*}} : (i32) -> i32 +} diff --git a/clang/test/CIR/Transforms/flatten-preserve-attrs.cir b/clang/test/CIR/Transforms/flatten-preserve-attrs.cir index db239bdc73aa2c..42a27b7c710e1f 100644 --- a/clang/test/CIR/Transforms/flatten-preserve-attrs.cir +++ b/clang/test/CIR/Transforms/flatten-preserve-attrs.cir @@ -141,7 +141,27 @@ cir.func @test_preserve_all_attrs_indirect( // CHECK-SAME: %[[OBJ:.*]]: !cir.ptr<!rec_SomeClass> // CHECK: cir.try_call %[[FN_PTR]](%[[OBJ]]) ^{{bb[0-9]+}}, ^{{bb[0-9]+}} {memory_effects = #cir.memory_effects<other = none, arg_mem = none, inaccessible_mem = none, errno_mem = none, target_mem0 = none, target_mem1 = none>} : (!cir.ptr<!cir.func<(!cir.ptr<!rec_SomeClass>) -> !s32i>>, !cir.ptr<!rec_SomeClass> {llvm.nonnull}) -> (!s32i {llvm.nonnull}) +// Test that the calling convention on a cir.call is preserved on the resulting +// cir.try_call after flattening. +cir.func @test_preserve_calling_conv() { + cir.scope { + cir.try { + cir.call @spirFunc() cc(spir_function) : () -> () + cir.yield + } catch all (%eh_token : !cir.eh_token) { + %catch_token, %exn_ptr = cir.begin_catch %eh_token : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!cir.void>) + cir.end_catch %catch_token : !cir.catch_token + cir.yield + } + } + cir.return +} + +// CHECK-LABEL: cir.func @test_preserve_calling_conv() +// CHECK: cir.try_call @spirFunc() ^{{.*}}, ^{{.*}} cc(spir_function) : () -> () + cir.func private @generalFunc(!cir.ptr<!s32i>) -> !cir.ptr<!s32i> cir.func private @takesPtr(!cir.ptr<!s32i>) cir.func private @returnsPtr() -> !cir.ptr<!s32i> cir.func private @pureFunc() -> !s32i +cir.func private @spirFunc() cc(spir_function) >From 72afc44b1a814e7e20e725e5d73713f0d098fe97 Mon Sep 17 00:00:00 2001 From: Konstantinos Parasyris <[email protected]> Date: Thu, 8 Oct 2026 13:29:51 -0700 Subject: [PATCH 2/2] Fix comments and add more todo's --- clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 2 + clang/lib/CIR/CodeGen/CIRGenCall.cpp | 8 ++++ clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 2 + .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 3 ++ clang/test/CIR/CodeGen/calling-conv-nyi.c | 30 ++++++++++++++ .../test/CIR/CodeGen/spir-call-calling-conv.c | 41 +++++++++++++++++++ .../CIR/CodeGen/spir-call-calling-conv.cpp | 4 +- 7 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 clang/test/CIR/CodeGen/calling-conv-nyi.c create mode 100644 clang/test/CIR/CodeGen/spir-call-calling-conv.c diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index c1418907082a54..eb842d24beb6e3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -494,6 +494,8 @@ static RValue emitAtomicIsLockFree(CIRGenFunction &cgf, const CallExpr *e, cir::FuncOp func = cgf.cgm.createRuntimeFunction( cir::FuncType::get({sizeTy, builder.getVoidPtrTy()}, builder.getBoolTy()), "__atomic_is_lock_free"); + // TODO(cir): set the runtime calling convention to this call. + assert(!cir::MissingFeatures::opFuncCallingConv()); return RValue::get( builder.createCallOp(loc, func, mlir::ValueRange{size, ptr}).getResult()); } diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 6e67dc13722974..0a167281025146 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -1345,6 +1345,14 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, attrs, argAttrs, retAttrs, callingConv, /*attrOnCallSite=*/true, /*isThunk=*/false); + // TODO(cir): Classic CodeGen redirects calls to OpenCL kernels to a + // non-kernel stub (__clang_ocl_kern_imp_*), since kernel calling conventions + // do not permit calls. + if (callingConv == cir::CallingConv::SpirKernel || + callingConv == cir::CallingConv::AMDGPUKernel || + callingConv == cir::CallingConv::PTXKernel) + cgm.errorNYI(loc, "emitCall: call to kernel function"); + auto resolvedFuncOpFromGlobal = [&](mlir::Operation *op) -> cir::FuncOp { if (auto fnOp = dyn_cast<cir::FuncOp>(op)) return fnOp; diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index 09283cf7362bb3..e1da57c9a38db7 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -778,6 +778,8 @@ CIRGenTypes::clangCallConvToCIRCallConv(clang::CallingConv cc) { return cgm.getTargetCIRGenInfo().getDeviceKernelCallingConv(); default: // TODO(cir): Support the remaining target-specific calling conventions. + cgm.errorNYI(SourceLocation(), "calling convention", + FunctionType::getNameForCallConv(cc)); return cir::CallingConv::C; } } diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index da952a39aced09..7a1e61b5262642 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -4735,6 +4735,9 @@ mlir::LogicalResult CIRToLLVMThrowOpLowering::matchAndRewrite( mlir::Location loc = op.getLoc(); auto voidTy = mlir::LLVM::LLVMVoidType::get(getContext()); + // TODO(cir): set the runtime calling convention on the runtime calls below. + assert(!cir::MissingFeatures::opFuncCallingConv()); + if (op.rethrows()) { auto funcTy = mlir::LLVM::LLVMFunctionType::get(voidTy, {}); diff --git a/clang/test/CIR/CodeGen/calling-conv-nyi.c b/clang/test/CIR/CodeGen/calling-conv-nyi.c new file mode 100644 index 00000000000000..dd769264c4afb7 --- /dev/null +++ b/clang/test/CIR/CodeGen/calling-conv-nyi.c @@ -0,0 +1,30 @@ +// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir \ +// RUN: -DVECTORCALL %s -o %t.cir 2>&1 | FileCheck %s --check-prefix=VECTORCALL +// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir \ +// RUN: -DMS_ABI %s -o %t.cir 2>&1 | FileCheck %s --check-prefix=MS_ABI +// RUN: not %clang_cc1 -triple i386-unknown-linux-gnu -fclangir -emit-cir \ +// RUN: -DFASTCALL %s -o %t.cir 2>&1 | FileCheck %s --check-prefix=FASTCALL +// RUN: not %clang_cc1 -triple i386-unknown-linux-gnu -fclangir -emit-cir \ +// RUN: -x c++ -DFASTCALL %s -o %t.cir 2>&1 | FileCheck %s --check-prefix=FASTCALL +// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir \ +// RUN: -x c++ -DVECTORCALL %s -o %t.cir 2>&1 | FileCheck %s --check-prefix=VECTORCALL + +// Target-specific calling conventions are not representable in CIR yet. Make +// sure they are diagnosed instead of silently falling back to the C calling +// convention. + +#if defined(VECTORCALL) +#define CC __attribute__((vectorcall)) +#elif defined(MS_ABI) +#define CC __attribute__((ms_abi)) +#elif defined(FASTCALL) +#define CC __attribute__((fastcall)) +#endif + +int CC callee(int x); + +int caller(int x) { return callee(x); } + +// VECTORCALL: ClangIR code gen Not Yet Implemented: calling convention: vectorcall +// MS_ABI: ClangIR code gen Not Yet Implemented: calling convention: ms_abi +// FASTCALL: ClangIR code gen Not Yet Implemented: calling convention: fastcall diff --git a/clang/test/CIR/CodeGen/spir-call-calling-conv.c b/clang/test/CIR/CodeGen/spir-call-calling-conv.c new file mode 100644 index 00000000000000..a6146df934357a --- /dev/null +++ b/clang/test/CIR/CodeGen/spir-call-calling-conv.c @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -std=c17 -Wno-deprecated-non-prototype -triple spir64 -disable-llvm-passes -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -std=c17 -Wno-deprecated-non-prototype -triple spir64 -disable-llvm-passes -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM +// RUN: %clang_cc1 -std=c17 -Wno-deprecated-non-prototype -triple spir64 -disable-llvm-passes -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM + +// Plain C calls on SPIR use the non-default spir_func calling convention for +// direct, indirect and unprototyped calls. + +int callee(int x); + +int direct(int x) { return callee(x); } + +// CIR: cir.func {{.*}}@direct({{.*}}) -> !s32i cc(spir_function) +// CIR: cir.call @callee(%{{.*}}) cc(spir_function) : (!s32i {{.*}}) -> !s32i + +// LLVM: define {{.*}}spir_func i32 @direct( +// LLVM: call spir_func i32 @callee(i32 noundef %{{.*}}) + +int indirect(int (*fp)(int), int x) { return fp(x); } + +// CIR: cir.func {{.*}}@indirect({{.*}}) -> !s32i cc(spir_function) +// CIR: cir.call %{{.*}}(%{{.*}}) cc(spir_function) : (!cir.ptr<!cir.func<(!s32i) -> !s32i>>, !s32i {{.*}}) -> !s32i + +// LLVM: define {{.*}}spir_func i32 @indirect( +// LLVM: call spir_func i32 %{{.*}}(i32 noundef %{{.*}}) + +int noproto(); + +int call_noproto(void) { return noproto(42); } + +int noproto(int x) { return x; } + +// CIR: cir.func {{.*}}@call_noproto() -> !s32i cc(spir_function) +// CIR: %[[FN:.*]] = cir.get_global @noproto +// CIR: cir.call %[[FN]](%{{.*}}) cc(spir_function) +// CIR: cir.func {{.*}}no_proto {{.*}}@noproto({{.*}}) -> !s32i cc(spir_function) + +// LLVM: define {{.*}}spir_func i32 @call_noproto( +// LLVM: call spir_func i32 @noproto(i32 noundef 42) diff --git a/clang/test/CIR/CodeGen/spir-call-calling-conv.cpp b/clang/test/CIR/CodeGen/spir-call-calling-conv.cpp index af36535e440cbd..fd24e448e835bd 100644 --- a/clang/test/CIR/CodeGen/spir-call-calling-conv.cpp +++ b/clang/test/CIR/CodeGen/spir-call-calling-conv.cpp @@ -14,11 +14,11 @@ struct S { S(); ~S(); int x; }; S g; const S &r = S(); -// CIR-BEFORE-LPP: cir.global external @g = ctor : !rec_S { +// CIR-BEFORE-LPP: cir.global external {{.*}}@g = {{.*}} : !rec_S ctor { // CIR-BEFORE-LPP: cir.call @_ZN1SC1Ev(%{{.*}}) cc(spir_function) // CIR-BEFORE-LPP: } dtor { // CIR-BEFORE-LPP: cir.call @_ZN1SD1Ev(%{{.*}}) cc(spir_function) -// CIR-BEFORE-LPP: cir.global external @r = ctor : !cir.ptr<!rec_S> { +// CIR-BEFORE-LPP: cir.global external {{.*}}@r = {{.*}} : !cir.ptr<!rec_S> ctor { // CIR-BEFORE-LPP: cir.call @_ZN1SC1Ev(%{{.*}}) cc(spir_function) // CIR-BEFORE-LPP: } dtor { // CIR-BEFORE-LPP: cir.call @_ZN1SD1Ev(%{{.*}}) cc(spir_function) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
