From 32f7ce9d08c4e0154a181e3cf975272ca4a5b9f9 Mon Sep 17 00:00:00 2001 From: gitoleg Date: Wed, 9 Aug 2023 22:24:19 +0300 Subject: [PATCH] [CIR][Lowering] Fixed break/continue lowering for loops (#211) This PR fixes lowering for `break/continue` in loops. The idea is to replace `cir.yield break` and `cir.yield continue` with the branch operations to the corresponding blocks. Note, that we need to ignore nesting loops and don't touch `break` in switch operations. Also, `yield` from `if` need to be considered only when it's not the loop `yield` and `continue` in switch is ignored since it's processed in the loops lowering. Fixes #160 --- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 70 +++- clang/test/CIR/Lowering/loops-with-break.cir | 322 ++++++++++++++++++ .../test/CIR/Lowering/loops-with-continue.cir | 318 +++++++++++++++++ 3 files changed, 702 insertions(+), 8 deletions(-) create mode 100644 clang/test/CIR/Lowering/loops-with-break.cir create mode 100644 clang/test/CIR/Lowering/loops-with-continue.cir diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index a9708f526a9c..33e707d78909 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -240,6 +240,47 @@ class CIRLoopOpLowering : public mlir::OpConversionPattern { return mlir::success(); } + void makeYieldIf(mlir::cir::YieldOpKind kind, mlir::cir::YieldOp &op, + mlir::Block *to, + mlir::ConversionPatternRewriter &rewriter) const { + if (op.getKind() == kind) { + rewriter.setInsertionPoint(op); + rewriter.replaceOpWithNewOp(op, op.getArgs(), to); + } + } + + void + lowerNestedBreakContinue(mlir::Region &loopBody, mlir::Block *exitBlock, + mlir::Block *continueBlock, + mlir::ConversionPatternRewriter &rewriter) const { + + auto processBreak = [&](mlir::Operation *op) { + if (isa( + *op)) // don't process breaks in nested loops and switches + return mlir::WalkResult::skip(); + + if (auto yield = dyn_cast(*op)) + makeYieldIf(mlir::cir::YieldOpKind::Break, yield, exitBlock, rewriter); + + return mlir::WalkResult::advance(); + }; + + auto processContinue = [&](mlir::Operation *op) { + if (isa( + *op)) // don't process continues in nested loops + return mlir::WalkResult::skip(); + + if (auto yield = dyn_cast(*op)) + makeYieldIf(mlir::cir::YieldOpKind::Continue, yield, continueBlock, + rewriter); + + return mlir::WalkResult::advance(); + }; + + loopBody.walk(processBreak); + loopBody.walk(processContinue); + } + mlir::LogicalResult matchAndRewrite(mlir::cir::LoopOp loopOp, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const override { @@ -267,6 +308,9 @@ class CIRLoopOpLowering : public mlir::OpConversionPattern { auto &stepFrontBlock = stepRegion.front(); auto stepYield = dyn_cast(stepRegion.back().getTerminator()); + auto &stepBlock = (kind == LoopKind::For ? stepFrontBlock : condFrontBlock); + + lowerNestedBreakContinue(bodyRegion, continueBlock, &stepBlock, rewriter); // Move loop op region contents to current CFG. rewriter.inlineRegionBefore(condRegion, continueBlock); @@ -289,8 +333,7 @@ class CIRLoopOpLowering : public mlir::OpConversionPattern { // Branch from body to condition or to step on for-loop cases. rewriter.setInsertionPoint(bodyYield); - auto &bodyExit = (kind == LoopKind::For ? stepFrontBlock : condFrontBlock); - rewriter.replaceOpWithNewOp(bodyYield, &bodyExit); + rewriter.replaceOpWithNewOp(bodyYield, &stepBlock); // Is a for loop: branch from step to condition. if (kind == LoopKind::For) { @@ -488,6 +531,11 @@ class CIRCastOpLowering : public mlir::OpConversionPattern { } }; +static bool isLoopYield(mlir::cir::YieldOp &op) { + return op.getKind() == mlir::cir::YieldOpKind::Break || + op.getKind() == mlir::cir::YieldOpKind::Continue; +} + class CIRIfLowering : public mlir::OpConversionPattern { public: using mlir::OpConversionPattern::OpConversionPattern; @@ -516,8 +564,10 @@ class CIRIfLowering : public mlir::OpConversionPattern { rewriter.setInsertionPointToEnd(thenAfterBody); if (auto thenYieldOp = dyn_cast(thenAfterBody->getTerminator())) { - rewriter.replaceOpWithNewOp( - thenYieldOp, thenYieldOp.getArgs(), continueBlock); + if (!isLoopYield(thenYieldOp)) // lowering of parent loop yields is + // deferred to loop lowering + rewriter.replaceOpWithNewOp( + thenYieldOp, thenYieldOp.getArgs(), continueBlock); } else if (!dyn_cast(thenAfterBody->getTerminator())) { llvm_unreachable("what are we terminating with?"); } @@ -545,8 +595,10 @@ class CIRIfLowering : public mlir::OpConversionPattern { rewriter.setInsertionPointToEnd(elseAfterBody); if (auto elseYieldOp = dyn_cast(elseAfterBody->getTerminator())) { - rewriter.replaceOpWithNewOp( - elseYieldOp, elseYieldOp.getArgs(), continueBlock); + if (!isLoopYield(elseYieldOp)) // lowering of parent loop yields is + // deferred to loop lowering + rewriter.replaceOpWithNewOp( + elseYieldOp, elseYieldOp.getArgs(), continueBlock); } else if (!dyn_cast( elseAfterBody->getTerminator())) { llvm_unreachable("what are we terminating with?"); @@ -1109,6 +1161,9 @@ class CIRSwitchOpLowering case mlir::cir::YieldOpKind::Break: rewriteYieldOp(rewriter, yieldOp, exitBlock); break; + case mlir::cir::YieldOpKind::Continue: // Continue is handled only in + // loop lowering + break; default: return op->emitError("invalid yield kind in case statement"); } @@ -1692,8 +1747,7 @@ void populateCIRToLLVMConversionPatterns(mlir::RewritePatternSet &patterns, CIRVAStartLowering, CIRVAEndLowering, CIRVACopyLowering, CIRVAArgLowering, CIRBrOpLowering, CIRTernaryOpLowering, CIRStructElementAddrOpLowering, CIRSwitchOpLowering, - CIRPtrDiffOpLowering>( - converter, patterns.getContext()); + CIRPtrDiffOpLowering>(converter, patterns.getContext()); } namespace { diff --git a/clang/test/CIR/Lowering/loops-with-break.cir b/clang/test/CIR/Lowering/loops-with-break.cir new file mode 100644 index 000000000000..f22865ebcc78 --- /dev/null +++ b/clang/test/CIR/Lowering/loops-with-break.cir @@ -0,0 +1,322 @@ +// RUN: cir-opt %s -cir-to-llvm -reconcile-unrealized-casts -o %t.mlir +// RUN: FileCheck --input-file=%t.mlir %s + +!s32i = !cir.int +module { + cir.func @testFor() { + cir.scope { + %0 = cir.alloca !s32i, cir.ptr , ["i", init] {alignment = 4 : i64} + %1 = cir.const(#cir.int<1> : !s32i) : !s32i + cir.store %1, %0 : !s32i, cir.ptr + cir.loop for(cond : { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.const(#cir.int<10> : !s32i) : !s32i + %4 = cir.cmp(lt, %2, %3) : !s32i, !s32i + %5 = cir.cast(int_to_bool, %4 : !s32i), !cir.bool + cir.brcond %5 ^bb1, ^bb2 + ^bb1: // pred: ^bb0 + cir.yield continue + ^bb2: // pred: ^bb0 + cir.yield + }, step : { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.unary(inc, %2) : !s32i, !s32i + cir.store %3, %0 : !s32i, cir.ptr + cir.yield + }) { + cir.scope { + cir.scope { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.const(#cir.int<5> : !s32i) : !s32i + %4 = cir.cmp(eq, %2, %3) : !s32i, !s32i + %5 = cir.cast(int_to_bool, %4 : !s32i), !cir.bool + cir.if %5 { + cir.yield break + } + } + } + cir.yield + } + } + cir.return + } + + // CHECK: llvm.func @testFor() + // [...] + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#COND]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preBREAK0:]], ^bb[[#preEXIT0:]] + // CHECK: ^bb[[#preBREAK0]]: + // CHECK: llvm.br ^bb[[#preBREAK1:]] + // CHECK: ^bb[[#preEXIT0]]: + // CHECK: llvm.br ^bb[[#EXIT:]] + // CHECK: ^bb[[#preBREAK1]]: + // CHECK: llvm.br ^bb[[#preBREAK2:]] + // CHECK: ^bb[[#preBREAK2]]: + // CHECK: llvm.br ^bb[[#BREAK:]] + // CHECK: ^bb[[#BREAK]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preEXIT1:]], ^bb[[#preBODY0:]] + // CHECK: ^bb[[#preEXIT1]]: + // CHECK: llvm.br ^bb[[#EXIT:]] + // CHECK: ^bb[[#preBODY0]]: + // CHECK: llvm.br ^bb[[#preBODY1:]] + // CHECK: ^bb[[#preBODY1]]: + // CHECK: llvm.br ^bb[[#BODY:]] + // CHECK: ^bb[[#BODY]]: + // CHECK: llvm.br ^bb[[#STEP:]] + // CHECK: ^bb[[#STEP]]: + // [...] + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#EXIT]]: + // [...] + // CHECK: } + + cir.func @testForNested() { + cir.scope { + %0 = cir.alloca !s32i, cir.ptr , ["i", init] {alignment = 4 : i64} + %1 = cir.const(#cir.int<1> : !s32i) : !s32i + cir.store %1, %0 : !s32i, cir.ptr + cir.loop for(cond : { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.const(#cir.int<10> : !s32i) : !s32i + %4 = cir.cmp(lt, %2, %3) : !s32i, !s32i + %5 = cir.cast(int_to_bool, %4 : !s32i), !cir.bool + cir.brcond %5 ^bb1, ^bb2 + ^bb1: // pred: ^bb0 + cir.yield continue + ^bb2: // pred: ^bb0 + cir.yield + }, step : { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.unary(inc, %2) : !s32i, !s32i + cir.store %3, %0 : !s32i, cir.ptr + cir.yield + }) { + cir.scope { + cir.scope { + %2 = cir.alloca !s32i, cir.ptr , ["j", init] {alignment = 4 : i64} + %3 = cir.const(#cir.int<1> : !s32i) : !s32i + cir.store %3, %2 : !s32i, cir.ptr + cir.loop for(cond : { + %4 = cir.load %2 : cir.ptr , !s32i + %5 = cir.const(#cir.int<10> : !s32i) : !s32i + %6 = cir.cmp(lt, %4, %5) : !s32i, !s32i + %7 = cir.cast(int_to_bool, %6 : !s32i), !cir.bool + cir.brcond %7 ^bb1, ^bb2 + ^bb1: // pred: ^bb0 + cir.yield continue + ^bb2: // pred: ^bb0 + cir.yield + }, step : { + %4 = cir.load %2 : cir.ptr , !s32i + %5 = cir.unary(inc, %4) : !s32i, !s32i + cir.store %5, %2 : !s32i, cir.ptr + cir.yield + }) { + cir.scope { + cir.scope { + %4 = cir.load %2 : cir.ptr , !s32i + %5 = cir.const(#cir.int<5> : !s32i) : !s32i + %6 = cir.cmp(eq, %4, %5) : !s32i, !s32i + %7 = cir.cast(int_to_bool, %6 : !s32i), !cir.bool + cir.if %7 { + cir.yield break + } + } + } + cir.yield + } + } + } + cir.yield + } + } + cir.return + } + + // CHECK: llvm.func @testForNested() + // [...] + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#COND]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preNESTED0:]], ^bb[[#preEXIT0:]] + // CHECK: ^bb[[#preNESTED0]]: + // CHECK: llvm.br ^bb[[#preNESTED1:]] + // CHECK: ^bb[[#preEXIT0]]: + // CHECK: llvm.br ^bb[[#EXIT:]] + // CHECK: ^bb[[#preNESTED1]]: + // CHECK: llvm.br ^bb[[#preNESTED2:]] + // CHECK: ^bb[[#preNESTED2]]: + // CHECK: llvm.br ^bb[[#NESTED:]] + // CHECK: ^bb[[#NESTED]]: + // [...] + // CHECK: llvm.br ^bb[[#COND_NESTED:]] + // CHECK: ^bb[[#COND_NESTED]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preBREAK0:]], ^bb[[#preEXIT1:]] + // CHECK: ^bb[[#preBREAK0]]: + // CHECK: llvm.br ^bb[[#preBREAK1:]] + // CHECK: ^bb[[#preEXIT1]]: + // CHECK: llvm.br ^bb[[#EXIT_NESTED:]] + // CHECK: ^bb[[#preBREAK1]]: + // CHECK: llvm.br ^bb[[#preBREAK2:]] + // CHECK: ^bb[[#preBREAK2]]: + // CHECK: llvm.br ^bb[[#BREAK:]] + // CHECK: ^bb[[#BREAK]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preEXIT2:]], ^bb[[#preBODY0:]] + // CHECK: ^bb[[#preEXIT2]]: + // CHECK: llvm.br ^bb[[#EXIT_NESTED:]] + // CHECK: ^bb[[#preBODY0]]: + // CHECK: llvm.br ^bb[[#preBODY1:]] + // CHECK: ^bb[[#preBODY1]]: + // CHECK: llvm.br ^bb[[#BODY_NESTED:]] + // CHECK: ^bb[[#BODY_NESTED]]: + // CHECK: llvm.br ^bb[[#STEP_NESTED:]] + // CHECK: ^bb[[#STEP_NESTED]]: + // [...] + // CHECK: llvm.br ^bb[[#COND_NESTED:]] + // CHECK: ^bb[[#EXIT_NESTED]]: + // [...] + // CHECK: llvm.br ^bb[[#BODY:]] + // CHECK: ^bb[[#BODY]]: + // CHECK: llvm.br ^bb[[#STEP:]] + // CHECK: ^bb[[#STEP]]: + // [...] + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#EXIT]]: + // [...] + // CHECK: } + + cir.func @testWhile() { + %0 = cir.alloca !s32i, cir.ptr , ["i", init] {alignment = 4 : i64} + %1 = cir.const(#cir.int<0> : !s32i) : !s32i + cir.store %1, %0 : !s32i, cir.ptr + cir.scope { + cir.loop while(cond : { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.const(#cir.int<10> : !s32i) : !s32i + %4 = cir.cmp(lt, %2, %3) : !s32i, !s32i + %5 = cir.cast(int_to_bool, %4 : !s32i), !cir.bool + cir.brcond %5 ^bb1, ^bb2 + ^bb1: // pred: ^bb0 + cir.yield continue + ^bb2: // pred: ^bb0 + cir.yield + }, step : { + cir.yield + }) { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.unary(inc, %2) : !s32i, !s32i + cir.store %3, %0 : !s32i, cir.ptr + cir.scope { + %4 = cir.load %0 : cir.ptr , !s32i + %5 = cir.const(#cir.int<5> : !s32i) : !s32i + %6 = cir.cmp(eq, %4, %5) : !s32i, !s32i + %7 = cir.cast(int_to_bool, %6 : !s32i), !cir.bool + cir.if %7 { + cir.yield break + } + } + cir.yield + } + } + cir.return + } + + + // CHECK: llvm.func @testWhile() + // [...] + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#COND]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preBODY:]], ^bb[[#preEXIT0:]] + // CHECK: ^bb[[#preBODY]]: + // CHECK: llvm.br ^bb[[#BODY:]] + // CHECK: ^bb[[#preEXIT0]]: + // CHECK: llvm.br ^bb[[#EXIT:]] + // CHECK: ^bb[[#BODY]]: + // [...] + // CHECK: llvm.br ^bb[[#BREAK:]] + // CHECK: ^bb[[#BREAK]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preEXIT1:]], ^bb[[#preCOND0:]] + // CHECK: ^bb[[#preEXIT1]]: + // CHECK: llvm.br ^bb[[#preEXIT2:]] + // CHECK: ^bb[[#preCOND0]]: + // CHECK: llvm.br ^bb[[#preCOND1:]] + // CHECK: ^bb[[#preCOND1]]: + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#preEXIT2]]: + // CHECK: llvm.br ^bb[[#EXIT:]] + // CHECK: ^bb[[#EXIT]]: + // [...] + // CHECK: } + +cir.func @testDoWhile() { + %0 = cir.alloca !s32i, cir.ptr , ["i", init] {alignment = 4 : i64} + %1 = cir.const(#cir.int<0> : !s32i) : !s32i + cir.store %1, %0 : !s32i, cir.ptr + cir.scope { + cir.loop dowhile(cond : { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.const(#cir.int<10> : !s32i) : !s32i + %4 = cir.cmp(lt, %2, %3) : !s32i, !s32i + %5 = cir.cast(int_to_bool, %4 : !s32i), !cir.bool + cir.brcond %5 ^bb1, ^bb2 + ^bb1: // pred: ^bb0 + cir.yield continue + ^bb2: // pred: ^bb0 + cir.yield + }, step : { + cir.yield + }) { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.unary(inc, %2) : !s32i, !s32i + cir.store %3, %0 : !s32i, cir.ptr + cir.scope { + %4 = cir.load %0 : cir.ptr , !s32i + %5 = cir.const(#cir.int<5> : !s32i) : !s32i + %6 = cir.cmp(eq, %4, %5) : !s32i, !s32i + %7 = cir.cast(int_to_bool, %6 : !s32i), !cir.bool + cir.if %7 { + cir.yield break + } + } + cir.yield + } + } + cir.return + } + + // CHECK: llvm.func @testDoWhile() + // [...] + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#COND]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preBODY:]], ^bb[[#preEXIT0:]] + // CHECK: ^bb[[#preBODY]]: + // CHECK: llvm.br ^bb[[#BODY:]] + // CHECK: ^bb[[#preEXIT0]]: + // CHECK: llvm.br ^bb[[#EXIT:]] + // CHECK: ^bb[[#BODY]]: + // [...] + // CHECK: llvm.br ^bb[[#BREAK:]] + // CHECK: ^bb[[#BREAK]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preEXIT1:]], ^bb[[#preCOND0:]] + // CHECK: ^bb[[#preEXIT1]]: + // CHECK: llvm.br ^bb[[#preEXIT2:]] + // CHECK: ^bb[[#preCOND0]]: + // CHECK: llvm.br ^bb[[#preCOND1:]] + // CHECK: ^bb[[#preCOND1]]: + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#preEXIT2]]: + // CHECK: llvm.br ^bb[[#EXIT:]] + // CHECK: ^bb[[#EXIT]]: + // [...] + // CHECK: } + +} \ No newline at end of file diff --git a/clang/test/CIR/Lowering/loops-with-continue.cir b/clang/test/CIR/Lowering/loops-with-continue.cir new file mode 100644 index 000000000000..c0f2c2658c2c --- /dev/null +++ b/clang/test/CIR/Lowering/loops-with-continue.cir @@ -0,0 +1,318 @@ +// RUN: cir-opt %s -cir-to-llvm -reconcile-unrealized-casts -o %t.mlir +// RUN: FileCheck --input-file=%t.mlir %s + +!s32i = !cir.int +module { + cir.func @testFor() { + cir.scope { + %0 = cir.alloca !s32i, cir.ptr , ["i", init] {alignment = 4 : i64} + %1 = cir.const(#cir.int<1> : !s32i) : !s32i + cir.store %1, %0 : !s32i, cir.ptr + cir.loop for(cond : { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.const(#cir.int<10> : !s32i) : !s32i + %4 = cir.cmp(lt, %2, %3) : !s32i, !s32i + %5 = cir.cast(int_to_bool, %4 : !s32i), !cir.bool + cir.brcond %5 ^bb1, ^bb2 + ^bb1: // pred: ^bb0 + cir.yield continue + ^bb2: // pred: ^bb0 + cir.yield + }, step : { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.unary(inc, %2) : !s32i, !s32i + cir.store %3, %0 : !s32i, cir.ptr + cir.yield + }) { + cir.scope { + cir.scope { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.const(#cir.int<5> : !s32i) : !s32i + %4 = cir.cmp(eq, %2, %3) : !s32i, !s32i + %5 = cir.cast(int_to_bool, %4 : !s32i), !cir.bool + cir.if %5 { + cir.yield continue + } + } + } + cir.yield + } + } + cir.return + } + + // CHECK: llvm.func @testFor() + // [...] + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#COND]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preCONTINUE0:]], ^bb[[#preEXIT0:]] + // CHECK: ^bb[[#preCONTINUE0]]: + // CHECK: llvm.br ^bb[[#preCONTINUE1:]] + // CHECK: ^bb[[#preEXIT0]]: + // CHECK: llvm.br ^bb[[#EXIT:]] + // CHECK: ^bb[[#preCONTINUE1]]: + // CHECK: llvm.br ^bb[[#preCONTINUE2:]] + // CHECK: ^bb[[#preCONTINUE2]]: + // CHECK: llvm.br ^bb[[#CONTINUE:]] + // CHECK: ^bb[[#CONTINUE]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preSTEP:]], ^bb[[#preBODY0:]] + // CHECK: ^bb[[#preSTEP]]: + // CHECK: llvm.br ^bb[[#STEP:]] + // CHECK: ^bb[[#preBODY0]]: + // CHECK: llvm.br ^bb[[#preBODY1:]] + // CHECK: ^bb[[#preBODY1]]: + // CHECK: llvm.br ^bb[[#BODY:]] + // CHECK: ^bb[[#BODY]]: + // CHECK: llvm.br ^bb[[#STEP:]] + // CHECK: ^bb[[#STEP]]: + // [...] + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#EXIT]]: + // [...] + // CHECK: } + + + cir.func @testForNested() { + cir.scope { + %0 = cir.alloca !s32i, cir.ptr , ["i", init] {alignment = 4 : i64} + %1 = cir.const(#cir.int<1> : !s32i) : !s32i + cir.store %1, %0 : !s32i, cir.ptr + cir.loop for(cond : { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.const(#cir.int<10> : !s32i) : !s32i + %4 = cir.cmp(lt, %2, %3) : !s32i, !s32i + %5 = cir.cast(int_to_bool, %4 : !s32i), !cir.bool + cir.brcond %5 ^bb1, ^bb2 + ^bb1: // pred: ^bb0 + cir.yield continue + ^bb2: // pred: ^bb0 + cir.yield + }, step : { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.unary(inc, %2) : !s32i, !s32i + cir.store %3, %0 : !s32i, cir.ptr + cir.yield + }) { + cir.scope { + cir.scope { + %2 = cir.alloca !s32i, cir.ptr , ["j", init] {alignment = 4 : i64} + %3 = cir.const(#cir.int<1> : !s32i) : !s32i + cir.store %3, %2 : !s32i, cir.ptr + cir.loop for(cond : { + %4 = cir.load %2 : cir.ptr , !s32i + %5 = cir.const(#cir.int<10> : !s32i) : !s32i + %6 = cir.cmp(lt, %4, %5) : !s32i, !s32i + %7 = cir.cast(int_to_bool, %6 : !s32i), !cir.bool + cir.brcond %7 ^bb1, ^bb2 + ^bb1: // pred: ^bb0 + cir.yield continue + ^bb2: // pred: ^bb0 + cir.yield + }, step : { + %4 = cir.load %2 : cir.ptr , !s32i + %5 = cir.unary(inc, %4) : !s32i, !s32i + cir.store %5, %2 : !s32i, cir.ptr + cir.yield + }) { + cir.scope { + cir.scope { + %4 = cir.load %2 : cir.ptr , !s32i + %5 = cir.const(#cir.int<5> : !s32i) : !s32i + %6 = cir.cmp(eq, %4, %5) : !s32i, !s32i + %7 = cir.cast(int_to_bool, %6 : !s32i), !cir.bool + cir.if %7 { + cir.yield continue + } + } + } + cir.yield + } + } + } + cir.yield + } + } + cir.return + } + + // CHECK: llvm.func @testForNested() + // [...] + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#COND]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preNESTED0:]], ^bb[[#preEXIT0:]] + // CHECK: ^bb[[#preNESTED0]]: + // CHECK: llvm.br ^bb[[#preNESTED1:]] + // CHECK: ^bb[[#preEXIT0]]: + // CHECK: llvm.br ^bb[[#EXIT:]] + // CHECK: ^bb[[#preNESTED1]]: + // CHECK: llvm.br ^bb[[#preNESTED2:]] + // CHECK: ^bb[[#preNESTED2]]: + // CHECK: llvm.br ^bb[[#NESTED:]] + // CHECK: ^bb[[#NESTED]]: + // [...] + // CHECK: llvm.br ^bb[[#COND_NESTED:]] + // CHECK: ^bb[[#COND_NESTED]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preCONTINUE0:]], ^bb[[#preEXIT1:]] + // CHECK: ^bb[[#preCONTINUE0]]: + // CHECK: llvm.br ^bb[[#preCONTINUE1:]] + // CHECK: ^bb[[#preEXIT1]]: + // CHECK: llvm.br ^bb[[#EXIT_NESTED:]] + // CHECK: ^bb[[#preCONTINUE1]]: + // CHECK: llvm.br ^bb[[#preCONTINUE2:]] + // CHECK: ^bb[[#preCONTINUE2]]: + // CHECK: llvm.br ^bb[[#CONTINUE:]] + // CHECK: ^bb[[#CONTINUE]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preSTEP0:]], ^bb[[#preBODY0:]] + // CHECK: ^bb[[#preSTEP0]]: + // CHECK: llvm.br ^bb[[#STEP_NESTED:]] + // CHECK: ^bb[[#preBODY0]]: + // CHECK: llvm.br ^bb[[#preBODY1:]] + // CHECK: ^bb[[#preBODY1]]: + // CHECK: llvm.br ^bb[[#BODY_NESTED:]] + // CHECK: ^bb[[#BODY_NESTED]]: + // CHECK: llvm.br ^bb[[#STEP_NESTED:]] + // CHECK: ^bb[[#STEP_NESTED]]: + // [...] + // CHECK: llvm.br ^bb[[#COND_NESTED:]] + // CHECK: ^bb[[#EXIT_NESTED]]: + // CHECK: llvm.br ^bb[[#BODY:]] + // CHECK: ^bb[[#BODY]]: + // CHECK: llvm.br ^bb[[#STEP:]] + // CHECK: ^bb[[#STEP]]: + // [...] + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#EXIT]]: + // [...] + // CHECK: } + +cir.func @testWhile() { + %0 = cir.alloca !s32i, cir.ptr , ["i", init] {alignment = 4 : i64} + %1 = cir.const(#cir.int<0> : !s32i) : !s32i + cir.store %1, %0 : !s32i, cir.ptr + cir.scope { + cir.loop while(cond : { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.const(#cir.int<10> : !s32i) : !s32i + %4 = cir.cmp(lt, %2, %3) : !s32i, !s32i + %5 = cir.cast(int_to_bool, %4 : !s32i), !cir.bool + cir.brcond %5 ^bb1, ^bb2 + ^bb1: // pred: ^bb0 + cir.yield continue + ^bb2: // pred: ^bb0 + cir.yield + }, step : { + cir.yield + }) { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.unary(inc, %2) : !s32i, !s32i + cir.store %3, %0 : !s32i, cir.ptr + cir.scope { + %4 = cir.load %0 : cir.ptr , !s32i + %5 = cir.const(#cir.int<5> : !s32i) : !s32i + %6 = cir.cmp(eq, %4, %5) : !s32i, !s32i + %7 = cir.cast(int_to_bool, %6 : !s32i), !cir.bool + cir.if %7 { + cir.yield continue + } + } + cir.yield + } + } + cir.return + } + + // CHECK: llvm.func @testWhile() + // [...] + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#COND]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preBODY:]], ^bb[[#preEXIT0:]] + // CHECK: ^bb[[#preBODY]]: + // CHECK: llvm.br ^bb[[#BODY:]] + // CHECK: ^bb[[#preEXIT0]]: + // CHECK: llvm.br ^bb[[#EXIT:]] + // CHECK: ^bb[[#BODY]]: + // [...] + // CHECK: llvm.br ^bb[[#CONTINUE:]] + // CHECK: ^bb[[#CONTINUE]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preCOND0:]], ^bb[[#preCOND1:]] + // CHECK: ^bb[[#preCOND0]]: + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#preCOND1]]: + // CHECK: llvm.br ^bb[[#preCOND2:]] + // CHECK: ^bb[[#preCOND2]]: + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#EXIT]]: + // [...] + // CHECK: } + + cir.func @testDoWhile() { + %0 = cir.alloca !s32i, cir.ptr , ["i", init] {alignment = 4 : i64} + %1 = cir.const(#cir.int<0> : !s32i) : !s32i + cir.store %1, %0 : !s32i, cir.ptr + cir.scope { + cir.loop dowhile(cond : { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.const(#cir.int<10> : !s32i) : !s32i + %4 = cir.cmp(lt, %2, %3) : !s32i, !s32i + %5 = cir.cast(int_to_bool, %4 : !s32i), !cir.bool + cir.brcond %5 ^bb1, ^bb2 + ^bb1: // pred: ^bb0 + cir.yield continue + ^bb2: // pred: ^bb0 + cir.yield + }, step : { + cir.yield + }) { + %2 = cir.load %0 : cir.ptr , !s32i + %3 = cir.unary(inc, %2) : !s32i, !s32i + cir.store %3, %0 : !s32i, cir.ptr + cir.scope { + %4 = cir.load %0 : cir.ptr , !s32i + %5 = cir.const(#cir.int<5> : !s32i) : !s32i + %6 = cir.cmp(eq, %4, %5) : !s32i, !s32i + %7 = cir.cast(int_to_bool, %6 : !s32i), !cir.bool + cir.if %7 { + cir.yield continue + } + } + cir.yield + } + } + cir.return + } + + + // CHECK: llvm.func @testDoWhile() + // [...] + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#COND]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preBODY:]], ^bb[[#preEXIT0:]] + // CHECK: ^bb[[#preBODY]]: + // CHECK: llvm.br ^bb[[#BODY:]] + // CHECK: ^bb[[#preEXIT0]]: + // CHECK: llvm.br ^bb[[#EXIT:]] + // CHECK: ^bb[[#BODY]]: + // [...] + // CHECK: llvm.br ^bb[[#CONTINUE:]] + // CHECK: ^bb[[#CONTINUE]]: + // [...] + // CHECK: llvm.cond_br %{{.+}}, ^bb[[#preCOND0:]], ^bb[[#preCOND1:]] + // CHECK: ^bb[[#preCOND0]]: + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#preCOND1]]: + // CHECK: llvm.br ^bb[[#preCOND2:]] + // CHECK: ^bb[[#preCOND2]]: + // CHECK: llvm.br ^bb[[#COND:]] + // CHECK: ^bb[[#EXIT]]: + // [...] + // CHECK: } + +} \ No newline at end of file