https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/182953
Because we are using a structured representation of cleanups in CIR, we don't need to handle branching through cleanups during codegen. These branches are created during CFG flattening instead. However, we had already committed some code that copied the classic codegen behavior for branching through cleanups. This change deletes that unneeded code. The most significant change here is that when we encounter a return statement we emit the return directly in the current location. The coroutine implementation still creates a return block in the current lexical scope and branches to that block. Cleaning up that representation is left as future work. The popCleanupBlock handling still has a significant amount of logic that is carried over from the classic codegen implementation. It is left in place until we can be sure we won't need it. >From e2bc0eb19d7f7327c0982ccdd941ad7d7fd217bc Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Thu, 19 Feb 2026 17:52:58 -0800 Subject: [PATCH] [CIR] Remove branch through cleanup fixups Because we are using a structured representation of cleanups in CIR, we don't need to handle branching through cleanups during codegen. These branches are created during CFG flattening instead. However, we had already committed some code that copied the classic codegen behavior for branching through cleanups. This change deletes that unneeded code. The most significant change here is that when we encounter a return statement we emit the return directly in the current location. The coroutine implementation still creates a return block in the current lexical scope and branches to that block. Cleaning up that representation is left as future work. The popCleanupBlock handling still has a significant amount of logic that is carried over from the classic codegen implementation. It is left in place until we can be sure we won't need it. --- clang/include/clang/CIR/MissingFeatures.h | 4 - clang/lib/CIR/CodeGen/CIRGenCleanup.cpp | 100 ++-------------------- clang/lib/CIR/CodeGen/CIRGenCleanup.h | 11 +-- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 27 +++++- clang/lib/CIR/CodeGen/CIRGenFunction.h | 42 --------- clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 48 +++++++---- clang/lib/CIR/CodeGen/EHScopeStack.h | 61 ------------- clang/test/CIR/CodeGen/goto.cpp | 14 ++- clang/test/CIR/CodeGen/label-values.c | 28 +++--- clang/test/CIR/CodeGen/label.c | 10 +-- clang/test/CIR/CodeGen/nrvo.cpp | 28 +++--- clang/test/CIR/CodeGen/vla.c | 6 +- 12 files changed, 109 insertions(+), 270 deletions(-) diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 97c76df0bb0b9..4fe4715511be7 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -263,13 +263,9 @@ struct MissingFeatures { static bool devirtualizeMemberFunction() { return false; } static bool dtorCleanups() { return false; } static bool ehCleanupActiveFlag() { return false; } - static bool ehCleanupHasPrebranchedFallthrough() { return false; } static bool ehCleanupScope() { return false; } static bool ehCleanupScopeRequiresEHCleanup() { return false; } - static bool ehCleanupBranchFixups() { return false; } static bool ehScopeFilter() { return false; } - static bool ehstackBranches() { return false; } - static bool emitBranchThroughCleanup() { return false; } static bool emitCheckedInBoundsGEP() { return false; } static bool emitCondLikelihoodViaExpectIntrinsic() { return false; } static bool emitConstrainedFPCall() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp index fc6417ce0c3e5..804cf3fcfbd6c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp @@ -28,46 +28,6 @@ using namespace clang::CIRGen; // CIRGenFunction cleanup related //===----------------------------------------------------------------------===// -/// Build a unconditional branch to the lexical scope cleanup block -/// or with the labeled blocked if already solved. -/// -/// Track on scope basis, goto's we need to fix later. -cir::BrOp CIRGenFunction::emitBranchThroughCleanup(mlir::Location loc, - JumpDest dest) { - // Insert a branch: to the cleanup block (unsolved) or to the already - // materialized label. Keep track of unsolved goto's. - assert(dest.getBlock() && "assumes incoming valid dest"); - auto brOp = cir::BrOp::create(builder, loc, dest.getBlock()); - - // Calculate the innermost active normal cleanup. - EHScopeStack::stable_iterator topCleanup = - ehStack.getInnermostActiveNormalCleanup(); - - // If we're not in an active normal cleanup scope, or if the - // destination scope is within the innermost active normal cleanup - // scope, we don't need to worry about fixups. - if (topCleanup == ehStack.stable_end() || - topCleanup.encloses(dest.getScopeDepth())) { // works for invalid - // FIXME(cir): should we clear insertion point here? - return brOp; - } - - // If we can't resolve the destination cleanup scope, just add this - // to the current cleanup scope as a branch fixup. - if (!dest.getScopeDepth().isValid()) { - BranchFixup &fixup = ehStack.addBranchFixup(); - fixup.destination = dest.getBlock(); - fixup.destinationIndex = dest.getDestIndex(); - fixup.initialBranch = brOp; - fixup.optimisticBranchBlock = nullptr; - // FIXME(cir): should we clear insertion point here? - return brOp; - } - - cgm.errorNYI(loc, "emitBranchThroughCleanup: valid destination scope depth"); - return brOp; -} - /// Emits all the code to cause the given temporary to be cleaned up. void CIRGenFunction::emitCXXTemporary(const CXXTemporary *temporary, QualType tempType, Address ptr) { @@ -128,17 +88,6 @@ void EHScopeStack::deallocate(size_t size) { startOfData += llvm::alignTo(size, ScopeStackAlignment); } -/// Remove any 'null' fixups on the stack. However, we can't pop more -/// fixups than the fixup depth on the innermost normal cleanup, or -/// else fixups that we try to add to that cleanup will end up in the -/// wrong place. We *could* try to shrink fixup depths, but that's -/// actually a lot of work for little benefit. -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. - cgf->cgm.errorNYI("popNullFixups"); -} - void *EHScopeStack::pushCleanup(CleanupKind kind, size_t size) { char *buffer = allocate(EHCleanupScope::getSizeForCleanupSize(size)); bool isNormalCleanup = kind & NormalCleanup; @@ -185,8 +134,8 @@ void *EHScopeStack::pushCleanup(CleanupKind kind, size_t size) { isEHCleanup = false; EHCleanupScope *scope = new (buffer) - EHCleanupScope(isNormalCleanup, isEHCleanup, size, branchFixups.size(), - cleanupScope, innermostNormalCleanup, innermostEHScope); + EHCleanupScope(isNormalCleanup, isEHCleanup, size, cleanupScope, + innermostNormalCleanup, innermostEHScope); if (isNormalCleanup) innermostNormalCleanup = stable_begin(); @@ -227,18 +176,6 @@ void EHScopeStack::popCleanup() { // Destroy the cleanup. cleanup.destroy(); - - // Check whether we can shrink the branch-fixups stack. - if (!branchFixups.empty()) { - // If we no longer have any normal cleanups, all the fixups are - // complete. - if (!hasNormalCleanups()) { - branchFixups.clear(); - } else { - // Otherwise we can still trim out unnecessary nulls. - popNullFixups(); - } - } } bool EHScopeStack::requiresCatchOrCleanup() const { @@ -291,14 +228,11 @@ static mlir::Block *createNormalEntry(CIRGenFunction &cgf, return entry; } -/// Pops a cleanup block. If the block includes a normal cleanup, the -/// current insertion point is threaded through the cleanup, as are -/// any branch fixups on the cleanup. +/// Pops a cleanup block. void CIRGenFunction::popCleanupBlock() { assert(!ehStack.empty() && "cleanup stack is empty!"); assert(isa<EHCleanupScope>(*ehStack.begin()) && "top not a cleanup!"); EHCleanupScope &scope = cast<EHCleanupScope>(*ehStack.begin()); - assert(scope.getFixupDepth() <= ehStack.getNumBranchFixups()); cir::CleanupScopeOp cleanupScope = scope.getCleanupScopeOp(); assert(cleanupScope && "CleanupScopeOp is nullptr"); @@ -306,16 +240,11 @@ void CIRGenFunction::popCleanupBlock() { // Remember activation information. bool isActive = scope.isActive(); - // - whether there are branch fix-ups through this cleanup - unsigned fixupDepth = scope.getFixupDepth(); - bool hasFixups = ehStack.getNumBranchFixups() != fixupDepth; - // - whether there's a fallthrough mlir::Block *fallthroughSource = builder.getInsertionBlock(); bool hasFallthrough = fallthroughSource != nullptr && isActive; - bool requiresNormalCleanup = - scope.isNormalCleanup() && (hasFixups || hasFallthrough); + bool requiresNormalCleanup = scope.isNormalCleanup() && hasFallthrough; // If we don't need the cleanup at all, we're done. assert(!cir::MissingFeatures::ehCleanupScopeRequiresEHCleanup()); @@ -356,7 +285,7 @@ void CIRGenFunction::popCleanupBlock() { // If we have a fallthrough and no other need for the cleanup, // emit it directly. - if (hasFallthrough && !hasFixups) { + if (hasFallthrough) { assert(!cir::MissingFeatures::ehCleanupScopeRequiresEHCleanup()); ehStack.popCleanup(); scope.markEmitted(); @@ -371,14 +300,9 @@ void CIRGenFunction::popCleanupBlock() { // I. Set up the fallthrough edge in. mlir::OpBuilder::InsertPoint savedInactiveFallthroughIP; - // If there's a fallthrough, we need to store the cleanup - // destination index. For fall-throughs this is always zero. - if (hasFallthrough) { - assert(!cir::MissingFeatures::ehCleanupHasPrebranchedFallthrough()); - - } else if (fallthroughSource) { - // Otherwise, save and clear the IP if we don't have fallthrough - // because the cleanup is inactive. + // If we have a fallthrough source, but this cleanup is inactive, + // save and clear the IP. + if (!hasFallthrough && fallthroughSource) { assert(!isActive && "source without fallthrough for active cleanup"); savedInactiveFallthroughIP = builder.saveInsertionPoint(); } @@ -402,7 +326,7 @@ void CIRGenFunction::popCleanupBlock() { // - if there are fixups that will be optimistically forwarded // to the enclosing cleanup assert(!cir::MissingFeatures::cleanupBranchThrough()); - if (hasFixups && hasEnclosingCleanups) + if (hasEnclosingCleanups) cgm.errorNYI("cleanup branch-through dest"); mlir::Block *fallthroughDest = nullptr; @@ -426,10 +350,6 @@ void CIRGenFunction::popCleanupBlock() { // Append the prepared cleanup prologue from above. assert(!cir::MissingFeatures::cleanupAppendInsts()); - // Optimistically hope that any fixups will continue falling through. - if (fixupDepth != ehStack.getNumBranchFixups()) - cgm.errorNYI("cleanup fixup depth mismatch"); - // V. Set up the fallthrough edge out. // Case 1: a fallthrough source exists but doesn't branch to the @@ -471,8 +391,6 @@ void CIRGenFunction::popCleanupBlock() { /// Pops cleanup blocks until the given savepoint is reached. void CIRGenFunction::popCleanupBlocks( EHScopeStack::stable_iterator oldCleanupStackDepth) { - assert(!cir::MissingFeatures::ehstackBranches()); - // Pop cleanup blocks until we reach the base stack depth for the // current scope. while (ehStack.stable_begin() != oldCleanupStackDepth) { diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.h b/clang/lib/CIR/CodeGen/CIRGenCleanup.h index aab678d032111..eeac5fdcafc3a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCleanup.h +++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.h @@ -108,11 +108,6 @@ class alignas(EHScopeStack::ScopeStackAlignment) EHCleanupScope /// created if needed before the cleanup is popped. mlir::Block *normalBlock = nullptr; - /// The number of fixups required by enclosing scopes (not including - /// this one). If this is the top cleanup scope, all the fixups - /// from this index onwards belong to this scope. - unsigned fixupDepth = 0; - /// Cleanup scope op that represent the current scope in CIR cir::CleanupScopeOp cleanupScopeOp; @@ -128,12 +123,11 @@ class alignas(EHScopeStack::ScopeStackAlignment) EHCleanupScope } EHCleanupScope(bool isNormal, bool isEH, unsigned cleanupSize, - unsigned fixupDepth, cir::CleanupScopeOp cleanupScopeOp, + cir::CleanupScopeOp cleanupScopeOp, EHScopeStack::stable_iterator enclosingNormal, EHScopeStack::stable_iterator enclosingEH) : EHScope(EHScope::Cleanup, enclosingEH), - enclosingNormal(enclosingNormal), fixupDepth(fixupDepth), - cleanupScopeOp(cleanupScopeOp) { + enclosingNormal(enclosingNormal), cleanupScopeOp(cleanupScopeOp) { cleanupBits.isNormalCleanup = isNormal; cleanupBits.isEHCleanup = isEH; cleanupBits.isActive = true; @@ -160,7 +154,6 @@ class alignas(EHScopeStack::ScopeStackAlignment) EHCleanupScope bool isLifetimeMarker() const { return cleanupBits.isLifetimeMarker; } - unsigned getFixupDepth() const { return fixupDepth; } EHScopeStack::stable_iterator getEnclosingNormalCleanup() const { return enclosingNormal; } diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index cc4fa7abf4edf..88545ba1ec1a3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -387,6 +387,30 @@ static bool mayDropFunctionReturn(const ASTContext &astContext, return returnType.isTriviallyCopyableType(astContext); } +static bool previousOpIsNonYieldingCleanup(mlir::Block *block) { + if (block->empty()) + return false; + mlir::Operation *op = &block->back(); + auto cleanupScopeOp = mlir::dyn_cast<cir::CleanupScopeOp>(op); + if (!cleanupScopeOp) + return false; + + // Check whether the body region of the cleanup scope exits via cir.yield. + // Exits via cir.return or cir.goto do not fall through to the operation + // following the cleanup scope, and exits via break, continue, and resume + // are not expected here. + for (mlir::Block &bodyBlock : cleanupScopeOp.getBodyRegion()) { + if (bodyBlock.mightHaveTerminator()) { + if (mlir::isa<cir::YieldOp>(bodyBlock.getTerminator())) + return false; + assert(!mlir::isa<cir::BreakOp>(bodyBlock.getTerminator()) && + !mlir::isa<cir::ContinueOp>(bodyBlock.getTerminator()) && + !mlir::isa<cir::ResumeOp>(bodyBlock.getTerminator())); + } + } + return true; +} + void CIRGenFunction::LexicalScope::emitImplicitReturn() { CIRGenBuilderTy &builder = cgf.getBuilder(); LexicalScope *localScope = cgf.curLexScope; @@ -400,7 +424,8 @@ void CIRGenFunction::LexicalScope::emitImplicitReturn() { // return. if (cgf.getLangOpts().CPlusPlus && !fd->hasImplicitReturnZero() && !cgf.sawAsmBlock && !fd->getReturnType()->isVoidType() && - builder.getInsertionBlock()) { + builder.getInsertionBlock() && + !previousOpIsNonYieldingCleanup(builder.getInsertionBlock())) { bool shouldEmitUnreachable = cgf.cgm.getCodeGenOpts().StrictReturn || !mayDropFunctionReturn(fd->getASTContext(), fd->getReturnType()); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index cf1722e6cf6df..041bd8b8b2837 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -63,42 +63,11 @@ class CIRGenFunction : public CIRGenTypeCache { /// is where the next operations will be introduced. CIRGenBuilderTy &builder; - /// A jump destination is an abstract label, branching to which may - /// require a jump out through normal cleanups. - struct JumpDest { - JumpDest() = default; - JumpDest(mlir::Block *block, EHScopeStack::stable_iterator depth = {}, - unsigned index = 0) - : block(block) {} - - bool isValid() const { return block != nullptr; } - mlir::Block *getBlock() const { return block; } - EHScopeStack::stable_iterator getScopeDepth() const { return scopeDepth; } - unsigned getDestIndex() const { return index; } - - // This should be used cautiously. - void setScopeDepth(EHScopeStack::stable_iterator depth) { - scopeDepth = depth; - } - - private: - mlir::Block *block = nullptr; - EHScopeStack::stable_iterator scopeDepth; - unsigned index; - }; - public: /// The GlobalDecl for the current function being compiled or the global /// variable currently being initialized. clang::GlobalDecl curGD; - /// Unified return block. - /// In CIR this is a function because each scope might have - /// its associated return block. - JumpDest returnBlock(mlir::Block *retBlock) { - return getJumpDestInCurrentScope(retBlock); - } - unsigned nextCleanupDestIndex = 1; /// The compiler-generated variable that holds the return value. @@ -665,15 +634,6 @@ class CIRGenFunction : public CIRGenTypeCache { } }; - /// The given basic block lies in the current EH scope, but may be a - /// target of a potentially scope-crossing jump; get a stable handle - /// to which we can perform this jump later. - /// CIRGen: this mostly tracks state for figuring out the proper scope - /// information, no actual branches are emitted. - JumpDest getJumpDestInCurrentScope(mlir::Block *target) { - return JumpDest(target, ehStack.getInnermostNormalCleanup(), - nextCleanupDestIndex++); - } /// IndirectBranch - The first time an indirect goto is seen we create a block /// reserved for the indirect branch. Unlike before,the actual 'indirectbr' /// is emitted at the end of the function, once all block destinations have @@ -1416,8 +1376,6 @@ class CIRGenFunction : public CIRGenTypeCache { LValue emitBinaryOperatorLValue(const BinaryOperator *e); - cir::BrOp emitBranchThroughCleanup(mlir::Location loc, JumpDest dest); - mlir::LogicalResult emitBreakStmt(const clang::BreakStmt &s); RValue emitBuiltinExpr(const clang::GlobalDecl &gd, unsigned builtinID, diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index db3827340c455..0107e62678317 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -639,16 +639,27 @@ mlir::LogicalResult CIRGenFunction::emitReturnStmt(const ReturnStmt &s) { cleanupScope.forceCleanup(); - // In CIR we might have returns in different scopes. - // FIXME(cir): cleanup code is handling actual return emission, the logic - // should try to match traditional codegen more closely (to the extent which - // is possible). - auto *retBlock = curLexScope->getOrCreateRetBlock(*this, loc); - emitBranchThroughCleanup(loc, returnBlock(retBlock)); - - // Insert the new block to continue codegen after branch to ret block. - builder.createBlock(builder.getBlock()->getParent()); + // Classic codegen emits a branch through any cleanups before continuing to + // a shared return block. Because CIR handles branching through cleanups + // during the CFG flattening phase, we can just emit the return statement + // directly. + // TODO(cir): Eliminate this redundant load and the store above when we can. + if (fnRetAlloca) { + // Load the value from `__retval` and return it via the `cir.return` op. + cir::AllocaOp retAlloca = + mlir::cast<cir::AllocaOp>(fnRetAlloca->getDefiningOp()); + auto value = cir::LoadOp::create(builder, loc, retAlloca.getAllocaType(), + *fnRetAlloca); + + cir::ReturnOp::create(builder, loc, {value}); + } else { + cir::ReturnOp::create(builder, loc); + } + // Insert the new block to continue codegen after the return statement. + // This will get deleted if we don't populate it. This handles the case of + // unreachable statements below a return. + builder.createBlock(builder.getBlock()->getParent()); return mlir::success(); } @@ -716,7 +727,6 @@ mlir::LogicalResult CIRGenFunction::emitLabel(const clang::LabelDecl &d) { label.getLabelAttr()), label); // FIXME: emit debug info for labels, incrementProfileCounter - assert(!cir::MissingFeatures::ehstackBranches()); assert(!cir::MissingFeatures::incrementProfileCounter()); assert(!cir::MissingFeatures::generateDebugInfo()); return mlir::success(); @@ -1228,9 +1238,17 @@ void CIRGenFunction::emitReturnOfRValue(mlir::Location loc, RValue rv, } else { cgm.errorNYI(loc, "emitReturnOfRValue: complex return type"); } - mlir::Block *retBlock = curLexScope->getOrCreateRetBlock(*this, loc); - assert(!cir::MissingFeatures::emitBranchThroughCleanup()); - cir::BrOp::create(builder, loc, retBlock); - if (ehStack.stable_begin() != currentCleanupStackDepth) - cgm.errorNYI(loc, "return of r-value with cleanup stack"); + + // Classic codegen emits a branch through any cleanups before continuing to + // a shared return block. Because CIR handles branching through cleanups + // during the CFG flattening phase, we can just emit the return statement + // directly. + // TODO(cir): Eliminate this redundant load and the store above when we can. + // Load the value from `__retval` and return it via the `cir.return` op. + cir::AllocaOp retAlloca = + mlir::cast<cir::AllocaOp>(fnRetAlloca->getDefiningOp()); + auto value = cir::LoadOp::create(builder, loc, retAlloca.getAllocaType(), + *fnRetAlloca); + + cir::ReturnOp::create(builder, loc, {value}); } diff --git a/clang/lib/CIR/CodeGen/EHScopeStack.h b/clang/lib/CIR/CodeGen/EHScopeStack.h index bbe0fced1c6ba..347ecbdfdfa2e 100644 --- a/clang/lib/CIR/CodeGen/EHScopeStack.h +++ b/clang/lib/CIR/CodeGen/EHScopeStack.h @@ -25,31 +25,6 @@ namespace clang::CIRGen { class CIRGenFunction; -/// A branch fixup. These are required when emitting a goto to a -/// label which hasn't been emitted yet. The goto is optimistically -/// emitted as a branch to the basic block for the label, and (if it -/// occurs in a scope with non-trivial cleanups) a fixup is added to -/// the innermost cleanup. When a (normal) cleanup is popped, any -/// unresolved fixups in that scope are threaded through the cleanup. -struct BranchFixup { - /// The block containing the terminator which needs to be modified - /// into a switch if this fixup is resolved into the current scope. - /// If null, LatestBranch points directly to the destination. - mlir::Block *optimisticBranchBlock = nullptr; - - /// The ultimate destination of the branch. - /// - /// This can be set to null to indicate that this fixup was - /// successfully resolved. - mlir::Block *destination = nullptr; - - /// The destination index value. - unsigned destinationIndex = 0; - - /// The initial branch of the fixup. - cir::BrOp initialBranch = {}; -}; - enum CleanupKind : unsigned { /// Denotes a cleanup that should run when a scope is exited using exceptional /// control flow (a throw statement leading to stack unwinding, ). @@ -191,25 +166,6 @@ class EHScopeStack { /// The CGF this Stack belong to CIRGenFunction *cgf = nullptr; - /// The current set of branch fixups. A branch fixup is a jump to - /// an as-yet unemitted label, i.e. a label for which we don't yet - /// know the EH stack depth. Whenever we pop a cleanup, we have - /// to thread all the current branch fixups through it. - /// - /// Fixups are recorded as the Use of the respective branch or - /// switch statement. The use points to the final destination. - /// When popping out of a cleanup, these uses are threaded through - /// the cleanup and adjusted to point to the new cleanup. - /// - /// Note that branches are allowed to jump into protected scopes - /// in certain situations; e.g. the following code is legal: - /// struct A { ~A(); }; // trivial ctor, non-trivial dtor - /// goto foo; - /// A a; - /// foo: - /// bar(); - llvm::SmallVector<BranchFixup> branchFixups; - // This class uses a custom allocator for maximum efficiency because cleanups // are allocated and freed very frequently. It's basically a bump pointer // allocator, but we can't use LLVM's BumpPtrAllocator because we use offsets @@ -279,23 +235,6 @@ class EHScopeStack { /// to the EH stack. iterator find(stable_iterator savePoint) const; - /// Add a branch fixup to the current cleanup scope. - BranchFixup &addBranchFixup() { - assert(hasNormalCleanups() && "adding fixup in scope without cleanups"); - branchFixups.push_back(BranchFixup()); - return branchFixups.back(); - } - - unsigned getNumBranchFixups() const { return branchFixups.size(); } - BranchFixup &getBranchFixup(unsigned i) { - assert(i < getNumBranchFixups()); - return branchFixups[i]; - } - - /// Pops lazily-removed fixups from the end of the list. This - /// should only be called by procedures which have just popped a - /// cleanup or resolved one or more fixups. - void popNullFixups(); }; } // namespace clang::CIRGen diff --git a/clang/test/CIR/CodeGen/goto.cpp b/clang/test/CIR/CodeGen/goto.cpp index 2feba69dbf619..a4d1fc35e9f18 100644 --- a/clang/test/CIR/CodeGen/goto.cpp +++ b/clang/test/CIR/CodeGen/goto.cpp @@ -18,15 +18,14 @@ int shouldNotGenBranchRet(int x) { // CIR: } // CIR: [[ZERO:%.*]] = cir.const #cir.int<0> : !s32i // CIR: cir.store [[ZERO]], [[RETVAL:%.*]] : !s32i, !cir.ptr<!s32i> -// CIR: cir.br ^bb1 -// CIR: ^bb1: // CIR: [[RET:%.*]] = cir.load [[RETVAL]] : !cir.ptr<!s32i>, !s32i // CIR: cir.return [[RET]] : !s32i -// CIR: ^bb2: +// CIR: ^bb1: // CIR: cir.label "err" // CIR: [[MINUS_ONE:%.*]] = cir.const #cir.int<-1> : !s32i // CIR: cir.store [[MINUS_ONE]], [[RETVAL]] : !s32i, !cir.ptr<!s32i> -// CIR: cir.br ^bb1 +// CIR: [[RET:%.*]] = cir.load [[RETVAL]] : !cir.ptr<!s32i>, !s32i +// CIR: cir.return [[RET]] : !s32i // LLVM: define dso_local noundef i32 @_Z21shouldNotGenBranchReti // LLVM: [[COND:%.*]] = load i32, ptr {{.*}}, align 4 @@ -35,16 +34,13 @@ int shouldNotGenBranchRet(int x) { // LLVM: [[IFTHEN]]: // LLVM: br label %[[ERR:.*]] // LLVM: [[IFEND]]: -// LLVM: br label %[[BB9:.*]] -// LLVM: [[BB9]]: // LLVM: store i32 0, ptr %[[RETVAL:.*]], align 4 -// LLVM: br label %[[BBRET:.*]] -// LLVM: [[BBRET]]: // LLVM: [[RET:%.*]] = load i32, ptr %[[RETVAL]], align 4 // LLVM: ret i32 [[RET]] // LLVM: [[ERR]]: // LLVM: store i32 -1, ptr %[[RETVAL]], align 4 -// LLVM: br label %10 +// LLVM: [[RET:%.*]] = load i32, ptr %[[RETVAL]], align 4 +// LLVM: ret i32 [[RET]] // OGCG: define dso_local noundef i32 @_Z21shouldNotGenBranchReti // OGCG: if.then: diff --git a/clang/test/CIR/CodeGen/label-values.c b/clang/test/CIR/CodeGen/label-values.c index 8ba0dd72f9ef5..639bcb0fd4ae4 100644 --- a/clang/test/CIR/CodeGen/label-values.c +++ b/clang/test/CIR/CodeGen/label-values.c @@ -102,37 +102,33 @@ void C(int x) { // CIR: [[BLOCK1:%.*]] = cir.block_address <@C, "LABEL_A"> : !cir.ptr<!void> // CIR: [[BLOCK2:%.*]] = cir.block_address <@C, "LABEL_B"> : !cir.ptr<!void> // CIR: [[COND:%.*]] = cir.select if [[CMP:%.*]] then [[BLOCK1]] else [[BLOCK2]] : (!cir.bool, !cir.ptr<!void>, !cir.ptr<!void>) -> !cir.ptr<!void> -// CIR: cir.store align(8) [[COND]], [[PTR:%.*]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>> -// CIR: [[BLOCKADD:%.*]] = cir.load align(8) [[PTR]] : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void> -// CIR: cir.br ^bb1([[BLOCKADD]] : !cir.ptr<!void>) -// CIR: ^bb1([[PHI:%.*]]: !cir.ptr<!void> {{.*}}): // pred: ^bb0 +// CIR: cir.store{{.*}} [[COND]], [[PTR:%.*]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>> +// CIR: [[BLOCKADD:%.*]] = cir.load{{.*}} [[PTR]] : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void> +// CIR: cir.br ^[[INDIRECT_GOTO:.*]]([[BLOCKADD]] : !cir.ptr<!void>) +// CIR: ^[[INDIRECT_GOTO]]([[PHI:%.*]]: !cir.ptr<!void> {{.*}}): // CIR: cir.indirect_br [[PHI]] : !cir.ptr<!void>, [ -// CIR-NEXT: ^bb2, -// CIR-NEXT: ^bb4 +// CIR-NEXT: ^[[LABEL_A_BB:.*]], +// CIR-NEXT: ^[[LABEL_B_BB:.*]] // CIR: ] -// CIR: ^bb2: // pred: ^bb1 +// CIR: ^[[LABEL_A_BB]]: // CIR: cir.label "LABEL_A" -// CIR: cir.br ^bb3 -// CIR: ^bb3: // 2 preds: ^bb2, ^bb4 // CIR: cir.return -// CIR: ^bb4: // pred: ^bb1 +// CIR: ^[[LABEL_B_BB]]: // CIR: cir.label "LABEL_B" -// CIR: cir.br ^bb3 +// CIR: cir.return // LLVM: define dso_local void @C(i32 %0) // LLVM: [[COND:%.*]] = select i1 [[CMP:%.*]], ptr blockaddress(@C, %[[LABEL_A:.*]]), ptr blockaddress(@C, %[[LABEL_B:.*]]) // LLVM: store ptr [[COND]], ptr [[PTR:%.*]], align 8 // LLVM: [[BLOCKADD:%.*]] = load ptr, ptr [[PTR]], align 8 -// LLVM: br label %[[indirectgoto:.*]] -// LLVM: [[indirectgoto]]: +// LLVM: br label %[[INDIRECT_GOTO:.*]] +// LLVM: [[INDIRECT_GOTO]]: // LLVM: [[PHI:%.*]] = phi ptr [ [[BLOCKADD]], %[[ENTRY:.*]] ] // LLVM: indirectbr ptr [[PHI]], [label %[[LABEL_A]], label %[[LABEL_B]]] // LLVM: [[LABEL_A]]: -// LLVM: br label %[[RET:.*]] -// LLVM: [[RET]]: // LLVM: ret void // LLVM: [[LABEL_B]]: -// LLVM: br label %[[RET]] +// LLVM: ret void // OGCG: define dso_local void @C // OGCG: [[COND:%.*]] = select i1 [[CMP:%.*]], ptr blockaddress(@C, %LABEL_A), ptr blockaddress(@C, %LABEL_B) diff --git a/clang/test/CIR/CodeGen/label.c b/clang/test/CIR/CodeGen/label.c index d3fdf5f5abb77..93ed6c583b98a 100644 --- a/clang/test/CIR/CodeGen/label.c +++ b/clang/test/CIR/CodeGen/label.c @@ -108,19 +108,15 @@ void after_return() { } // CIR: cir.func {{.*}} @after_return -// CIR: cir.br ^bb1 -// CIR: ^bb1: // 2 preds: ^bb0, ^bb2 // CIR: cir.return -// CIR: ^bb2: // no predecessors +// CIR: ^bb1: // no predecessors // CIR: cir.label "label" -// CIR: cir.br ^bb1 +// CIR: cir.return // LLVM: define dso_local void @after_return{{.*}} -// LLVM: br label %1 +// LLVM: ret void // LLVM: 1: // LLVM: ret void -// LLVM: 2: -// LLVM: br label %1 // OGCG: define dso_local void @after_return // OGCG: br label %label diff --git a/clang/test/CIR/CodeGen/nrvo.cpp b/clang/test/CIR/CodeGen/nrvo.cpp index a4d4657b5be39..5988871c8d4b9 100644 --- a/clang/test/CIR/CodeGen/nrvo.cpp +++ b/clang/test/CIR/CodeGen/nrvo.cpp @@ -11,8 +11,6 @@ // lowering isn't of interest for this test. We just need to see that the // copy constructor is elided without -fno-elide-constructors but not with it. -// XFAIL: * - struct S { S(); int a; @@ -71,16 +69,24 @@ NonTrivial test_nrvo() { // CIR: %[[NRVO_FLAG:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["nrvo"] // CIR: %[[FALSE:.*]] = cir.const #false // CIR: cir.store{{.*}} %[[FALSE]], %[[NRVO_FLAG]] -// CIR: cir.call @_Z10maybeThrowv() : () -> () -// CIR: %[[TRUE:.*]] = cir.const #true -// CIR: cir.store{{.*}} %[[TRUE]], %[[NRVO_FLAG]] -// CIR: %[[NRVO_FLAG_VAL:.*]] = cir.load{{.*}} %[[NRVO_FLAG]] -// CIR: %[[NOT_NRVO_VAL:.*]] = cir.unary(not, %[[NRVO_FLAG_VAL]]) -// CIR: cir.if %[[NOT_NRVO_VAL]] { -// CIR: cir.call @_ZN10NonTrivialD1Ev(%[[RESULT]]) +// CIR: cir.cleanup.scope { +// CIR: cir.call @_Z10maybeThrowv() : () -> () +// CIR: %[[TRUE:.*]] = cir.const #true +// CIR: cir.store{{.*}} %[[TRUE]], %[[NRVO_FLAG]] +// CIR: %[[RET:.*]] = cir.load %[[RESULT]] +// CIR: cir.return %[[RET]] +// CIR: } cleanup normal { +// CIR: %[[NRVO_FLAG_VAL:.*]] = cir.load{{.*}} %[[NRVO_FLAG]] +// CIR: %[[NOT_NRVO_VAL:.*]] = cir.unary(not, %[[NRVO_FLAG_VAL]]) +// CIR: cir.if %[[NOT_NRVO_VAL]] { +// CIR: cir.call @_ZN10NonTrivialD1Ev(%[[RESULT]]) +// CIR: } +// CIR: cir.yield // CIR: } -// CIR: %[[RET:.*]] = cir.load %[[RESULT]] -// CIR: cir.return %[[RET]] +// +// TODO(cir): This is unreachable, but it really shouldn't be here. This is an +// artifact of us falling through to emitImplicitReturn(). +// CIR: cir.trap // LLVM: define {{.*}} %struct.NonTrivial @_Z9test_nrvov() // LLVM: %[[RESULT:.*]] = alloca %struct.NonTrivial diff --git a/clang/test/CIR/CodeGen/vla.c b/clang/test/CIR/CodeGen/vla.c index a70c721c2252e..44f2dcb10a086 100644 --- a/clang/test/CIR/CodeGen/vla.c +++ b/clang/test/CIR/CodeGen/vla.c @@ -5,8 +5,6 @@ // RUN: %clang_cc1 -Wno-error=incompatible-pointer-types -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll // RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG -// XFAIL: * - void f0(int len) { int arr[len]; } @@ -158,13 +156,13 @@ void f3(unsigned len) { // CIR: cir.func{{.*}} @f3(%[[LEN_ARG:.*]]: !u32i {{.*}}) // CIR: %[[LEN_ADDR:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["len", init] // CIR: %[[SAVED_STACK:.*]] = cir.alloca !cir.ptr<!u8i>, !cir.ptr<!cir.ptr<!u8i>>, ["saved_stack"] +// CIR: %[[I:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["i", init] // CIR: cir.store{{.*}} %[[LEN_ARG]], %[[LEN_ADDR]] // CIR: %[[LEN:.*]] = cir.load{{.*}} %[[LEN_ADDR]] // CIR: %[[LEN_SIZE_T:.*]] = cir.cast integral %[[LEN]] : !u32i -> !u64i // CIR: %[[STACK_PTR:.*]] = cir.stacksave // CIR: cir.store{{.*}} %[[STACK_PTR]], %[[SAVED_STACK]] // CIR: %[[S1:.*]] = cir.alloca !s8i, !cir.ptr<!s8i>, %[[LEN_SIZE_T]] : !u64i, ["s1"] -// CIR: %[[I:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["i", init] // CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !u32i // CIR: cir.store{{.*}} %[[ZERO]], %[[I]] // CIR: cir.scope { @@ -196,13 +194,13 @@ void f3(unsigned len) { // LLVM: %[[SAVED_STACK2:.*]] = alloca ptr // LLVM: %[[LEN_ADDR:.*]] = alloca i32 // LLVM: %[[SAVED_STACK:.*]] = alloca ptr +// LLVM: %[[I:.*]] = alloca i32 // LLVM: store i32 %[[LEN_ARG]], ptr %[[LEN_ADDR]] // LLVM: %[[LEN:.*]] = load i32, ptr %[[LEN_ADDR]] // LLVM: %[[LEN_SIZE_T:.*]] = zext i32 %[[LEN]] to i64 // LLVM: %[[STACK_PTR:.*]] = call ptr @llvm.stacksave.p0() // LLVM: store ptr %[[STACK_PTR]], ptr %[[SAVED_STACK]] // LLVM: %[[S1:.*]] = alloca i8, i64 %[[LEN_SIZE_T]] -// LLVM: %[[I:.*]] = alloca i32 // LLVM: store i32 0, ptr %[[I]] // LLVM: br label %[[WHILE_START:.*]] // LLVM: [[WHILE_START]]: _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
