From 185619ee1a14daa9c146d709e8cc6228bbd3e92f Mon Sep 17 00:00:00 2001 From: gitoleg Date: Wed, 8 May 2024 21:20:30 +0300 Subject: [PATCH] [CIR][CodeGen] Goto pass (#562) - Add new operations: `GotoOp` and `LabelOp` and inserts them in the codegen - Adds a pass that replaces `goto` operations with branches to the corresponded blocks (and erases `LabelOp` from CIR) - Update verifiers and tests --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 42 ++++ clang/include/clang/CIR/Dialect/Passes.h | 1 + clang/include/clang/CIR/Dialect/Passes.td | 10 + clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 16 -- clang/lib/CIR/CodeGen/CIRGenFunction.h | 7 - clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 30 +-- clang/lib/CIR/CodeGen/CIRPasses.cpp | 2 +- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 31 +++ .../lib/CIR/Dialect/Transforms/CMakeLists.txt | 1 + .../lib/CIR/Dialect/Transforms/FlattenCFG.cpp | 8 +- .../lib/CIR/Dialect/Transforms/GotoSolver.cpp | 54 ++++ .../CIR/Dialect/Transforms/MergeCleanups.cpp | 3 + clang/test/CIR/CodeGen/goto.cpp | 232 +++++++++++++++++- clang/test/CIR/IR/invalid.cir | 11 + clang/test/CIR/Lowering/goto.cir | 79 +++--- clang/test/CIR/Lowering/region-simplify.cir | 38 +++ 16 files changed, 488 insertions(+), 77 deletions(-) create mode 100644 clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp create mode 100644 clang/test/CIR/Lowering/region-simplify.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 30f3182b949e..18c26a82b289 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -3751,6 +3751,48 @@ def SwitchFlatOp : CIR_Op<"switch.flat", [AttrSizedOperandSegments, Terminator]> ]; } +//===----------------------------------------------------------------------===// +// GotoOp +//===----------------------------------------------------------------------===// + +def GotoOp : CIR_Op<"goto", [Terminator]> { + let description = [{ Transfers control to the specified label. + + Example: + ```C++ + void foo() { + goto exit; + + exit: + return; + } + ``` + + ```mlir + cir.func @foo() { + cir.goto "exit" + ^bb1: + cir.label "exit" + cir.return + } + ``` + }]; + let arguments = (ins StrAttr:$label); + let assemblyFormat = [{ $label attr-dict }]; +} + +//===----------------------------------------------------------------------===// +// LabelOp +//===----------------------------------------------------------------------===// + +// The LabelOp has AlwaysSpeculatable trait in order to not to be swept by canonicalizer +def LabelOp : CIR_Op<"label", [AlwaysSpeculatable]> { + let description = [{ An identifier which may be referred by cir.goto operation }]; + let arguments = (ins StrAttr:$label); + let assemblyFormat = [{ $label attr-dict }]; + let hasVerifier = 1; +} + //===----------------------------------------------------------------------===// // Atomic operations //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/Passes.h b/clang/include/clang/CIR/Dialect/Passes.h index 2f713240944f..30ec06114476 100644 --- a/clang/include/clang/CIR/Dialect/Passes.h +++ b/clang/include/clang/CIR/Dialect/Passes.h @@ -35,6 +35,7 @@ std::unique_ptr createIdiomRecognizerPass(clang::ASTContext *astCtx); std::unique_ptr createLibOptPass(); std::unique_ptr createLibOptPass(clang::ASTContext *astCtx); std::unique_ptr createFlattenCFGPass(); +std::unique_ptr createGotoSolverPass(); void populateCIRPreLoweringPasses(mlir::OpPassManager &pm); diff --git a/clang/include/clang/CIR/Dialect/Passes.td b/clang/include/clang/CIR/Dialect/Passes.td index e63b97469980..1253bccf77b8 100644 --- a/clang/include/clang/CIR/Dialect/Passes.td +++ b/clang/include/clang/CIR/Dialect/Passes.td @@ -89,6 +89,16 @@ def FlattenCFG : Pass<"cir-flatten-cfg"> { let dependentDialects = ["cir::CIRDialect"]; } +def GotoSolver : Pass<"cir-goto-solver"> { + let summary = "Replaces goto operatations with branches"; + let description = [{ + This pass transforms CIR and replaces goto-s with branch + operations to the proper blocks. + }]; + let constructor = "mlir::createGotoSolverPass()"; + let dependentDialects = ["cir::CIRDialect"]; +} + def IdiomRecognizer : Pass<"cir-idiom-recognizer"> { let summary = "Raise calls to C/C++ libraries to CIR operations"; let description = [{ diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 8256bd6db71c..1f95be2f542d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -323,22 +323,6 @@ void CIRGenFunction::LexicalScope::cleanup() { auto &builder = CGF.builder; auto *localScope = CGF.currLexScope; - // Handle pending gotos and the solved labels in this scope. - while (!localScope->PendingGotos.empty()) { - auto gotoInfo = localScope->PendingGotos.back(); - // FIXME: Currently only support resolving goto labels inside the - // same lexical ecope. - assert(localScope->SolvedLabels.count(gotoInfo.second) && - "goto across scopes not yet supported"); - - // The goto in this lexical context actually maps to a basic - // block. - auto g = cast(gotoInfo.first); - g.setSuccessor(CGF.LabelMap[gotoInfo.second].getBlock()); - localScope->PendingGotos.pop_back(); - } - localScope->SolvedLabels.clear(); - auto applyCleanup = [&]() { if (PerformCleanup) { // ApplyDebugLocation diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 2b68a13bc840..75a9f4333c9d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1962,13 +1962,6 @@ class CIRGenFunction : public CIRGenTypeCache { return CleanupBlock; } - // Goto's introduced in this scope but didn't get fixed. - llvm::SmallVector, 4> - PendingGotos; - - // Labels solved inside this scope. - llvm::SmallPtrSet SolvedLabels; - // --- // Exception handling // --- diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index b83b8795f841..019df15e1ce4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -551,14 +551,19 @@ mlir::LogicalResult CIRGenFunction::buildGotoStmt(const GotoStmt &S) { // info support just yet, look at this again once we have it. assert(builder.getInsertionBlock() && "not yet implemented"); + mlir::Block *currBlock = builder.getBlock(); + mlir::Block *gotoBlock = currBlock; + if (!currBlock->empty() && + currBlock->back().hasTrait()) { + gotoBlock = builder.createBlock(builder.getBlock()->getParent()); + builder.setInsertionPointToEnd(gotoBlock); + } + // A goto marks the end of a block, create a new one for codegen after // buildGotoStmt can resume building in that block. - // Build a cir.br to the target label. - auto &JD = LabelMap[S.getLabel()]; - auto brOp = buildBranchThroughCleanup(getLoc(S.getSourceRange()), JD); - if (!JD.isValid()) - currLexScope->PendingGotos.push_back(std::make_pair(brOp, S.getLabel())); + builder.create(getLoc(S.getSourceRange()), + S.getLabel()->getName()); // Insert the new block to continue codegen after goto. builder.createBlock(builder.getBlock()->getParent()); @@ -568,31 +573,22 @@ mlir::LogicalResult CIRGenFunction::buildGotoStmt(const GotoStmt &S) { } mlir::LogicalResult CIRGenFunction::buildLabel(const LabelDecl *D) { - JumpDest &Dest = LabelMap[D]; - // Create a new block to tag with a label and add a branch from // the current one to it. If the block is empty just call attach it // to this label. mlir::Block *currBlock = builder.getBlock(); mlir::Block *labelBlock = currBlock; if (!currBlock->empty()) { - { mlir::OpBuilder::InsertionGuard guard(builder); labelBlock = builder.createBlock(builder.getBlock()->getParent()); } - builder.create(getLoc(D->getSourceRange()), labelBlock); - builder.setInsertionPointToEnd(labelBlock); } - if (!Dest.isValid()) { - Dest.Block = labelBlock; - currLexScope->SolvedLabels.insert(D); - // FIXME: add a label attribute to block... - } else { - assert(0 && "unimplemented"); - } + builder.setInsertionPointToEnd(labelBlock); + builder.create(getLoc(D->getSourceRange()), D->getName()); + builder.setInsertionPointToEnd(labelBlock); // FIXME: emit debug info for labels, incrementProfileCounter return mlir::success(); diff --git a/clang/lib/CIR/CodeGen/CIRPasses.cpp b/clang/lib/CIR/CodeGen/CIRPasses.cpp index 7819d6db21ea..edffd66a9357 100644 --- a/clang/lib/CIR/CodeGen/CIRPasses.cpp +++ b/clang/lib/CIR/CodeGen/CIRPasses.cpp @@ -82,7 +82,7 @@ namespace mlir { void populateCIRPreLoweringPasses(OpPassManager &pm) { pm.addPass(createFlattenCFGPass()); - // add other passes here + pm.addPass(createGotoSolverPass()); } } // namespace mlir diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 3d7dbf868212..f02eaaa4e444 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -19,6 +19,7 @@ #include "llvm/Support/ErrorHandling.h" #include #include +#include #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/LLVMIR/LLVMTypes.h" @@ -2174,6 +2175,24 @@ LogicalResult cir::FuncOp::verify() { << "' must have empty body"; } + std::set labels; + std::set gotos; + + getOperation()->walk([&](mlir::Operation *op) { + if (auto lab = dyn_cast(op)) { + labels.emplace(lab.getLabel()); + } else if (auto goTo = dyn_cast(op)) { + gotos.emplace(goTo.getLabel()); + } + }); + + std::vector mismatched; + std::set_difference(gotos.begin(), gotos.end(), labels.begin(), labels.end(), + std::back_inserter(mismatched)); + + if (!mismatched.empty()) + return emitOpError() << "goto/label mismatch"; + return success(); } @@ -3083,6 +3102,18 @@ LogicalResult BinOp::verify() { return mlir::success(); } +//===----------------------------------------------------------------------===// +// LabelOp Definitions +//===----------------------------------------------------------------------===// + +LogicalResult LabelOp::verify() { + auto *op = getOperation(); + auto *blk = op->getBlock(); + if (&blk->front() != op) + return emitError() << "must be the first operation in a block"; + return mlir::success(); +} + //===----------------------------------------------------------------------===// // TableGen'd op method definitions //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt index 8bd6a06b7c4e..647d15aea8dc 100644 --- a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt +++ b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt @@ -8,6 +8,7 @@ add_clang_library(MLIRCIRTransforms LibOpt.cpp StdHelpers.cpp FlattenCFG.cpp + GotoSolver.cpp DEPENDS MLIRCIRPassIncGen diff --git a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp index ea1b413fc685..ce643e6735fa 100644 --- a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp +++ b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp @@ -224,9 +224,11 @@ class CIRLoopOpInterfaceFlattening }); // Lower optional body region yield. - auto bodyYield = dyn_cast(body->getTerminator()); - if (bodyYield) - lowerTerminator(bodyYield, (step ? step : cond), rewriter); + for (auto &blk : op.getBody().getBlocks()) { + auto bodyYield = dyn_cast(blk.getTerminator()); + if (bodyYield) + lowerTerminator(bodyYield, (step ? step : cond), rewriter); + } // Lower mandatory step region yield. if (step) diff --git a/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp b/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp new file mode 100644 index 000000000000..34eb488b732c --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/GotoSolver.cpp @@ -0,0 +1,54 @@ +#include "PassDetail.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/Support/LogicalResult.h" +#include "mlir/Transforms/DialectConversion.h" +#include "mlir/Transforms/GreedyPatternRewriteDriver.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/Dialect/Passes.h" + +using namespace mlir; +using namespace mlir::cir; + +namespace { + +struct GotoSolverPass : public GotoSolverBase { + + GotoSolverPass() = default; + void runOnOperation() override; +}; + +static void process(mlir::cir::FuncOp func) { + + mlir::OpBuilder rewriter(func.getContext()); + std::map labels; + std::vector gotos; + + func.getBody().walk([&](mlir::Operation *op) { + if (auto lab = dyn_cast(op)) { + labels.emplace(lab.getLabel().str(), lab->getBlock()); + lab.erase(); + } else if (auto goTo = dyn_cast(op)) { + gotos.push_back(goTo); + } + }); + + for (auto goTo : gotos) { + mlir::OpBuilder::InsertionGuard guard(rewriter); + rewriter.setInsertionPoint(goTo); + auto dest = labels[goTo.getLabel().str()]; + rewriter.create(goTo.getLoc(), dest); + goTo.erase(); + } +} + +void GotoSolverPass::runOnOperation() { + SmallVector ops; + getOperation()->walk([&](mlir::cir::FuncOp op) { process(op); }); +} + +} // namespace + +std::unique_ptr mlir::createGotoSolverPass() { + return std::make_unique(); +} \ No newline at end of file diff --git a/clang/lib/CIR/Dialect/Transforms/MergeCleanups.cpp b/clang/lib/CIR/Dialect/Transforms/MergeCleanups.cpp index e4848a21d0bd..05b951c01ad2 100644 --- a/clang/lib/CIR/Dialect/Transforms/MergeCleanups.cpp +++ b/clang/lib/CIR/Dialect/Transforms/MergeCleanups.cpp @@ -42,6 +42,9 @@ struct RemoveRedudantBranches : public OpRewritePattern { Block *block = op.getOperation()->getBlock(); Block *dest = op.getDest(); + if (isa(dest->front())) + return failure(); + // Single edge between blocks: merge it. if (block->getNumSuccessors() == 1 && dest->getSinglePredecessor() == block) { diff --git a/clang/test/CIR/CodeGen/goto.cpp b/clang/test/CIR/CodeGen/goto.cpp index 204b00303fca..dc36517863ef 100644 --- a/clang/test/CIR/CodeGen/goto.cpp +++ b/clang/test/CIR/CodeGen/goto.cpp @@ -1,5 +1,8 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir -// RUN: FileCheck --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir-flat %s -o %t1.cir +// RUN: FileCheck --input-file=%t1.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t2.cir +// RUN: FileCheck --input-file=%t2.cir %s -check-prefix=NOFLAT + void g0(int a) { int b = a; @@ -64,3 +67,228 @@ int g2() { // CHECK: [[R:%[0-9]+]] = cir.load %0 : !cir.ptr, !s32i // CHECK-NEXT: [[R]] : !s32i // CHECK-NEXT: } + + +int shouldNotGenBranchRet(int x) { + if (x > 5) + goto err; + return 0; +err: + return -1; +} +// NOFLAT: cir.func @_Z21shouldNotGenBranchReti +// NOFLAT: cir.if %8 { +// NOFLAT: cir.goto "err" +// NOFLAT: } +// NOFLAT: ^bb1: +// NOFLAT: %3 = cir.load %1 : !cir.ptr, !s32i +// NOFLAT: cir.return %3 : !s32i +// NOFLAT: ^bb2: // no predecessors +// NOFLAT: cir.label "err" + +int shouldGenBranch(int x) { + if (x > 5) + goto err; + x++; +err: + return -1; +} +// NOFLAT: cir.func @_Z15shouldGenBranchi +// NOFLAT: cir.if %9 { +// NOFLAT: cir.goto "err" +// NOFLAT: } +// NOFLAT: cir.br ^bb1 +// NOFLAT: ^bb1: +// NOFLAT: cir.label "err" + +int shouldCreateBlkForGoto(int a) { + switch (a) { + case(42): + break; + goto exit; + default: + return 0; + }; + +exit: + return -1; + +} +// NOFLAT: cir.func @_Z22shouldCreateBlkForGotoi +// NOFLAT: case (equal, 42) { +// NOFLAT: cir.break +// NOFLAT: ^bb1: // no predecessors +// NOFLAT: cir.goto "exit" +// NOFLAT: } + +void severalLabelsInARow(int a) { + int b = a; + goto end1; + b = b + 1; + goto end2; +end1: +end2: + b = b + 2; +} +// NOFLAT: cir.func @_Z19severalLabelsInARowi +// NOFLAT: ^bb[[#BLK1:]]: +// NOFLAT: cir.label "end1" +// NOFLAT: cir.br ^bb[[#BLK2:]] +// NOFLAT: ^bb[[#BLK2]]: +// NOFLAT: cir.label "end2" + +void severalGotosInARow(int a) { + int b = a; + goto end; + goto end; +end: + b = b + 2; +} +// NOFLAT: cir.func @_Z18severalGotosInARowi +// NOFLAT: cir.goto "end" +// NOFLAT: ^bb[[#BLK1:]]: +// NOFLAT: cir.goto "end" +// NOFLAT: ^bb[[#BLK2:]]: +// NOFLAT: cir.label "end" + + +void labelWithoutMatch() { +end: + return; +} +// NOFLAT: cir.func @_Z17labelWithoutMatchv() +// NOFLAT: cir.label "end" +// NOFLAT: cir.return +// NOFLAT: } + + +int jumpIntoLoop(int* ar) { + + if (ar) + goto label; + return -1; + + while (ar) { + label: + ++ar; + } + + return 0; +} + +// CHECK: cir.func @_Z12jumpIntoLoopPi +// CHECK: cir.brcond {{.*}} ^bb[[#BLK2:]], ^bb[[#BLK3:]] +// CHECK: ^bb[[#BLK2]]: +// CHECK: cir.br ^bb[[#BODY:]] +// CHECK: ^bb[[#BLK3]]: +// CHECK: cir.br ^bb[[#BLK4:]] +// CHECK: ^bb[[#BLK4]]: +// CHECK: cir.br ^bb[[#RETURN:]] +// CHECK: ^bb[[#RETURN]]: +// CHECK: cir.return +// CHECK: ^bb[[#BLK5:]]: +// CHECK: cir.br ^bb[[#BLK6:]] +// CHECK: ^bb[[#BLK6]]: +// CHECK: cir.br ^bb[[#COND:]] +// CHECK: ^bb[[#COND]]: +// CHECK: cir.brcond {{.*}} ^bb[[#BODY]], ^bb[[#EXIT:]] +// CHECK: ^bb[[#BODY]]: +// CHECK: cir.br ^bb[[#COND]] +// CHECK: ^bb[[#EXIT]]: +// CHECK: cir.br ^bb[[#BLK7:]] +// CHECK: ^bb[[#BLK7]]: +// CHECK: cir.br ^bb[[#RETURN]] + + + +int jumpFromLoop(int* ar) { + + if (!ar) { +err: + return -1; +} + + while (ar) { + if (*ar == 42) + goto err; + ++ar; + } + + return 0; +} +// CHECK: cir.func @_Z12jumpFromLoopPi +// CHECK: cir.brcond {{.*}} ^bb[[#RETURN1:]], ^bb[[#BLK3:]] +// CHECK: ^bb[[#RETURN1]]: +// CHECK: cir.return +// CHECK: ^bb[[#BLK3]]: +// CHECK: cir.br ^bb[[#BLK4:]] +// CHECK: ^bb[[#BLK4]]: +// CHECK: cir.br ^bb[[#BLK5:]] +// CHECK: ^bb[[#BLK5]]: +// CHECK: cir.br ^bb[[#COND:]] +// CHECK: ^bb[[#COND]]: +// CHECK: cir.brcond {{.*}} ^bb[[#BODY:]], ^bb[[#EXIT:]] +// CHECK: ^bb[[#BODY]]: +// CHECK: cir.br ^bb[[#IF42:]] +// CHECK: ^bb[[#IF42]]: +// CHECK: cir.brcond {{.*}} ^bb[[#IF42TRUE:]], ^bb[[#IF42FALSE:]] +// CHECK: ^bb[[#IF42TRUE]]: +// CHECK: cir.br ^bb[[#RETURN1]] +// CHECK: ^bb[[#IF42FALSE]]: +// CHECK: cir.br ^bb[[#BLK11:]] +// CHECK: ^bb[[#BLK11]]: +// CHECK: cir.br ^bb[[#COND]] +// CHECK: ^bb[[#EXIT]]: +// CHECK: cir.br ^bb[[#RETURN2:]] +// CHECK: ^bb[[#RETURN2]]: +// CHECK: cir.return + + +void flatLoopWithNoTerminatorInFront(int* ptr) { + + if (ptr) + goto loop; + + do { + if (!ptr) + goto end; + loop: + ptr++; + } while(ptr); + + end: + ; +} + +// CHECK: cir.func @_Z31flatLoopWithNoTerminatorInFrontPi +// CHECK: cir.brcond {{.*}} ^bb[[#BLK2:]], ^bb[[#BLK3:]] +// CHECK: ^bb[[#BLK2]]: +// CHECK: cir.br ^bb[[#LABEL_LOOP:]] +// CHECK: ^bb[[#BLK3]]: +// CHECK: cir.br ^bb[[#BLK4:]] +// CHECK: ^bb[[#BLK4]]: +// CHECK: cir.br ^bb[[#BLK5:]] +// CHECK: ^bb[[#BLK5]]: +// CHECK: cir.br ^bb[[#BODY:]] +// CHECK: ^bb[[#COND]]: +// CHECK: cir.brcond {{.*}} ^bb[[#BODY]], ^bb[[#EXIT:]] +// CHECK: ^bb[[#BODY]]: +// CHECK: cir.br ^bb[[#BLK8:]] +// CHECK: ^bb[[#BLK8]]: +// CHECK: cir.brcond {{.*}} ^bb[[#BLK9:]], ^bb[[#BLK10:]] +// CHECK: ^bb[[#BLK9]]: +// CHECK: cir.br ^bb[[#RETURN:]] +// CHECK: ^bb[[#BLK10]]: +// CHECK: cir.br ^bb[[#BLK11:]] +// CHECK: ^bb[[#BLK11]]: +// CHECK: cir.br ^bb[[#LABEL_LOOP]] +// CHECK: ^bb[[#LABEL_LOOP]]: +// CHECK: cir.br ^bb[[#COND]] +// CHECK: ^bb[[#EXIT]]: +// CHECK: cir.br ^bb[[#BLK14:]] +// CHECK: ^bb[[#BLK14]]: +// CHECK: cir.br ^bb[[#RETURN]] +// CHECK: ^bb[[#RETURN]]: +// CHECK: cir.return +// CHECK: } +// CHECK:} \ No newline at end of file diff --git a/clang/test/CIR/IR/invalid.cir b/clang/test/CIR/IR/invalid.cir index 6128fa42b824..2ed718d6176f 100644 --- a/clang/test/CIR/IR/invalid.cir +++ b/clang/test/CIR/IR/invalid.cir @@ -1107,3 +1107,14 @@ module { %0 = cir.dyn_cast(ptr, %arg0 : !cir.ptr, #cir.dyn_cast_info<#cir.global_view<@_ZTI4Base> : !cir.ptr, #cir.global_view<@_ZTI7Derived> : !cir.ptr, @__dynamic_cast, @__cxa_bad_cast, #cir.int<0> : !s64i>) -> !cir.ptr } } + + +// ----- + +// expected-error@+1 {{goto/label mismatch}} +cir.func @bad_goto() -> () { + cir.goto "somewhere" +^bb1: + cir.label "label" + cir.return +} diff --git a/clang/test/CIR/Lowering/goto.cir b/clang/test/CIR/Lowering/goto.cir index 271666744d6c..379617d8f1f5 100644 --- a/clang/test/CIR/Lowering/goto.cir +++ b/clang/test/CIR/Lowering/goto.cir @@ -1,36 +1,53 @@ -// RUN: cir-opt %s -canonicalize -cir-to-llvm -o - | FileCheck %s -check-prefix=MLIR -// RUN: cir-opt %s -canonicalize -o - | cir-translate -cir-to-llvmir | FileCheck %s -check-prefix=LLVM +// RUN: cir-opt %s --pass-pipeline='builtin.module(cir-to-llvm,canonicalize{region-simplify=false})' -o - | FileCheck %s -check-prefix=MLIR -!u32i = !cir.int +!s32i = !cir.int module { - cir.func @foo() { - %0 = cir.alloca !u32i, !cir.ptr, ["b", init] {alignment = 4 : i64} - %1 = cir.const #cir.int<1> : !u32i - cir.store %1, %0 : !u32i, !cir.ptr - cir.br ^bb2 - ^bb1: // no predecessors - %2 = cir.load %0 : !cir.ptr, !u32i - %3 = cir.const #cir.int<1> : !u32i - %4 = cir.binop(add, %2, %3) : !u32i - cir.store %4, %0 : !u32i, !cir.ptr - cir.br ^bb2 - ^bb2: // 2 preds: ^bb0, ^bb1 - %5 = cir.load %0 : !cir.ptr, !u32i - %6 = cir.const #cir.int<2> : !u32i - %7 = cir.binop(add, %5, %6) : !u32i - cir.store %7, %0 : !u32i, !cir.ptr - cir.return + + cir.func @gotoFromIf(%arg0: !s32i) -> !s32i { + %0 = cir.alloca !s32i, !cir.ptr, ["x", init] {alignment = 4 : i64} + %1 = cir.alloca !s32i, !cir.ptr, ["__retval"] {alignment = 4 : i64} + cir.store %arg0, %0 : !s32i, !cir.ptr + cir.scope { + %6 = cir.load %0 : !cir.ptr, !s32i + %7 = cir.const #cir.int<5> : !s32i + %8 = cir.cmp(gt, %6, %7) : !s32i, !s32i + %9 = cir.cast(int_to_bool, %8 : !s32i), !cir.bool + cir.if %9 { + cir.goto "err" + } + } + %2 = cir.const #cir.int<0> : !s32i + cir.store %2, %1 : !s32i, !cir.ptr + cir.br ^bb1 + ^bb1: + %3 = cir.load %1 : !cir.ptr, !s32i + cir.return %3 : !s32i + ^bb2: + cir.label "err" + %4 = cir.const #cir.int<1> : !s32i + %5 = cir.unary(minus, %4) : !s32i, !s32i + cir.store %5, %1 : !s32i, !cir.ptr + cir.br ^bb1 } -} -// MLIR: module { -// MLIR-NEXT: llvm.func @foo -// MLIR: llvm.br ^bb1 -// MLIR: ^bb1: -// MLIR: return - -// LLVM: br label %[[Value:[0-9]+]] -// LLVM-EMPTY: -// LLVM-NEXT: [[Value]]: ; preds = -// LLVM: ret void +// MLIR: llvm.func @gotoFromIf +// MLIR: %[[#One:]] = llvm.mlir.constant(1 : i32) : i32 +// MLIR: %[[#Zero:]] = llvm.mlir.constant(0 : i32) : i32 +// MLIR: llvm.cond_br {{.*}}, ^bb[[#COND_YES:]], ^bb[[#COND_NO:]] +// MLIR: ^bb[[#COND_YES]]: +// MLIR: llvm.br ^bb[[#GOTO_BLK:]] +// MLIR: ^bb[[#COND_NO]]: +// MLIR: llvm.br ^bb[[#BLK:]] +// MLIR: ^bb[[#BLK]]: +// MLIR: llvm.store %[[#Zero]], %[[#Ret_val_addr:]] : i32, !llvm.ptr +// MLIR: llvm.br ^bb[[#RETURN:]] +// MLIR: ^bb[[#RETURN]]: +// MLIR: %[[#Ret_val:]] = llvm.load %[[#Ret_val_addr]] : !llvm.ptr -> i32 +// MLIR: llvm.return %[[#Ret_val]] : i32 +// MLIR: ^bb[[#GOTO_BLK]]: +// MLIR: %[[#Neg_one:]] = llvm.sub %[[#Zero]], %[[#One]] : i32 +// MLIR: llvm.store %[[#Neg_one]], %[[#Ret_val_addr]] : i32, !llvm.ptr +// MLIR: llvm.br ^bb[[#RETURN]] +// MLIR: } +} \ No newline at end of file diff --git a/clang/test/CIR/Lowering/region-simplify.cir b/clang/test/CIR/Lowering/region-simplify.cir new file mode 100644 index 000000000000..5f32205cb032 --- /dev/null +++ b/clang/test/CIR/Lowering/region-simplify.cir @@ -0,0 +1,38 @@ +// RUN: cir-opt %s -canonicalize -cir-to-llvm -o - | FileCheck %s -check-prefix=MLIR +// RUN: cir-opt %s -canonicalize -o - | cir-translate -cir-to-llvmir | FileCheck %s -check-prefix=LLVM + +!u32i = !cir.int + +module { + cir.func @foo() { + %0 = cir.alloca !u32i, !cir.ptr, ["b", init] {alignment = 4 : i64} + %1 = cir.const #cir.int<1> : !u32i + cir.store %1, %0 : !u32i, !cir.ptr + cir.br ^bb2 + ^bb1: // no predecessors + %2 = cir.load %0 : !cir.ptr, !u32i + %3 = cir.const #cir.int<1> : !u32i + %4 = cir.binop(add, %2, %3) : !u32i + cir.store %4, %0 : !u32i, !cir.ptr + cir.br ^bb2 + ^bb2: // 2 preds: ^bb0, ^bb1 + %5 = cir.load %0 : !cir.ptr, !u32i + %6 = cir.const #cir.int<2> : !u32i + %7 = cir.binop(add, %5, %6) : !u32i + cir.store %7, %0 : !u32i, !cir.ptr + cir.return + } + + // MLIR: module { +// MLIR-NEXT: llvm.func @foo +// MLIR: llvm.br ^bb1 +// MLIR: ^bb1: +// MLIR: return + +// LLVM: br label %[[Value:[0-9]+]] +// LLVM-EMPTY: +// LLVM-NEXT: [[Value]]: ; preds = +// LLVM: ret void + + +} \ No newline at end of file