https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/210472
This change adds support for CFG flattening of loop operations with cleanup regions. Rather than re-invent the entire branch through cleanup mechanism, I am first rewriting the loop by sinking the condition into the body (as a test + break-if-false) and hoisting the step into the body, and surrounding the entire body with a cir.cleanup.scope op. Then the greedy flattening algorithm runs the newly formed loop through the existing flattening mechanisms so that the loop cleanup handling ends up being done by the same code that flattens other cleanup scopes. Assisted-by: Cursor / various models >From 357023cd8d97e9e82da3221b0062f457778f1bb9 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Thu, 16 Jul 2026 09:38:54 -0700 Subject: [PATCH] [CIR] Flatten loop ops with a cleanup region This change adds support for CFG flattening of loop operations with cleanup regions. Rather than re-invent the entire branch through cleanup mechanism, I am first rewriting the loop by sinking the condition into the body (as a test + break-if-false) and hoisting the step into the body, and surrounding the entire body with a cir.cleanup.scope op. Then the greedy flattening algorithm runs the newly formed loop through the existing flattening mechanisms so that the loop cleanup handling ends up being done by the same code that flattens other cleanup scopes. Assisted-by: Cursor / various models --- .../lib/CIR/Dialect/Transforms/FlattenCFG.cpp | 169 ++++++++++ .../CIR/Transforms/flatten-loop-cleanup.cir | 290 ++++++++++++++++++ 2 files changed, 459 insertions(+) create mode 100644 clang/test/CIR/Transforms/flatten-loop-cleanup.cir diff --git a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp index a878199c03d99..940db1d3b74d4 100644 --- a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp +++ b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp @@ -518,6 +518,164 @@ class CIRLoopOpInterfaceFlattening exit); } + // Return the cleanup-kind attribute or a null attribute if the loop has no + // cleanups. + static cir::CleanupKindAttr getLoopCleanupKind(cir::LoopOpInterface op) { + if (auto whileOp = mlir::dyn_cast<cir::WhileOp>(op.getOperation())) + return whileOp.getCleanupKindAttr(); + if (auto forOp = mlir::dyn_cast<cir::ForOp>(op.getOperation())) + return forOp.getCleanupKindAttr(); + return {}; + } + + // Rewrite a loop that has a per-iteration cleanup region into a loop whose + // condition is always 'true' and whose body is a cir.cleanup.scope enclosing + // the original condition, body, and step regions. + // + // The condition test is sunk to the top of the scope body (a false + // result becomes a cir.break out of the loop) and, for a for loop, the step + // is appended to the end of the scope body. The loop's cleanup region becomes + // the a cir.cleanup.scope enclosing the body region. + // + // For example, a while loop: + // + // cir.while { <cond>; cir.condition(%c) } + // do { <body> } + // cleanup all { <cleanup> } + // + // becomes: + // + // cir.while { cir.condition(%true) } do { + // cir.cleanup.scope { + // <cond> + // cir.brcond %c ^body, ^cond_false + // ^cond_false: + // cir.break + // ^body: + // <body> + // } cleanup all { <cleanup> } + // cir.yield + // } + // + // A for loop is the same, except the step region is appended to the scope + // body (after the body) and both the body's normal end and any continue are + // redirected into the step so that the step runs before the cleanup. The init + // section of a for loop is already outside the loop op. + mlir::LogicalResult + rewriteLoopWithCleanup(cir::LoopOpInterface op, + mlir::PatternRewriter &rewriter) const { + mlir::Location loc = op.getLoc(); + mlir::Region *stepRegion = op.maybeGetStep(); + + cir::CleanupKindAttr cleanupKind = getLoopCleanupKind(op); + assert(cleanupKind && "loop cleanup region without a cleanup kind"); + + mlir::Region &condRegion = op.getCond(); + mlir::Region &bodyRegion = op.getBody(); + mlir::Region &cleanupRegion = *op.maybeGetCleanup(); + + // The cir.condition is the terminator of the condition region's last block + // (there can be more than one block if a nested scope was already + // flattened within the condition). + auto conditionOp = + cast<cir::ConditionOp>(condRegion.back().getTerminator()); + mlir::Value condVal = conditionOp.getCondition(); + + // Capture block references and exit terminators before moving blocks + // around. Block pointers stay valid across region inlining because blocks + // are reparented, not recreated. + mlir::Block *bodyFront = &bodyRegion.front(); + mlir::Block *stepFront = stepRegion ? &stepRegion->front() : nullptr; + + // For a for loop the body's normal end and any continue must run the step + // before the cleanup, so collect them to redirect into the step region. For + // a while loop they are genuine cleanup-scope exits and are left untouched. + llvm::SmallVector<cir::YieldOp> bodyYieldsToStep; + llvm::SmallVector<cir::ContinueOp> continuesToStep; + if (stepRegion) { + for (mlir::Block &blk : bodyRegion.getBlocks()) + if (auto y = dyn_cast<cir::YieldOp>(blk.getTerminator())) + bodyYieldsToStep.push_back(y); + op.walkBodySkippingNestedLoops([&](mlir::Operation *o) { + if (auto c = dyn_cast<cir::ContinueOp>(o)) { + continuesToStep.push_back(c); + return mlir::WalkResult::skip(); + } + return mlir::WalkResult::advance(); + }); + } + + // Assemble the per-iteration blocks into the condition region, in + // execution order: [ cond blocks..., body blocks..., step blocks... ]. + rewriter.inlineRegionBefore(bodyRegion, condRegion, condRegion.end()); + if (stepRegion) + rewriter.inlineRegionBefore(*stepRegion, condRegion, condRegion.end()); + + // Replace cir.condition(%c) with a conditional branch into the body whose + // false edge breaks out of the loop. + mlir::Block *breakBlock = + rewriter.createBlock(&condRegion, condRegion.end()); + rewriter.setInsertionPointToEnd(breakBlock); + cir::BreakOp::create(rewriter, conditionOp.getLoc()); + + rewriter.setInsertionPoint(conditionOp); + rewriter.replaceOpWithNewOp<cir::BrCondOp>(conditionOp, condVal, bodyFront, + breakBlock); + + // For a for loop, redirect the body's normal end and any continue to the + // step. The step's own yield remains the normal end of the iteration. + for (cir::YieldOp y : bodyYieldsToStep) + lowerTerminator(y, stepFront, rewriter); + for (cir::ContinueOp c : continuesToStep) + lowerTerminator(c, stepFront, rewriter); + + // Build the trivial loop body: a single cir.cleanup.scope followed by a + // yield, in a fresh block (the body region's blocks were moved out above). + mlir::Block *newBodyBlock = rewriter.createBlock(&bodyRegion); + rewriter.setInsertionPointToEnd(newBodyBlock); + auto emitYield = [](mlir::OpBuilder &b, mlir::Location l) { + cir::YieldOp::create(b, l); + }; + auto scope = cir::CleanupScopeOp::create( + rewriter, loc, cleanupKind.getValue(), emitYield, emitYield); + cir::YieldOp::create(rewriter, loc); + + // Move the assembled per-iteration blocks into the scope's body region and + // the loop's cleanup blocks into the scope's cleanup region, discarding the + // placeholder blocks the builder created. + mlir::Block *bodyPlaceholder = &scope.getBodyRegion().front(); + rewriter.inlineRegionBefore(condRegion, bodyPlaceholder); + rewriter.eraseBlock(bodyPlaceholder); + + mlir::Block *cleanupPlaceholder = &scope.getCleanupRegion().front(); + rewriter.inlineRegionBefore(cleanupRegion, cleanupPlaceholder); + rewriter.eraseBlock(cleanupPlaceholder); + + // Rebuild the loop condition region with an always-true condition. + mlir::Block *newCondBlock = rewriter.createBlock(&condRegion); + rewriter.setInsertionPointToEnd(newCondBlock); + mlir::Value trueVal = cir::ConstantOp::create( + rewriter, loc, cir::BoolAttr::get(rewriter.getContext(), true)); + cir::ConditionOp::create(rewriter, loc, trueVal); + + // For a for loop, rebuild the (now empty) step region with a trivial yield. + if (stepRegion) { + mlir::Block *newStepBlock = rewriter.createBlock(stepRegion); + rewriter.setInsertionPointToEnd(newStepBlock); + cir::YieldOp::create(rewriter, loc); + } + + // Drop the cleanup-kind attribute now that the loop's cleanup region is + // empty, so the trivial loop verifies and takes the no-cleanup flattening + // path when the greedy driver revisits it. + if (auto whileOp = mlir::dyn_cast<cir::WhileOp>(op.getOperation())) + whileOp.removeCleanupKindAttr(); + else if (auto forOp = mlir::dyn_cast<cir::ForOp>(op.getOperation())) + forOp.removeCleanupKindAttr(); + + return mlir::success(); + } + mlir::LogicalResult matchAndRewrite(cir::LoopOpInterface op, mlir::PatternRewriter &rewriter) const final { @@ -529,6 +687,17 @@ class CIRLoopOpInterfaceFlattening if (hasNestedOpsToFlatten(region)) return mlir::failure(); + // Loops with a per-iteration cleanup region need every iteration-exit edge + // routed through that cleanup, including the false-condition exit and any + // exceptions that might be thrown from the step region. Rather than trying + // to figure out all of the cleanup routing here, we sink the condition into + // the body region, hoist the step region (if any) and create a new + // cir.cleanup.scope enclosing the body region. Subsequent passes of the + // greedy driver will flatten the cir.cleanup.scope and the loop reusing + // the normal handlers. + if (op.maybeGetCleanup()) + return rewriteLoopWithCleanup(op, rewriter); + // Setup CFG blocks. mlir::Block *entry = rewriter.getInsertionBlock(); mlir::Block *exit = diff --git a/clang/test/CIR/Transforms/flatten-loop-cleanup.cir b/clang/test/CIR/Transforms/flatten-loop-cleanup.cir new file mode 100644 index 0000000000000..d362d86526ebe --- /dev/null +++ b/clang/test/CIR/Transforms/flatten-loop-cleanup.cir @@ -0,0 +1,290 @@ +// RUN: cir-opt %s -cir-flatten-cfg -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +!u8i = !cir.int<u, 8> +!s32i = !cir.int<s, 32> +!rec_S = !cir.struct<"S" padded {!u8i}> + +module { + cir.func private @ctor(!cir.ptr<!rec_S>) + cir.func private @operatorBool(!cir.ptr<!rec_S>) -> !cir.bool + cir.func private @dtor(!cir.ptr<!rec_S>) func_info<#cir.cxx_dtor<!rec_S>> + attributes {nothrow} + cir.func private @doBodyStuff() + cir.func private @doStepStuff() + cir.func private @doBreakStuff() + cir.func private @doContinueStuff() + cir.func private @doReturnStuff() + cir.func private @maythrow() + + // In actual generated CIR, a cleanup-active flag will be used to guard + // against calling the destructor if a constructor in the condition region + // throws an exception. For the sake of simplicity, that behavior is omitted + // throughout this test. This test is focused only on flattening the CFG. + + cir.func @while_cleanup() { + %s = cir.alloca "s" align(1) init : !cir.ptr<!rec_S> + cir.while { + cir.call @ctor(%s) : (!cir.ptr<!rec_S>) -> () + %c = cir.call @operatorBool(%s) : (!cir.ptr<!rec_S>) -> !cir.bool + cir.condition(%c) + } do { + cir.call @doBodyStuff() : () -> () + cir.yield + } cleanup normal { + cir.call @dtor(%s) nothrow : (!cir.ptr<!rec_S>) -> () + cir.yield + } + cir.return + } + + // CHECK-LABEL: cir.func @while_cleanup() + // CHECK: %[[SLOT:.*]] = cir.alloca "__cleanup_dest_slot" {{.*}} cleanup_dest_slot : !cir.ptr<!s32i> + // CHECK: %[[S:.*]] = cir.alloca "s" {{.*}} : !cir.ptr<!rec_S> + // CHECK: cir.br ^[[HEADER:bb[0-9]+]] + // CHECK: ^[[HEADER]]: + // CHECK: %[[TRUE:.*]] = cir.const #true + // CHECK: cir.brcond %[[TRUE]] ^[[LOOP_BODY:bb[0-9]+]], ^[[EXIT:bb[0-9]+]] + // CHECK: ^[[LOOP_BODY]]: + // CHECK: cir.call @ctor(%[[S]]) + // CHECK: %[[C:.*]] = cir.call @operatorBool(%[[S]]) + // CHECK: cir.brcond %[[C]] ^[[NORMAL:bb[0-9]+]], ^[[CONDFALSE:bb[0-9]+]] + // CHECK: ^[[NORMAL]]: + // CHECK: cir.call @doBodyStuff() + // CHECK: %[[ID0:.*]] = cir.const #cir.int<0> : !s32i + // CHECK: cir.store %[[ID0]], %[[SLOT]] + // CHECK: cir.br ^[[CLEANUP:bb[0-9]+]] + // CHECK: ^[[CONDFALSE]]: + // CHECK: %[[ID1:.*]] = cir.const #cir.int<1> : !s32i + // CHECK: cir.store %[[ID1]], %[[SLOT]] + // CHECK: cir.br ^[[CLEANUP]] + // CHECK: ^[[CLEANUP]]: + // CHECK: cir.call @dtor(%[[S]]) + // CHECK: %[[D:.*]] = cir.load %[[SLOT]] + // CHECK: cir.switch.flat %[[D]] : !s32i, ^[[UNREACHABLE:bb[0-9]+]] [ + // CHECK: 0: ^[[NORMAL_CONT:bb[0-9]+]], + // CHECK: 1: ^[[FALSE_CONT:bb[0-9]+]] + // CHECK: ] + // CHECK: ^[[NORMAL_CONT]]: + // CHECK: cir.br ^[[LATCH:bb[0-9]+]] + // CHECK: ^[[FALSE_CONT]]: + // CHECK: cir.br ^[[EXIT]] + // CHECK: ^[[UNREACHABLE]]: + // CHECK: cir.unreachable + // CHECK: ^[[LATCH]]: + // CHECK: cir.br ^[[HEADER]] + // CHECK: ^[[EXIT]]: + // CHECK: cir.return + + cir.func @for_cleanup() { + %s = cir.alloca "s" align(1) init : !cir.ptr<!rec_S> + cir.for : cond { + cir.call @ctor(%s) : (!cir.ptr<!rec_S>) -> () + %c = cir.call @operatorBool(%s) : (!cir.ptr<!rec_S>) -> !cir.bool + cir.condition(%c) + } body { + cir.call @doBodyStuff() : () -> () + cir.yield + } step { + cir.call @doStepStuff() : () -> () + cir.yield + } cleanup normal { + cir.call @dtor(%s) nothrow : (!cir.ptr<!rec_S>) -> () + cir.yield + } + cir.return + } + + // CHECK-LABEL: cir.func @for_cleanup() + // CHECK: %[[SLOT:.*]] = cir.alloca "__cleanup_dest_slot" {{.*}} cleanup_dest_slot : !cir.ptr<!s32i> + // CHECK: %[[S:.*]] = cir.alloca "s" {{.*}} : !cir.ptr<!rec_S> + // CHECK: cir.br ^[[HEADER:bb[0-9]+]] + // CHECK: ^[[HEADER]]: + // CHECK: %[[TRUE:.*]] = cir.const #true + // CHECK: cir.brcond %[[TRUE]] ^[[LOOP_BODY:bb[0-9]+]], ^[[EXIT:bb[0-9]+]] + // CHECK: ^[[LOOP_BODY]]: + // CHECK: cir.call @ctor(%[[S]]) + // CHECK: %[[C:.*]] = cir.call @operatorBool(%[[S]]) + // CHECK: cir.brcond %[[C]] ^[[BODY:bb[0-9]+]], ^[[CONDFALSE:bb[0-9]+]] + // CHECK: ^[[BODY]]: + // CHECK: cir.call @doBodyStuff() + // CHECK: cir.br ^[[STEP:bb[0-9]+]] + // CHECK: ^[[STEP]]: + // CHECK: cir.call @doStepStuff() + // CHECK: %[[ID0:.*]] = cir.const #cir.int<0> : !s32i + // CHECK: cir.store %[[ID0]], %[[SLOT]] + // CHECK: cir.br ^[[CLEANUP:bb[0-9]+]] + // CHECK: ^[[CONDFALSE]]: + // CHECK: %[[ID1:.*]] = cir.const #cir.int<1> : !s32i + // CHECK: cir.store %[[ID1]], %[[SLOT]] + // CHECK: cir.br ^[[CLEANUP]] + // CHECK: ^[[CLEANUP]]: + // CHECK: cir.call @dtor(%[[S]]) + // CHECK: %[[D:.*]] = cir.load %[[SLOT]] + // CHECK: cir.switch.flat %[[D]] : !s32i, ^[[UNREACHABLE:bb[0-9]+]] [ + // CHECK: 0: ^[[STEP_CONT:bb[0-9]+]], + // CHECK: 1: ^[[FALSE_CONT:bb[0-9]+]] + // CHECK: ] + // CHECK: ^[[STEP_CONT]]: + // CHECK: cir.br ^[[FORWARD:bb[0-9]+]] + // CHECK: ^[[FALSE_CONT]]: + // CHECK: cir.br ^[[EXIT]] + // CHECK: ^[[UNREACHABLE]]: + // CHECK: cir.unreachable + // CHECK: ^[[FORWARD]]: + // CHECK: cir.br ^[[LATCH:bb[0-9]+]] + // CHECK: ^[[LATCH]]: + // CHECK: cir.br ^[[HEADER]] + // CHECK: ^[[EXIT]]: + // CHECK: cir.return + + cir.func @while_break_continue_return(%flag : !cir.bool) -> !s32i { + %s = cir.alloca "s" align(1) init : !cir.ptr<!rec_S> + cir.while { + cir.call @ctor(%s) : (!cir.ptr<!rec_S>) -> () + %c = cir.call @operatorBool(%s) : (!cir.ptr<!rec_S>) -> !cir.bool + cir.condition(%c) + } do { + cir.call @doBodyStuff() : () -> () + cir.if %flag { + cir.call @doBreakStuff() : () -> () + cir.break + } + cir.if %flag { + cir.call @doContinueStuff() : () -> () + cir.continue + } + cir.call @doReturnStuff() : () -> () + %r = cir.const #cir.int<42> : !s32i + cir.return %r : !s32i + } cleanup normal { + cir.call @dtor(%s) nothrow : (!cir.ptr<!rec_S>) -> () + cir.yield + } + %z = cir.const #cir.int<0> : !s32i + cir.return %z : !s32i + } + + // CHECK-LABEL: cir.func @while_break_continue_return + // CHECK: %[[SLOT:.*]] = cir.alloca "__cleanup_dest_slot" {{.*}} cleanup_dest_slot : !cir.ptr<!s32i> + // CHECK: %[[S:.*]] = cir.alloca "s" {{.*}} : !cir.ptr<!rec_S> + // CHECK: cir.br ^[[HEADER:bb[0-9]+]] + // CHECK: ^[[HEADER]]: + // CHECK: %[[TRUE:.*]] = cir.const #true + // CHECK: cir.brcond %[[TRUE]] ^[[LOOP_BODY:bb[0-9]+]], ^[[EXIT:bb[0-9]+]] + // CHECK: ^[[LOOP_BODY]]: + // CHECK: cir.call @ctor(%[[S]]) + // CHECK: %[[C:.*]] = cir.call @operatorBool(%[[S]]) + // CHECK: cir.brcond %[[C]] ^[[BODY:bb[0-9]+]], ^[[CONDFALSE:bb[0-9]+]] + // CHECK: ^[[BODY]]: + // CHECK: cir.call @doBodyStuff() + // CHECK: cir.brcond %arg0 ^[[BREAK:bb[0-9]+]], ^[[CHECK_CONTINUE:bb[0-9]+]] + // CHECK: ^[[BREAK]]: + // CHECK: cir.call @doBreakStuff() + // CHECK: %[[IDB:.*]] = cir.const #cir.int<0> : !s32i + // CHECK: cir.store %[[IDB]], %[[SLOT]] + // CHECK: cir.br ^[[CLEANUP:bb[0-9]+]] + // CHECK: ^[[CHECK_CONTINUE]]: + // CHECK: cir.brcond %arg0 ^[[CONTINUE:bb[0-9]+]], ^[[RETURN:bb[0-9]+]] + // CHECK: ^[[CONTINUE]]: + // CHECK: cir.call @doContinueStuff() + // CHECK: %[[IDC:.*]] = cir.const #cir.int<1> : !s32i + // CHECK: cir.store %[[IDC]], %[[SLOT]] + // CHECK: cir.br ^[[CLEANUP]] + // CHECK: ^[[RETURN]]: + // CHECK: cir.call @doReturnStuff() + // CHECK: %[[IDR:.*]] = cir.const #cir.int<2> : !s32i + // CHECK: cir.store %[[IDR]], %[[SLOT]] + // CHECK: cir.br ^[[CLEANUP]] + // CHECK: ^[[CONDFALSE]]: + // CHECK: %[[IDF:.*]] = cir.const #cir.int<3> : !s32i + // CHECK: cir.store %[[IDF]], %[[SLOT]] + // CHECK: cir.br ^[[CLEANUP]] + // CHECK: ^[[CLEANUP]]: + // CHECK: cir.call @dtor(%[[S]]) + // CHECK: %[[D:.*]] = cir.load %[[SLOT]] + // CHECK: cir.switch.flat %[[D]] : !s32i, ^[[UNREACHABLE:bb[0-9]+]] [ + // CHECK: 0: ^[[BREAK_CONT:bb[0-9]+]], + // CHECK: 1: ^[[CONTINUE_CONT:bb[0-9]+]], + // CHECK: 2: ^[[RETURN_CONT:bb[0-9]+]], + // CHECK: 3: ^[[FALSE_CONT:bb[0-9]+]] + // CHECK: ] + // CHECK: ^[[BREAK_CONT]]: + // CHECK: cir.br ^[[EXIT]] + // CHECK: ^[[CONTINUE_CONT]]: + // CHECK: cir.br ^[[HEADER]] + // CHECK: ^[[RETURN_CONT]]: + // CHECK: %[[R:.*]] = cir.const #cir.int<42> : !s32i + // CHECK: cir.return %[[R]] : !s32i + // CHECK: ^[[FALSE_CONT]]: + // CHECK: cir.br ^[[EXIT]] + // CHECK: ^[[UNREACHABLE]]: + // CHECK: cir.unreachable + // CHECK: ^[[EXIT]]: + // CHECK: %[[Z:.*]] = cir.const #cir.int<0> : !s32i + // CHECK: cir.return %[[Z]] : !s32i + + cir.func @while_cleanup_eh() { + %s = cir.alloca "s" align(1) init : !cir.ptr<!rec_S> + cir.while { + cir.call @ctor(%s) nothrow : (!cir.ptr<!rec_S>) -> () + %c = cir.call @operatorBool(%s) : (!cir.ptr<!rec_S>) -> !cir.bool + cir.condition(%c) + } do { + cir.call @maythrow() : () -> () + cir.yield + } cleanup all { + cir.call @dtor(%s) nothrow : (!cir.ptr<!rec_S>) -> () + cir.yield + } + cir.return + } + + // CHECK-LABEL: cir.func @while_cleanup_eh() + // CHECK: %[[SLOT:.*]] = cir.alloca "__cleanup_dest_slot" {{.*}} cleanup_dest_slot : !cir.ptr<!s32i> + // CHECK: %[[S:.*]] = cir.alloca "s" {{.*}} : !cir.ptr<!rec_S> + // CHECK: cir.br ^[[HEADER:bb[0-9]+]] + // CHECK: ^[[HEADER]]: + // CHECK: %[[TRUE:.*]] = cir.const #true + // CHECK: cir.brcond %[[TRUE]] ^[[LOOP_BODY:bb[0-9]+]], ^[[EXIT:bb[0-9]+]] + // CHECK: ^[[LOOP_BODY]]: + // CHECK: cir.call @ctor(%[[S]]) + // CHECK: %[[C:.*]] = cir.try_call @operatorBool(%[[S]]) ^[[COND_TEST:bb[0-9]+]], ^[[UNWIND:bb[0-9]+]] + // CHECK: ^[[COND_TEST]]: + // CHECK: cir.brcond %[[C]] ^[[BODY:bb[0-9]+]], ^[[CONDFALSE:bb[0-9]+]] + // CHECK: ^[[BODY]]: + // CHECK: cir.try_call @maythrow() ^[[NORMAL:bb[0-9]+]], ^[[UNWIND]] + // CHECK: ^[[NORMAL]]: + // CHECK: %[[ID0:.*]] = cir.const #cir.int<0> : !s32i + // CHECK: cir.store %[[ID0]], %[[SLOT]] + // CHECK: cir.br ^[[CLEANUP:bb[0-9]+]] + // CHECK: ^[[CONDFALSE]]: + // CHECK: %[[ID1:.*]] = cir.const #cir.int<1> : !s32i + // CHECK: cir.store %[[ID1]], %[[SLOT]] + // CHECK: cir.br ^[[CLEANUP]] + // CHECK: ^[[CLEANUP]]: + // CHECK: cir.call @dtor(%[[S]]) + // CHECK: %[[D:.*]] = cir.load %[[SLOT]] + // CHECK: cir.switch.flat %[[D]] : !s32i, ^[[UNREACHABLE:bb[0-9]+]] [ + // CHECK: 0: ^[[BODY_CONT:bb[0-9]+]], + // CHECK: 1: ^[[FALSE_CONT:bb[0-9]+]] + // CHECK: ] + // CHECK: ^[[BODY_CONT]]: + // CHECK: cir.br ^[[LATCH:bb[0-9]+]] + // CHECK: ^[[FALSE_CONT]]: + // CHECK: cir.br ^[[EXIT]] + // CHECK: ^[[UNREACHABLE]]: + // CHECK: cir.unreachable + // CHECK: ^[[UNWIND]]: + // CHECK: %[[TOK:.*]] = cir.eh.initiate cleanup : !cir.eh_token + // CHECK: cir.br ^[[EHCLEANUP:bb[0-9]+]](%[[TOK]] : !cir.eh_token) + // CHECK: ^[[EHCLEANUP]](%[[TOK2:.*]]: !cir.eh_token): + // CHECK: %[[CT:.*]] = cir.begin_cleanup %[[TOK2]] : !cir.eh_token -> !cir.cleanup_token + // CHECK: cir.call @dtor(%[[S]]) + // CHECK: cir.end_cleanup %[[CT]] : !cir.cleanup_token + // CHECK: cir.resume %[[TOK2]] : !cir.eh_token + // CHECK: ^[[LATCH]]: + // CHECK: cir.br ^[[HEADER]] + // CHECK: ^[[EXIT]]: + // CHECK: cir.return +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
