llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) <details> <summary>Changes</summary> This adds ClangIR support for break and continue statements in loops. Because only loops are currently implemented upstream in CIR, only breaks in loops are supported here, but this same code will work (with minor changes to the verification and cfg flattening) when switch statements are upstreamed. --- Full diff: https://github.com/llvm/llvm-project/pull/134181.diff 8 Files Affected: - (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+10) - (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+29) - (modified) clang/include/clang/CIR/MissingFeatures.h (-2) - (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+2) - (modified) clang/lib/CIR/CodeGen/CIRGenStmt.cpp (+23) - (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+21) - (modified) clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp (+11-10) - (modified) clang/test/CIR/CodeGen/loop.cpp (+122) ``````````diff diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index e666be0b25d75..900d78c401ebf 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -136,6 +136,16 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create<cir::ForOp>(loc, condBuilder, bodyBuilder, stepBuilder); } + /// Create a break operation. + cir::BreakOp createBreak(mlir::Location loc) { + return create<cir::BreakOp>(loc); + } + + /// Create a continue operation. + cir::ContinueOp createContinue(mlir::Location loc) { + return create<cir::ContinueOp>(loc); + } + mlir::TypedAttr getConstPtrAttr(mlir::Type type, int64_t value) { auto valueAttr = mlir::IntegerAttr::get( mlir::IntegerType::get(type.getContext(), 64), value); diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 3965372755685..7ddf5e843bde2 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -569,6 +569,35 @@ def YieldOp : CIR_Op<"yield", [ReturnLike, Terminator, ]; } +//===----------------------------------------------------------------------===// +// BreakOp +//===----------------------------------------------------------------------===// + +def BreakOp : CIR_Op<"break", [Terminator]> { + let summary = "C/C++ `break` statement equivalent"; + let description = [{ + The `cir.break` operation is used to cease the control flow to the parent + operation, exiting its region's control flow. It is only allowed if it is + within a breakable operation (loops and `switch`). + }]; + let assemblyFormat = "attr-dict"; + let hasVerifier = 1; +} + +//===----------------------------------------------------------------------===// +// ContinueOp +//===----------------------------------------------------------------------===// + +def ContinueOp : CIR_Op<"continue", [Terminator]> { + let summary = "C/C++ `continue` statement equivalent"; + let description = [{ + The `cir.continue` operation is used to continue execution to the next + iteration of a loop. It is only allowed within `cir.loop` regions. + }]; + let assemblyFormat = "attr-dict"; + let hasVerifier = 1; +} + //===----------------------------------------------------------------------===// // ScopeOp //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 3a102d90aba8f..be16b5c063f3b 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -122,12 +122,10 @@ struct MissingFeatures { // Future CIR operations static bool awaitOp() { return false; } - static bool breakOp() { return false; } static bool callOp() { return false; } static bool complexCreateOp() { return false; } static bool complexImagOp() { return false; } static bool complexRealOp() { return false; } - static bool continueOp() { return false; } static bool ifOp() { return false; } static bool labelOp() { return false; } static bool ptrDiffOp() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 5cae4d5da9516..c244e3752d7fe 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -395,6 +395,8 @@ class CIRGenFunction : public CIRGenTypeCache { LValue emitBinaryOperatorLValue(const BinaryOperator *e); + mlir::LogicalResult emitBreakStmt(const clang::BreakStmt &s); + mlir::LogicalResult emitContinueStmt(const clang::ContinueStmt &s); mlir::LogicalResult emitDoStmt(const clang::DoStmt &s); /// Emit an expression as an initializer for an object (variable, field, etc.) diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index b5c1f0ae2a7ef..0c33da6c8722c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -229,6 +229,10 @@ mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s, else emitCompoundStmt(cast<CompoundStmt>(*s)); break; + case Stmt::ContinueStmtClass: + return emitContinueStmt(cast<ContinueStmt>(*s)); + case Stmt::BreakStmtClass: + return emitBreakStmt(cast<BreakStmt>(*s)); case Stmt::ReturnStmtClass: return emitReturnStmt(cast<ReturnStmt>(*s)); } @@ -316,6 +320,25 @@ mlir::LogicalResult CIRGenFunction::emitReturnStmt(const ReturnStmt &s) { return mlir::success(); } +mlir::LogicalResult +CIRGenFunction::emitContinueStmt(const clang::ContinueStmt &s) { + builder.createContinue(getLoc(s.getContinueLoc())); + + // Insert the new block to continue codegen after the continue statement. + builder.createBlock(builder.getBlock()->getParent()); + + return mlir::success(); +} + +mlir::LogicalResult CIRGenFunction::emitBreakStmt(const clang::BreakStmt &s) { + builder.createBreak(getLoc(s.getBreakLoc())); + + // Insert the new block to continue codegen after the break statement. + builder.createBlock(builder.getBlock()->getParent()); + + return mlir::success(); +} + mlir::LogicalResult CIRGenFunction::emitForStmt(const ForStmt &s) { cir::ForOp forOp; diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 4ace083e3c081..ccbc075ae7d9d 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -151,6 +151,17 @@ void cir::AllocaOp::build(mlir::OpBuilder &odsBuilder, odsState.addTypes(addr); } +//===----------------------------------------------------------------------===// +// BreakOp +//===----------------------------------------------------------------------===// + +LogicalResult cir::BreakOp::verify() { + assert(!cir::MissingFeatures::switchOp()); + if (!getOperation()->getParentOfType<LoopOpInterface>()) + return emitOpError("must be within a loop"); + return success(); +} + //===----------------------------------------------------------------------===// // ConditionOp //===----------------------------------------------------------------------===// @@ -241,6 +252,16 @@ OpFoldResult cir::ConstantOp::fold(FoldAdaptor /*adaptor*/) { return getValue(); } +//===----------------------------------------------------------------------===// +// ContinueOp +//===----------------------------------------------------------------------===// + +LogicalResult cir::ContinueOp::verify() { + if (!getOperation()->getParentOfType<LoopOpInterface>()) + return emitOpError("must be within a loop"); + return success(); +} + //===----------------------------------------------------------------------===// // CastOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp index 52f4b2241505d..b49c5ffd35c00 100644 --- a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp +++ b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp @@ -148,23 +148,24 @@ class CIRLoopOpInterfaceFlattening // driver to customize the order that operations are visited. // Lower continue statements. + mlir::Block *dest = (step ? step : cond); op.walkBodySkippingNestedLoops([&](mlir::Operation *op) { - // When continue ops are supported, there will be a check for them here - // and a call to lowerTerminator(). The call to `advance()` handles the - // case where this is not a continue op. - assert(!cir::MissingFeatures::continueOp()); - return mlir::WalkResult::advance(); + if (!isa<cir::ContinueOp>(op)) + return mlir::WalkResult::advance(); + + lowerTerminator(op, dest, rewriter); + return mlir::WalkResult::skip(); }); // Lower break statements. assert(!cir::MissingFeatures::switchOp()); walkRegionSkipping<cir::LoopOpInterface>( op.getBody(), [&](mlir::Operation *op) { - // When break ops are supported, there will be a check for them here - // and a call to lowerTerminator(). The call to `advance()` handles - // the case where this is not a break op. - assert(!cir::MissingFeatures::breakOp()); - return mlir::WalkResult::advance(); + if (!isa<cir::BreakOp>(op)) + return mlir::WalkResult::advance(); + + lowerTerminator(op, exit, rewriter); + return mlir::WalkResult::skip(); }); // Lower optional body region yield. diff --git a/clang/test/CIR/CodeGen/loop.cpp b/clang/test/CIR/CodeGen/loop.cpp index a950460e8838d..46fa66e2fc7aa 100644 --- a/clang/test/CIR/CodeGen/loop.cpp +++ b/clang/test/CIR/CodeGen/loop.cpp @@ -265,3 +265,125 @@ void test_empty_while_true() { // OGCG: br label %[[WHILE_BODY:.*]] // OGCG: [[WHILE_BODY]]: // OGCG: ret void + +void unreachable_after_continue() { + for (;;) { + continue; + int x = 1; + } +} + +// CIR: cir.func @unreachable_after_continue +// CIR: cir.scope { +// CIR: cir.for : cond { +// CIR: %[[TRUE:.*]] = cir.const #true +// CIR: cir.condition(%[[TRUE]]) +// CIR: } body { +// CIR: cir.scope { +// CIR: %[[X:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init] {alignment = 4 : i64} +// CIR: cir.continue +// CIR: ^bb1: // no predecessors +// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i +// CIR: cir.store %[[ONE]], %[[X]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.yield +// CIR: } +// CIR: cir.yield +// CIR: } step { +// CIR: cir.yield +// CIR: } +// CIR: } +// CIR: cir.return +// CIR: } + +// LLVM: define void @unreachable_after_continue() +// LLVM: %[[X:.*]] = alloca i32, i64 1, align 4 +// LLVM: br label %[[LABEL1:.*]] +// LLVM: [[LABEL1]]: +// LLVM: br label %[[LABEL2:.*]] +// LLVM: [[LABEL2]]: +// LLVM: br i1 true, label %[[LABEL3:.*]], label %[[LABEL8:.*]] +// LLVM: [[LABEL3]]: +// LLVM: br label %[[LABEL4:.*]] +// LLVM: [[LABEL4]]: +// LLVM: br label %[[LABEL7:.*]] +// LLVM: [[LABEL5:.*]]: +// LLVM-SAME: ; No predecessors! +// LLVM: store i32 1, ptr %[[X]], align 4 +// LLVM: br label %[[LABEL6:.*]] +// LLVM: [[LABEL6]]: +// LLVM: br label %[[LABEL7:.*]] +// LLVM: [[LABEL7]]: +// LLVM: br label %[[LABEL2]] +// LLVM: [[LABEL8]]: +// LLVM: br label %[[LABEL9:]] +// LLVM: [[LABEL9]]: +// LLVM: ret void + +// OGCG: define{{.*}} void @_Z26unreachable_after_continuev() +// OGCG: entry: +// OGCG: %[[X:.*]] = alloca i32, align 4 +// OGCG: br label %[[FOR_COND:.*]] +// OGCG: [[FOR_COND]]: +// OGCG: br label %[[FOR_COND]] + +void unreachable_after_break() { + for (;;) { + break; + int x = 1; + } +} + +// CIR: cir.func @unreachable_after_break +// CIR: cir.scope { +// CIR: cir.for : cond { +// CIR: %[[TRUE:.*]] = cir.const #true +// CIR: cir.condition(%[[TRUE]]) +// CIR: } body { +// CIR: cir.scope { +// CIR: %[[X:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init] {alignment = 4 : i64} +// CIR: cir.break +// CIR: ^bb1: // no predecessors +// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i +// CIR: cir.store %[[ONE]], %[[X]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.yield +// CIR: } +// CIR: cir.yield +// CIR: } step { +// CIR: cir.yield +// CIR: } +// CIR: } +// CIR: cir.return +// CIR: } + +// LLVM: define void @unreachable_after_break() +// LLVM: %[[X:.*]] = alloca i32, i64 1, align 4 +// LLVM: br label %[[LABEL1:.*]] +// LLVM: [[LABEL1]]: +// LLVM: br label %[[LABEL2:.*]] +// LLVM: [[LABEL2]]: +// LLVM: br i1 true, label %[[LABEL3:.*]], label %[[LABEL8:.*]] +// LLVM: [[LABEL3]]: +// LLVM: br label %[[LABEL4:.*]] +// LLVM: [[LABEL4]]: +// LLVM: br label %[[LABEL8]] +// LLVM: [[LABEL5:.*]]: +// LLVM-SAME: ; No predecessors! +// LLVM: store i32 1, ptr %[[X]], align 4 +// LLVM: br label %[[LABEL6:.*]] +// LLVM: [[LABEL6]]: +// LLVM: br label %[[LABEL7:.*]] +// LLVM: [[LABEL7]]: +// LLVM: br label %[[LABEL2]] +// LLVM: [[LABEL8]]: +// LLVM: br label %[[LABEL9:]] +// LLVM: [[LABEL9]]: +// LLVM: ret void + +// OGCG: define{{.*}} void @_Z23unreachable_after_breakv() +// OGCG: entry: +// OGCG: %[[X:.*]] = alloca i32, align 4 +// OGCG: br label %[[FOR_COND:.*]] +// OGCG: [[FOR_COND]]: +// OGCG: br label %[[FOR_END:.*]] +// OGCG: [[FOR_END]]: +// OGCG: ret void `````````` </details> https://github.com/llvm/llvm-project/pull/134181 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits