From d40686ce64f65daa240052c3795968a8e3036731 Mon Sep 17 00:00:00 2001 From: bruteforceboy Date: Wed, 4 Jun 2025 12:34:40 +0300 Subject: [PATCH 1/3] [CIR][CodeGen] Implement tryMarkNoThrow and update wrong test --- clang/include/clang/CIR/MissingFeatures.h | 2 +- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 37 +++++++++++++++++++++- clang/test/CIR/CodeGen/try-catch-dtors.cpp | 27 ++++++++-------- 3 files changed, 50 insertions(+), 16 deletions(-) diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 2f4e354ca95b..fe0b80f5c1af 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -239,7 +239,6 @@ struct MissingFeatures { static bool emitScalarRangeCheck() { return false; } static bool stmtExprEvaluation() { return false; } static bool setCallingConv() { return false; } - static bool tryMarkNoThrow() { return false; } static bool indirectBranch() { return false; } static bool escapedLocals() { return false; } static bool deferredReplacements() { return false; } @@ -251,6 +250,7 @@ struct MissingFeatures { static bool createLaunderInvariantGroup() { return false; } static bool hipModuleCtor() { return false; } static bool checkMacOSXTriple() { return false; } + static bool getSemanticInterposition() { return false; } // Inline assembly static bool asmGoto() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 2a78a5b41298..996eabc68f50 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -633,6 +633,41 @@ static void eraseEmptyAndUnusedBlocks(cir::FuncOp fnOp) { b->erase(); } +static bool isInterposable(cir::FuncOp fn) { + if (isInterposableLinkage(fn.getLinkage())) + return true; + + assert(!cir::MissingFeatures::getSemanticInterposition()); + + return false; +} + +void tryMarkNoThrow(CIRGenFunction &cgf, cir::FuncOp fn) { + // LLVM treats 'nounwind' on a function as part of the type, so we + // can't do this on functions that can be overwritten. + if (isInterposable(fn)) + return; + + for (auto &blk : fn.getBlocks()) { + bool mayThrow = false; + blk.walk([&](mlir::Operation *op) { + if (isa(op) || isa(op)) + mayThrow = true; + if (auto callOp = dyn_cast(op)) + if (callOp.getException()) + mayThrow = true; + }); + if (mayThrow) + return; + } + + mlir::NamedAttrList extraAttrs{fn.getExtraAttrs().getElements().getValue()}; + auto noThrowAttr = cir::NoThrowAttr::get(&cgf.getMLIRContext()); + extraAttrs.set(noThrowAttr.getMnemonic(), noThrowAttr); + fn.setExtraAttrsAttr(cir::ExtraFuncAttributesAttr::get( + extraAttrs.getDictionary(&cgf.getMLIRContext()))); +} + cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, const CIRGenFunctionInfo &fnInfo) { assert(fn && "generating code for a null function"); @@ -779,7 +814,7 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, // If we haven't marked the function nothrow through other means, do a quick // pass now to see if we can. - assert(!cir::MissingFeatures::tryMarkNoThrow()); + tryMarkNoThrow(*this, fn); } eraseEmptyAndUnusedBlocks(fn); diff --git a/clang/test/CIR/CodeGen/try-catch-dtors.cpp b/clang/test/CIR/CodeGen/try-catch-dtors.cpp index cad3558b912d..cbce97a44f07 100644 --- a/clang/test/CIR/CodeGen/try-catch-dtors.cpp +++ b/clang/test/CIR/CodeGen/try-catch-dtors.cpp @@ -324,22 +324,21 @@ void bar() { // CIR-LABEL: @_Z3barv // CIR: %[[V0:.*]] = cir.alloca !rec_A, !cir.ptr, ["a"] {alignment = 1 : i64} // CIR: %[[V1:.*]] = cir.alloca !s32i, !cir.ptr, ["b", init] {alignment = 4 : i64} -// CIR: %[[V2:.*]] = cir.alloca !s32i, !cir.ptr, ["tmp.try.call.res"] {alignment = 4 : i64} -// CIR: cir.try synthetic cleanup { -// CIR: %[[V4:.*]] = cir.call exception @_Z3foov() : () -> !s32i cleanup { -// CIR: cir.call @_ZN1AD2Ev(%[[V0]]) : (!cir.ptr) -> () extra(#fn_attr) -// CIR: cir.yield -// CIR: } -// CIR: cir.store{{.*}} %[[V4]], %[[V2]] : !s32i, !cir.ptr -// CIR: cir.yield -// CIR: } catch [#cir.unwind { -// CIR: cir.resume -// CIR: }] -// CIR: %[[V3:.*]] = cir.load{{.*}} %[[V2]] : !cir.ptr, !s32i -// CIR: cir.store{{.*}} %[[V3]], %[[V1]] : !s32i, !cir.ptr -// CIR: cir.call @_ZN1AD2Ev(%[[V0]]) : (!cir.ptr) -> () extra(#fn_attr) +// CIR: %[[V2:.*]] = cir.call @_Z3foov() : () -> !s32i +// CIR: cir.store align(4) %[[V2]], %[[V1]] : !s32i, !cir.ptr +// CIR: cir.call @_ZN1AD2Ev(%[[V0]]) : (!cir.ptr) -> () // CIR: cir.return +// LLVM: ; Function Attrs: noinline nounwind optnone +// LLVM-NEXT: _Z3foo +// LLVM: @_Z3barv() +// LLVM: %[[V1:.*]] = alloca %struct.A, i64 1, align 1 +// LLVM: %[[V2:.*]] = alloca i32, i64 1, align 4 +// LLVM: %[[V3:.*]] = call i32 @_Z3foov() +// LLVM: store i32 %[[V3]], ptr %[[V2]], align 4 +// LLVM: call void @_ZN1AD2Ev(ptr %[[V1]]) +// LLVM: ret void + class C { public: ~C(); From 663a60f0584c1c32eba1d99f1aaf22d528bda069 Mon Sep 17 00:00:00 2001 From: bruteforceboy Date: Wed, 4 Jun 2025 13:11:03 +0300 Subject: [PATCH 2/3] mark tryMarkNoThrow as static --- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 996eabc68f50..45c52464301e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -642,7 +642,7 @@ static bool isInterposable(cir::FuncOp fn) { return false; } -void tryMarkNoThrow(CIRGenFunction &cgf, cir::FuncOp fn) { +static void tryMarkNoThrow(CIRGenFunction &cgf, cir::FuncOp fn) { // LLVM treats 'nounwind' on a function as part of the type, so we // can't do this on functions that can be overwritten. if (isInterposable(fn)) From 4248755606cbc23e9afb9bcb3e6498a4bf522752 Mon Sep 17 00:00:00 2001 From: bruteforceboy Date: Mon, 9 Jun 2025 11:32:11 +0300 Subject: [PATCH 3/3] add mayThrow state --- clang/lib/CIR/CodeGen/CIRGenCall.cpp | 1 + clang/lib/CIR/CodeGen/CIRGenException.cpp | 1 + clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 15 +-------------- clang/lib/CIR/CodeGen/CIRGenFunction.h | 4 ++++ clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 3 +++ 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index aa25ae431c4b..be44f56ad22c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -381,6 +381,7 @@ emitCallLikeOp(CIRGenFunction &CGF, mlir::Location callLoc, callLoc, directFuncOp, CIRCallArgs, callingConv, sideEffect); } callOpWithExceptions->setAttr("extra_attrs", extraFnAttrs); + CGF.mayThrow = true; CGF.callWithExceptionCtx = callOpWithExceptions; auto *invokeDest = CGF.getInvokeDest(tryOp); diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp index 461c7f5428ef..b438f9aae0f5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenException.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp @@ -280,6 +280,7 @@ void CIRGenFunction::emitEHResumeBlock(bool isCleanup, getBuilder().create(loc, mlir::Value{}, mlir::Value{}); getBuilder().restoreInsertionPoint(ip); + mayThrow = true; } mlir::Block *CIRGenFunction::getEHResumeBlock(bool isCleanup, diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 45c52464301e..a65d02821de4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -645,22 +645,9 @@ static bool isInterposable(cir::FuncOp fn) { static void tryMarkNoThrow(CIRGenFunction &cgf, cir::FuncOp fn) { // LLVM treats 'nounwind' on a function as part of the type, so we // can't do this on functions that can be overwritten. - if (isInterposable(fn)) + if (isInterposable(fn) || cgf.mayThrow) return; - for (auto &blk : fn.getBlocks()) { - bool mayThrow = false; - blk.walk([&](mlir::Operation *op) { - if (isa(op) || isa(op)) - mayThrow = true; - if (auto callOp = dyn_cast(op)) - if (callOp.getException()) - mayThrow = true; - }); - if (mayThrow) - return; - } - mlir::NamedAttrList extraAttrs{fn.getExtraAttrs().getElements().getValue()}; auto noThrowAttr = cir::NoThrowAttr::get(&cgf.getMLIRContext()); extraAttrs.set(noThrowAttr.getMnemonic(), noThrowAttr); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 2c7d16592f75..c4a33f71ff1b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -512,6 +512,10 @@ class CIRGenFunction : public CIRGenTypeCache { /// the constructor, but could be overwrriten to true if this is a coroutine. bool ShouldEmitLifetimeMarkers; + /// True if there are any operations in the body of the function that are + /// likely to throw an exception. + bool mayThrow = false; + using DeclMapTy = llvm::DenseMap; /// This keeps track of the CIR allocas or globals for local C /// delcs. diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 0559184fd389..82f676cf3632 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -2303,6 +2303,7 @@ void CIRGenItaniumCXXABI::emitRethrow(CIRGenFunction &CGF, bool isNoReturn) { auto loc = *CGF.currSrcLoc; insertThrowAndSplit(builder, loc, mlir::Value{}, mlir::FlatSymbolRefAttr{}, mlir::FlatSymbolRefAttr{}); + CGF.mayThrow = true; } else { llvm_unreachable("NYI"); } @@ -2369,6 +2370,8 @@ void CIRGenItaniumCXXABI::emitThrow(CIRGenFunction &CGF, // Now throw the exception. mlir::Location loc = CGF.getLoc(E->getSourceRange()); insertThrowAndSplit(builder, loc, exceptionPtr, typeInfo.getSymbol(), dtor); + + CGF.mayThrow = true; } mlir::Value CIRGenItaniumCXXABI::getVirtualBaseClassOffset(