diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp index 7989536df538..317e32d13bea 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp @@ -65,7 +65,7 @@ cir::BrOp CIRGenFunction::emitBranchThroughCleanup(mlir::Location Loc, Fixup.destinationIndex = Dest.getDestIndex(); Fixup.initialBranch = brOp; Fixup.optimisticBranchBlock = nullptr; - // FIXME(cir): should we clear insertion point here? + insertPointSet = false; return brOp; } @@ -889,7 +889,15 @@ void EHScopeStack::deallocate(size_t Size) { void EHScopeStack::popNullFixups() { // We expect this to only be called when there's still an innermost // normal cleanup; otherwise there really shouldn't be any fixups. - llvm_unreachable("NYI"); + assert(hasNormalCleanups()); + + EHScopeStack::iterator it = find(InnermostNormalCleanup); + unsigned minSize = cast(*it).getFixupDepth(); + assert(BranchFixups.size() >= minSize && "fixup stack out of order"); + + while (BranchFixups.size() > minSize && + BranchFixups.back().destination == nullptr) + BranchFixups.pop_back(); } bool EHScopeStack::requiresLandingPad() const { diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index 1f6eb24b15d4..aa8adc3c92d2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -278,8 +278,14 @@ void CIRGenFunction::emitAutoVarInit(const AutoVarEmission &emission) { // If this local has an initializer, emit it now. const Expr *Init = D.getInit(); - // TODO: in LLVM codegen if we are at an unreachable point, the initializer - // isn't emitted unless it contains a label. What we want for CIR? + if (!HaveInsertPoint()) { + if (!Init || !ContainsLabel(Init)) { + // TODO(cir): PGO->markStmtMaybeUsed(Init); + return; + } + ensureInsertPoint(); + } + assert(builder.getInsertionBlock()); // Initialize the variable here if it doesn't have a initializer and it is a @@ -373,8 +379,9 @@ void CIRGenFunction::emitAutoVarCleanups(const AutoVarEmission &emission) { if (emission.wasEmittedAsGlobal()) return; - // TODO: in LLVM codegen if we are at an unreachable point codgen - // is ignored. What we want for CIR? + if (!HaveInsertPoint()) + return; + assert(builder.getInsertionBlock()); const VarDecl &D = *emission.Variable; diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 825d3052b1f8..0d7a5d05f710 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -42,7 +42,7 @@ CIRGenFunction::CIRGenFunction(CIRGenModule &cgm, CIRGenBuilderTy &builder, bool suppressNewContext) : CIRGenTypeCache(cgm), CGM{cgm}, builder(builder), SanOpts(cgm.getLangOpts().Sanitize), CurFPFeatures(cgm.getLangOpts()), - ShouldEmitLifetimeMarkers(false) { + ShouldEmitLifetimeMarkers(false), insertPointSet(true) { if (!suppressNewContext) cgm.getCXXABI().getMangleContext().startNewFunction(); EHStack.setCGF(this); @@ -859,6 +859,7 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, llvm_unreachable("no definition for emitted function"); } + ensureInsertPoint(); assert(builder.getInsertionBlock() && "Should be valid"); if (mlir::failed(fn.verifyBody())) diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 557a90518e98..76fba98e4cfd 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -527,6 +527,10 @@ class CIRGenFunction : public CIRGenTypeCache { /// the constructor, but could be overwrriten to true if this is a coroutine. bool ShouldEmitLifetimeMarkers; + /// True if an insertion point is defined. If not, this indicates that the + /// current code being emitted is unreachable. + bool insertPointSet; + /// True if there are any operations in the body of the function that are /// likely to throw an exception. bool mayThrow = false; @@ -614,11 +618,20 @@ class CIRGenFunction : public CIRGenTypeCache { /// True if an insertion point is defined. If not, this indicates that the /// current code being emitted is unreachable. - /// FIXME(cir): we need to inspect this and perhaps use a cleaner mechanism - /// since we don't yet force null insertion point to designate behavior (like - /// LLVM's codegen does) and we probably shouldn't. - bool HaveInsertPoint() const { - return builder.getInsertionBlock() != nullptr; + /// In LLVM's codegen, forcing null insertion points is used to designate + /// behavior. In CIR, we use the `insertPointSet` flag as a mechanism to + /// implement the same behavior, since we don't force null insert points. + bool HaveInsertPoint() const { return insertPointSet; } + + /// ensureInsertPoint - Ensure that an insertion point is defined so that + /// emitted IR has a place to go. Note that by definition, if this function + /// creates a block then that block is unreachable; callers may do better to + /// detect when no insertion point is defined and simply skip IR generation. + void ensureInsertPoint() { + if (!HaveInsertPoint()) + insertPointSet = true; + + assert(builder.getInsertionBlock() && "expected an insertion block"); } /// Whether any type-checking sanitizers are enabled. If \c false, calls to @@ -1280,6 +1293,7 @@ class CIRGenFunction : public CIRGenTypeCache { assert(CGF.OutermostConditional != nullptr); if (CGF.OutermostConditional == this) CGF.OutermostConditional = nullptr; + CGF.ensureInsertPoint(); } /// Returns the insertion point which will be executed prior to each diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index 6d765ee7cc35..d4ee9a320e8f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -62,6 +62,7 @@ Address CIRGenFunction::emitCompoundStmtWithoutScope(const CompoundStmt &S, } } + ensureInsertPoint(); return retAlloca; } @@ -100,6 +101,27 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *S, if (mlir::succeeded(emitSimpleStmt(S, useCurrentScope))) return mlir::success(); + // Check if we are generating unreachable code. + if (!HaveInsertPoint()) { + // If so, and the statement doesn't contain a label, then we do not need to + // generate actual code. This is safe because (1) the current point is + // unreachable, so we don't need to execute the code, and (2) we've already + // handled the statements which update internal data structures (like the + // local variable map) which could be used by subsequent statements. + if (!ContainsLabel(S)) { + // Verify that any decl statements were handled as simple, they may be in + // scope of subsequent reachable statements. + assert(!isa(*S) && "Unexpected DeclStmt!"); + // TODO(cir): PGO->markStmtMaybeUsed(S); + return mlir::success(); + } + + // LLVM's codegen makes a new code block, but in CIR we reset + // `insertPointSet` to true, since we don't clear insertion points for + // unreachable code. + ensureInsertPoint(); + } + if (getContext().getLangOpts().OpenMP && getContext().getLangOpts().OpenMPSimd) assert(0 && "not implemented"); diff --git a/clang/test/CIR/CodeGen/cleanup-null-fixups.cpp b/clang/test/CIR/CodeGen/cleanup-null-fixups.cpp new file mode 100644 index 000000000000..9e8cced22679 --- /dev/null +++ b/clang/test/CIR/CodeGen/cleanup-null-fixups.cpp @@ -0,0 +1,120 @@ +#include "std-cxx.h" + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -I%S/../Inputs -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -I%S/../Inputs -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -I%S/../Inputs -emit-llvm %s -o %t.og.ll +// RUN: FileCheck --check-prefix=OGCG --input-file=%t.og.ll %s + +// CIR-LABEL: cir.func {{.*}} @_ZNK1d1eEv +// CIR-NEXT: %[[V0:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] +// CIR-NEXT: %[[V1:.*]] = cir.alloca !rec_a3A3Ac, !cir.ptr, ["__retval"] +// CIR-NEXT: %[[V2:.*]] = cir.alloca !rec_aj, !cir.ptr, ["ao"] +// CIR-NEXT: cir.store %arg0, %[[V0]] : !cir.ptr, !cir.ptr> +// CIR-NEXT: %[[V3:.*]] = cir.load %[[V0]] : !cir.ptr>, !cir.ptr +// CIR-NEXT: cir.scope { +// CIR-NEXT: %[[V5:.*]] = cir.alloca !rec_aj, !cir.ptr, ["agg.tmp0"] +// CIR-NEXT: %[[V6:.*]] = cir.get_global @an : !cir.ptr +// CIR-NEXT: cir.copy %[[V6]] to %[[V5]] : !cir.ptr +// CIR-NEXT: %[[V7:.*]] = cir.load align(1) %[[V5]] : !cir.ptr, !rec_aj +// CIR-NEXT: cir.call @_ZN1a1cC1I2ajEET_(%[[V1]], %[[V7]]) : (!cir.ptr, !rec_aj) -> () +// CIR-NEXT: cir.call @_ZN2ajD1Ev(%[[V5]]) : (!cir.ptr) -> () +// CIR-NEXT: } +// CIR-NEXT: cir.call @_ZN2ajD1Ev(%[[V2]]) : (!cir.ptr) -> () +// CIR-NEXT: %[[V4:.*]] = cir.load align(1) %[[V1]] : !cir.ptr, !rec_a3A3Ac +// CIR-NEXT: cir.return %[[V4]] : !rec_a3A3Ac +// CIR-NEXT: } + +// LLVM-LABEL: {{.*}} @_ZNK1d1eEv(ptr {{.*}}) +// LLVM-NEXT: %[[V2:.*]] = alloca %class.aj, i64 1, align 1 +// LLVM-NEXT: %[[V3:.*]] = alloca ptr, i64 1, align 8 +// LLVM-NEXT: %[[V4:.*]] = alloca %"class.a::c", i64 1, align 1 +// LLVM-NEXT: %[[V5:.*]] = alloca %class.aj, i64 1, align 1 +// LLVM-NEXT: store ptr {{.*}}, ptr %[[V3]], align 8 +// LLVM-NEXT: %[[V6:.*]] = load ptr, ptr %[[V3]], align 8 +// LLVM-NEXT: br label %[[B7:.*]] +// LLVM: [[B7]]: +// LLVM-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr %[[V2]], ptr @an, i32 1, i1 false) +// LLVM-NEXT: %[[V8:.*]] = load %class.aj, ptr %[[V2]], align 1 +// LLVM-NEXT: call void @_ZN1a1cC1I2ajEET_(ptr %[[V4]], %class.aj %[[V8]]) +// LLVM-NEXT: call void @_ZN2ajD1Ev(ptr %[[V2]]) +// LLVM-NEXT: br label %[[B9:.*]] +// LLVM: [[B9]]: +// LLVM-NEXT: call void @_ZN2ajD1Ev(ptr %[[V5]]) +// LLVM-NEXT: %[[V10:.*]] = load %"class.a::c", ptr %[[V4]], align 1 +// LLVM-NEXT: ret %"class.a::c" %[[V10]] +// LLVM-NEXT: } + +// OGCG-LABEL: {{.*}} @_ZNK1d1eEv(ptr {{.*}}) +// OGCG: %{{.*}} = alloca ptr, align 8 +// OGCG-NEXT: %{{.*}} = alloca ptr, align 8 +// OGCG-NEXT: %{{.*}} = alloca %{{.*}}, align 1 +// OGCG-NEXT: %{{.*}} = alloca %{{.*}}, align 1 +// OGCG-NEXT: store ptr %{{.*}}, ptr %{{.*}}, align 8 +// OGCG-NEXT: store ptr %{{.*}}, ptr %{{.*}}, align 8 +// OGCG-NEXT: %{{.*}} = load ptr, ptr %{{.*}}, align 8 +// OGCG-NEXT: call void @_ZN1a1cC1I2ajEET_(ptr noundef nonnull align 1 dereferenceable(1) %{{.*}}, ptr noundef %{{.*}}) +// OGCG-NEXT: call void @_ZN2ajD1Ev(ptr noundef nonnull align 1 dereferenceable(1) %{{.*}}) +// OGCG-NEXT: call void @_ZN2ajD1Ev(ptr noundef nonnull align 1 dereferenceable(1) %{{.*}}) +// OGCG-NEXT: ret void +// OGCG-NEXT: } + +inline namespace a { +class c { +public: + template c(b); + ~c(); +}; +} // namespace a +class d { + c e() const; +}; +class aj { +public: + ~aj(); +} an; +c d::e() const { + aj ao; + return an; + c(0); +} + +// CIR-LABEL: cir.func {{.*}} @_Z3foov +// CIR-NEXT: %[[V0:.*]] = cir.alloca !s32i, !cir.ptr, ["__retval"] {alignment = 4 : i64} +// CIR-NEXT: %[[V1:.*]] = cir.alloca !rec_std3A3Abasic_string3Cchar3E, !cir.ptr, ["a", init] +// CIR-NEXT: %[[V2:.*]] = cir.alloca !rec_std3A3Abasic_string3Cchar3E, !cir.ptr, ["b"] +// CIR-NEXT: cir.call @_ZNSbIcEC1Ev(%[[V1]]) : (!cir.ptr) -> () +// CIR-NEXT: %[[V3:.*]] = cir.const #cir.int<0> : !s32i +// CIR-NEXT: cir.store align(4) %[[V3]], %[[V0]] : !s32i, !cir.ptr +// CIR-NEXT: cir.call @_ZNSbIcED1Ev(%[[V1]]) : (!cir.ptr) -> () extra(#fn_attr) +// CIR-NEXT: %[[V4:.*]] = cir.load align(4) %[[V0]] : !cir.ptr, !s32i +// CIR-NEXT: cir.return %[[V4]] : !s32i +// CIR-NEXT: } + +// LLVM-LABEL: {{.*}} @_Z3foov() +// LLVM-NEXT: %[[V1:.*]] = alloca i32, i64 1, align 4 +// LLVM-NEXT: %[[V2:.*]] = alloca %"class.std::basic_string", i64 1, align 1 +// LLVM-NEXT: %[[V3:.*]] = alloca %"class.std::basic_string", i64 1, align 1 +// LLVM-NEXT: call void @_ZNSbIcEC1Ev(ptr %[[V2]]) +// LLVM-NEXT: store i32 0, ptr %[[V1]], align 4 +// LLVM-NEXT: call void @_ZNSbIcED1Ev(ptr %[[V2]]) +// LLVM-NEXT: %[[V4:.*]] = load i32, ptr %[[V1]], align 4 +// LLVM-NEXT: ret i32 %[[V4]] +// LLVM-NEXT: } + +// OGCG-LABEL: {{.*}} @_Z3foov() +// OGCG: %[[a:.*]] = alloca %"class.std::basic_string", align 1 +// OGCG-NEXT: %[[b:.*]] = alloca %"class.std::basic_string", align 1 +// OGCG-NEXT: call void @_ZNSbIcEC1Ev(ptr noundef nonnull align 1 dereferenceable(1) %[[a]]) +// OGCG-NEXT: call void @_ZNSbIcED1Ev(ptr noundef nonnull align 1 dereferenceable(1) %[[a]]) +// OGCG-NEXT: ret i32 0 +// OGCG-NEXT: } + +int foo() { + std::string a; + return 0; + std::string b; +} diff --git a/clang/test/CIR/crashes/cleanup-892-null-fixups.cpp b/clang/test/CIR/crashes/cleanup-892-null-fixups.cpp deleted file mode 100644 index 5a8d99c0bec8..000000000000 --- a/clang/test/CIR/crashes/cleanup-892-null-fixups.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir -// XFAIL: * -// -// Test for UNREACHABLE at CIRGenCleanup.cpp:892 -// Null fixups popping not yet implemented -// -// This test triggers the error: -// "UNREACHABLE executed at CIRGenCleanup.cpp:892!" -// -// Original failure: cleanup_892 from LLVM build -// Reduced from /tmp/Regex-8cd677.cpp - -inline namespace a { -class c { -public: - template c(b); - ~c(); -}; -} // namespace a -class d { - c e() const; -}; -class aj { -public: - ~aj(); -} an; -c d::e() const { - aj ao; - return an; - c(0); -}