Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
29 changes: 29 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't let me suggest an edit here for some reason, but the 'cease ...to the parent' text seems wrong. DO we mean 'cede' 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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The `cir.continue` operation is used to continue execution to the next
The `cir.continue` operation is used end execution of the current iteration of the loop, and resume execution of the next

Or something like that.

iteration of a loop. It is only allowed within `cir.loop` regions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: There is technically nothing like cir.loop. More precise would be to mention LoopOpInterface directly here?

}];
let assemblyFormat = "attr-dict";
let hasVerifier = 1;
}

//===----------------------------------------------------------------------===//
// ScopeOp
//===----------------------------------------------------------------------===//
Expand Down
2 changes: 0 additions & 2 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.)
Expand Down
23 changes: 23 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down Expand Up @@ -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;

Expand Down
21 changes: 21 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down
21 changes: 11 additions & 10 deletions clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
122 changes: 122 additions & 0 deletions clang/test/CIR/CodeGen/loop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading