https://github.com/Lancern updated https://github.com/llvm/llvm-project/pull/145178
>From 183dad89003c36f3c3d11c4b930908b829f40f17 Mon Sep 17 00:00:00 2001 From: Sirui Mu <msrlanc...@gmail.com> Date: Sun, 29 Jun 2025 20:06:51 +0800 Subject: [PATCH] [CIR] Add nothrow attribute to call operations This patch adds nothrow attribute to call operations to indicate the callee will not throw. This patch also contains a minor refactor. The construction of call operations is now split into two steps. The CallOp::build function and the create function in CIR builder now only accepts basic parameters including the callee and the arguments. Other extra arguments, including any additional attributes, are set later on the constructed op in a separate step. --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 20 ++++---- clang/include/clang/CIR/Dialect/IR/CIROps.td | 24 ++------- .../clang/CIR/Interfaces/CIROpInterfaces.td | 2 + clang/lib/CIR/CodeGen/CIRGenCall.cpp | 49 ++++++++++++++----- clang/lib/CIR/CodeGen/CIRGenCall.h | 3 +- clang/lib/CIR/CodeGen/CIRGenModule.h | 2 +- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 14 ++++-- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 8 +-- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 2 +- clang/test/CIR/CodeGen/builtin_call.cpp | 4 +- clang/test/CIR/CodeGen/builtin_printf.cpp | 6 +-- clang/test/CIR/CodeGen/call.cpp | 15 ++++++ 12 files changed, 92 insertions(+), 57 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 3e052c564112e..5789993b4d121 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -227,26 +227,24 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { //===--------------------------------------------------------------------===// cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee, - mlir::Type returnType, mlir::ValueRange operands, - cir::SideEffect sideEffect = cir::SideEffect::All) { - return create<cir::CallOp>(loc, callee, returnType, operands, sideEffect); + mlir::Type returnType, mlir::ValueRange operands) { + return create<cir::CallOp>(loc, callee, returnType, operands); } cir::CallOp createCallOp(mlir::Location loc, cir::FuncOp callee, - mlir::ValueRange operands, - cir::SideEffect sideEffect = cir::SideEffect::All) { + mlir::ValueRange operands) { return createCallOp(loc, mlir::SymbolRefAttr::get(callee), - callee.getFunctionType().getReturnType(), operands, - sideEffect); + callee.getFunctionType().getReturnType(), operands); } cir::CallOp createIndirectCallOp(mlir::Location loc, mlir::Value indirectTarget, cir::FuncType funcType, - mlir::ValueRange operands, - cir::SideEffect sideEffect) { - return create<cir::CallOp>(loc, indirectTarget, funcType.getReturnType(), - operands, sideEffect); + mlir::ValueRange operands) { + llvm::SmallVector<mlir::Value> resOperands{indirectTarget}; + resOperands.append(operands.begin(), operands.end()); + return createCallOp(loc, mlir::SymbolRefAttr(), funcType.getReturnType(), + resOperands); } //===--------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 4daff74cbae5a..766978a641912 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -1912,6 +1912,7 @@ class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []> dag commonArgs = (ins OptionalAttr<FlatSymbolRefAttr>:$callee, Variadic<CIR_AnyType>:$args, + UnitAttr:$nothrow, DefaultValuedAttr<CIR_SideEffect, "SideEffect::All">:$side_effect); } @@ -1941,29 +1942,14 @@ def CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> { let arguments = commonArgs; let builders = [ - // Build a call op for a direct call OpBuilder<(ins "mlir::SymbolRefAttr":$callee, "mlir::Type":$resType, - "mlir::ValueRange":$operands, - CArg<"SideEffect", "SideEffect::All">:$sideEffect), [{ - assert(callee && "callee attribute is required for direct call"); + "mlir::ValueRange":$operands), [{ $_state.addOperands(operands); - $_state.addAttribute("callee", callee); - $_state.addAttribute("side_effect", - SideEffectAttr::get($_builder.getContext(), sideEffect)); + if (callee) + $_state.addAttribute("callee", callee); if (resType && !isa<VoidType>(resType)) $_state.addTypes(resType); - }]>, - // Build a call op for an indirect call - OpBuilder<(ins "mlir::Value":$calleePtr, "mlir::Type":$resType, - "mlir::ValueRange":$operands, - CArg<"SideEffect", "SideEffect::All">:$sideEffect), [{ - $_state.addOperands(calleePtr); - $_state.addOperands(operands); - if (resType && !isa<VoidType>(resType)) - $_state.addTypes(resType); - $_state.addAttribute("side_effect", - SideEffectAttr::get($_builder.getContext(), sideEffect)); - }]>, + }]> ]; } diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td index 5817b91b49e31..4702b4ef08acc 100644 --- a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td +++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td @@ -34,6 +34,8 @@ let cppNamespace = "::cir" in { "Return the number of operands, accounts for indirect call or " "exception info", "unsigned", "getNumArgOperands", (ins)>, + InterfaceMethod<"Return whether the callee is nothrow", + "bool", "getNothrow", (ins)>, InterfaceMethod<"Return the side effects of the call operation", "cir::SideEffect", "getSideEffect", (ins)>, ]; diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 163d0dbfec3dc..6dac60037bb18 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -77,17 +77,38 @@ void CIRGenFunction::emitAggregateStore(mlir::Value value, Address dest) { builder.createStore(*currSrcLoc, value, dest); } +static void addAttributesFromFunctionProtoType(CIRGenBuilderTy &builder, + mlir::NamedAttrList &attrs, + const FunctionProtoType *fpt) { + if (!fpt) + return; + + if (!isUnresolvedExceptionSpec(fpt->getExceptionSpecType()) && + fpt->isNothrow()) + attrs.set("nothrow", mlir::UnitAttr::get(builder.getContext())); +} + /// Construct the CIR attribute list of a function or call. void CIRGenModule::constructAttributeList(CIRGenCalleeInfo calleeInfo, - cir::SideEffect &sideEffect) { + mlir::NamedAttrList &attrs) { assert(!cir::MissingFeatures::opCallCallConv()); - sideEffect = cir::SideEffect::All; + auto sideEffect = cir::SideEffect::All; - assert(!cir::MissingFeatures::opCallAttrs()); + addAttributesFromFunctionProtoType(getBuilder(), attrs, + calleeInfo.getCalleeFunctionProtoType()); const Decl *targetDecl = calleeInfo.getCalleeDecl().getDecl(); if (targetDecl) { + if (targetDecl->hasAttr<NoThrowAttr>()) + attrs.set("nothrow", mlir::UnitAttr::get(&getMLIRContext())); + + if (const FunctionDecl *func = dyn_cast<FunctionDecl>(targetDecl)) { + addAttributesFromFunctionProtoType( + getBuilder(), attrs, func->getType()->getAs<FunctionProtoType>()); + assert(!cir::MissingFeatures::opCallAttrs()); + } + assert(!cir::MissingFeatures::opCallAttrs()); // 'const', 'pure' and 'noalias' attributed functions are also nounwind. @@ -104,6 +125,9 @@ void CIRGenModule::constructAttributeList(CIRGenCalleeInfo calleeInfo, } assert(!cir::MissingFeatures::opCallAttrs()); + + attrs.set("side_effect", + cir::SideEffectAttr::get(&getMLIRContext(), sideEffect)); } /// Returns the canonical formal type of the given C++ method. @@ -416,7 +440,7 @@ emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc, cir::FuncType indirectFuncTy, mlir::Value indirectFuncVal, cir::FuncOp directFuncOp, const SmallVectorImpl<mlir::Value> &cirCallArgs, - cir::SideEffect sideEffect) { + const mlir::NamedAttrList &attrs) { CIRGenBuilderTy &builder = cgf.getBuilder(); assert(!cir::MissingFeatures::opCallSurroundingTry()); @@ -424,14 +448,18 @@ emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc, assert(builder.getInsertionBlock() && "expected valid basic block"); + cir::CallOp op; if (indirectFuncTy) { // TODO(cir): Set calling convention for indirect calls. assert(!cir::MissingFeatures::opCallCallConv()); - return builder.createIndirectCallOp( - callLoc, indirectFuncVal, indirectFuncTy, cirCallArgs, sideEffect); + op = builder.createIndirectCallOp(callLoc, indirectFuncVal, indirectFuncTy, + cirCallArgs); + } else { + op = builder.createCallOp(callLoc, directFuncOp, cirCallArgs); } - return builder.createCallOp(callLoc, directFuncOp, cirCallArgs, sideEffect); + op->setAttrs(attrs); + return op; } const CIRGenFunctionInfo & @@ -544,8 +572,7 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, assert(!cir::MissingFeatures::opCallCallConv()); assert(!cir::MissingFeatures::opCallAttrs()); - cir::SideEffect sideEffect; - cgm.constructAttributeList(callee.getAbstractInfo(), sideEffect); + cgm.constructAttributeList(callee.getAbstractInfo(), attrs); assert(!cir::MissingFeatures::invokeOp()); @@ -566,12 +593,10 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, indirectFuncVal = calleePtr->getResult(0); } - assert(!cir::MissingFeatures::opCallAttrs()); - mlir::Location callLoc = loc; cir::CIRCallOpInterface theCall = emitCallLikeOp(*this, loc, indirectFuncTy, indirectFuncVal, directFuncOp, - cirCallArgs, sideEffect); + cirCallArgs, attrs); if (callOp) *callOp = theCall; diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h index 56c76c51a46d8..bd113293fdafd 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.h +++ b/clang/lib/CIR/CodeGen/CIRGenCall.h @@ -33,7 +33,8 @@ class CIRGenCalleeInfo { CIRGenCalleeInfo(const clang::FunctionProtoType *calleeProtoTy, clang::GlobalDecl calleeDecl) : calleeProtoTy(calleeProtoTy), calleeDecl(calleeDecl) {} - CIRGenCalleeInfo(clang::GlobalDecl calleeDecl) : calleeDecl(calleeDecl) {} + CIRGenCalleeInfo(clang::GlobalDecl calleeDecl) + : calleeProtoTy(nullptr), calleeDecl(calleeDecl) {} const clang::FunctionProtoType *getCalleeFunctionProtoType() const { return calleeProtoTy; diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 5a1eb9dea35c6..16922b115027e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -166,7 +166,7 @@ class CIRGenModule : public CIRGenTypeCache { /// constructed for. If valid, the attributes applied to this decl may /// contribute to the function attributes and calling convention. void constructAttributeList(CIRGenCalleeInfo calleeInfo, - cir::SideEffect &sideEffect); + mlir::NamedAttrList &attrs); /// Return a constant array for the given string. mlir::Attribute getConstantArrayFromStringLiteral(const StringLiteral *e); diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 2d5dcf69c489f..0143f435c6dd8 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -611,6 +611,9 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser, if (parser.parseRParen()) return mlir::failure(); + if (parser.parseOptionalKeyword("nothrow").succeeded()) + result.addAttribute("nothrow", mlir::UnitAttr::get(parser.getContext())); + if (parser.parseOptionalKeyword("side_effect").succeeded()) { if (parser.parseLParen().failed()) return failure(); @@ -645,7 +648,7 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser, static void printCallCommon(mlir::Operation *op, mlir::FlatSymbolRefAttr calleeSym, mlir::Value indirectCallee, - mlir::OpAsmPrinter &printer, + mlir::OpAsmPrinter &printer, bool isNothrow, cir::SideEffect sideEffect) { printer << ' '; @@ -662,13 +665,17 @@ static void printCallCommon(mlir::Operation *op, } printer << "(" << ops << ")"; + if (isNothrow) + printer << " nothrow"; + if (sideEffect != cir::SideEffect::All) { printer << " side_effect("; printer << stringifySideEffect(sideEffect); printer << ")"; } - printer.printOptionalAttrDict(op->getAttrs(), {"callee", "side_effect"}); + printer.printOptionalAttrDict(op->getAttrs(), + {"callee", "nothrow", "side_effect"}); printer << " : "; printer.printFunctionalType(op->getOperands().getTypes(), @@ -683,7 +690,8 @@ mlir::ParseResult cir::CallOp::parse(mlir::OpAsmParser &parser, void cir::CallOp::print(mlir::OpAsmPrinter &p) { mlir::Value indirectCallee = isIndirect() ? getIndirectCall() : nullptr; cir::SideEffect sideEffect = getSideEffect(); - printCallCommon(*this, getCalleeAttr(), indirectCallee, p, sideEffect); + printCallCommon(*this, getCalleeAttr(), indirectCallee, p, getNothrow(), + sideEffect); } static LogicalResult diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 1034b8780c03c..5cab182d1f4a9 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -232,7 +232,7 @@ mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp, return value; } -void convertSideEffectForCall(mlir::Operation *callOp, +void convertSideEffectForCall(mlir::Operation *callOp, bool isNothrow, cir::SideEffect sideEffect, mlir::LLVM::MemoryEffectsAttr &memoryEffect, bool &noUnwind, bool &willReturn) { @@ -241,7 +241,7 @@ void convertSideEffectForCall(mlir::Operation *callOp, switch (sideEffect) { case cir::SideEffect::All: memoryEffect = {}; - noUnwind = false; + noUnwind = isNothrow; willReturn = false; break; @@ -800,8 +800,8 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands, mlir::LLVM::MemoryEffectsAttr memoryEffects; bool noUnwind = false; bool willReturn = false; - convertSideEffectForCall(op, call.getSideEffect(), memoryEffects, noUnwind, - willReturn); + convertSideEffectForCall(op, call.getNothrow(), call.getSideEffect(), + memoryEffects, noUnwind, willReturn); mlir::LLVM::LLVMFunctionType llvmFnTy; if (calleeAttr) { // direct call diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index 25cf218cf8b6c..d618493531b97 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -29,7 +29,7 @@ mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp, mlir::Attribute attr, mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage); -void convertSideEffectForCall(mlir::Operation *callOp, +void convertSideEffectForCall(mlir::Operation *callOp, bool isNothrow, cir::SideEffect sideEffect, mlir::LLVM::MemoryEffectsAttr &memoryEffect, bool &noUnwind, bool &willReturn); diff --git a/clang/test/CIR/CodeGen/builtin_call.cpp b/clang/test/CIR/CodeGen/builtin_call.cpp index b956f2580593e..ad0e478040836 100644 --- a/clang/test/CIR/CodeGen/builtin_call.cpp +++ b/clang/test/CIR/CodeGen/builtin_call.cpp @@ -84,8 +84,8 @@ void library_builtins() { // CIR: cir.func{{.*}} @_Z16library_builtinsv() { // CIR: %[[NULL:.+]] = cir.const #cir.ptr<null> : !cir.ptr<!s8i> -// CIR: cir.call @printf(%[[NULL]]) : (!cir.ptr<!s8i>) -> !s32i -// CIR: cir.call @abort() : () -> () +// CIR: cir.call @printf(%[[NULL]]) nothrow : (!cir.ptr<!s8i>) -> !s32i +// CIR: cir.call @abort() nothrow : () -> () // LLVM: define{{.*}} void @_Z16library_builtinsv() // LLVM: call i32 (ptr, ...) @printf(ptr null) diff --git a/clang/test/CIR/CodeGen/builtin_printf.cpp b/clang/test/CIR/CodeGen/builtin_printf.cpp index d12f822d43ebf..43128e44b19e4 100644 --- a/clang/test/CIR/CodeGen/builtin_printf.cpp +++ b/clang/test/CIR/CodeGen/builtin_printf.cpp @@ -26,16 +26,16 @@ void func(char const * const str, int i) { // CIR: cir.store %[[arg0]], %[[str_ptr]] : !cir.ptr<!s8i>, !cir.ptr<!cir.ptr<!s8i>> // CIR: cir.store %[[arg1]], %[[i_ptr]] : !s32i, !cir.ptr<!s32i> // CIR: %[[null_ptr:.+]] = cir.const #cir.ptr<null> : !cir.ptr<!s8i> -// CIR: %[[printf_result1:.+]] = cir.call @printf(%[[null_ptr]]) : (!cir.ptr<!s8i>) -> !s32i +// CIR: %[[printf_result1:.+]] = cir.call @printf(%[[null_ptr]]) nothrow : (!cir.ptr<!s8i>) -> !s32i // CIR: %[[str_fmt_global:.+]] = cir.get_global @".str" : !cir.ptr<!cir.array<!s8i x 3>> // CIR: %[[str_fmt_ptr:.+]] = cir.cast(array_to_ptrdecay, %[[str_fmt_global]] : !cir.ptr<!cir.array<!s8i x 3>>), !cir.ptr<!s8i> // CIR: %[[str_val:.+]] = cir.load{{.*}} %[[str_ptr]] : !cir.ptr<!cir.ptr<!s8i>>, !cir.ptr<!s8i> -// CIR: %[[printf_result2:.+]] = cir.call @printf(%[[str_fmt_ptr]], %[[str_val]]) : (!cir.ptr<!s8i>, !cir.ptr<!s8i>) -> !s32i +// CIR: %[[printf_result2:.+]] = cir.call @printf(%[[str_fmt_ptr]], %[[str_val]]) nothrow : (!cir.ptr<!s8i>, !cir.ptr<!s8i>) -> !s32i // CIR: %[[full_fmt_global:.+]] = cir.get_global @".str.1" : !cir.ptr<!cir.array<!s8i x 7>> // CIR: %[[full_fmt_ptr:.+]] = cir.cast(array_to_ptrdecay, %[[full_fmt_global]] : !cir.ptr<!cir.array<!s8i x 7>>), !cir.ptr<!s8i> // CIR: %[[str_val2:.+]] = cir.load{{.*}} %[[str_ptr]] : !cir.ptr<!cir.ptr<!s8i>>, !cir.ptr<!s8i> // CIR: %[[i_val:.+]] = cir.load{{.*}} %[[i_ptr]] : !cir.ptr<!s32i>, !s32i -// CIR: %[[printf_result3:.+]] = cir.call @printf(%[[full_fmt_ptr]], %[[str_val2]], %[[i_val]]) : (!cir.ptr<!s8i>, !cir.ptr<!s8i>, !s32i) -> !s32i +// CIR: %[[printf_result3:.+]] = cir.call @printf(%[[full_fmt_ptr]], %[[str_val2]], %[[i_val]]) nothrow : (!cir.ptr<!s8i>, !cir.ptr<!s8i>, !s32i) -> !s32i // CIR: cir.return // LLVM: define{{.*}} void @_Z4funcPKci(ptr %[[arg0:.+]], i32 %[[arg1:.+]]) diff --git a/clang/test/CIR/CodeGen/call.cpp b/clang/test/CIR/CodeGen/call.cpp index f7653ed7a572b..43e5d150a949f 100644 --- a/clang/test/CIR/CodeGen/call.cpp +++ b/clang/test/CIR/CodeGen/call.cpp @@ -102,3 +102,18 @@ void f12() { // LLVM: %[[#slot:]] = alloca %struct.S, i64 1, align 4 // LLVM-NEXT: %[[#ret:]] = call %struct.S @_Z3f10v() // LLVM-NEXT: store %struct.S %[[#ret]], ptr %[[#slot]], align 4 + +void f13() noexcept; +void f14() { + f13(); +} + +// CIR-LABEL: cir.func{{.+}} @_Z3f14v() +// CIR: cir.call @_Z3f13v() nothrow : () -> () +// CIR: } + +// LLVM-LABEL: define{{.+}} void @_Z3f14v() +// LLVM: call void @_Z3f13v() #[[LLVM_ATTR_0:.+]] +// LLVM: } + +// LLLVM: attributes #[[LLVM_ATTR_0]] = { nounwind } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits