[clang] [CIR] Upstream support for while and do..while loops (PR #133157)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 26 13:43:14 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clangir
Author: Andy Kaylor (andykaylor)
<details>
<summary>Changes</summary>
This adds basic support for while and do..while loops. Support for break and continue are left for a subsequent patch.
---
Full diff: https://github.com/llvm/llvm-project/pull/133157.diff
7 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+16)
- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+82-2)
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+4)
- (modified) clang/lib/CIR/CodeGen/CIRGenStmt.cpp (+111-2)
- (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+21-1)
- (modified) clang/test/CIR/CodeGen/loop.cpp (+76)
- (modified) clang/test/CIR/Transforms/loop.cir (+41)
``````````diff
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index ac7658276ec37..36ad22046deb5 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -87,6 +87,22 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return cir::BoolAttr::get(getContext(), getBoolTy(), state);
}
+ /// Create a do-while operation.
+ cir::DoWhileOp createDoWhile(
+ mlir::Location loc,
+ llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> condBuilder,
+ llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> bodyBuilder) {
+ return create<cir::DoWhileOp>(loc, condBuilder, bodyBuilder);
+ }
+
+ /// Create a while operation.
+ cir::WhileOp createWhile(
+ mlir::Location loc,
+ llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> condBuilder,
+ llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)> bodyBuilder) {
+ return create<cir::WhileOp>(loc, condBuilder, bodyBuilder);
+ }
+
/// Create a for operation.
cir::ForOp createFor(
mlir::Location loc,
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 455cc2b8b0277..7a604421212d9 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -424,7 +424,8 @@ def StoreOp : CIR_Op<"store", [
// ReturnOp
//===----------------------------------------------------------------------===//
-def ReturnOp : CIR_Op<"return", [ParentOneOf<["FuncOp", "ScopeOp", "ForOp"]>,
+def ReturnOp : CIR_Op<"return", [ParentOneOf<["FuncOp", "ScopeOp", "DoWhileOp",
+ "WhileOp", "ForOp"]>,
Terminator]> {
let summary = "Return from function";
let description = [{
@@ -511,7 +512,8 @@ def ConditionOp : CIR_Op<"condition", [
//===----------------------------------------------------------------------===//
def YieldOp : CIR_Op<"yield", [ReturnLike, Terminator,
- ParentOneOf<["ScopeOp", "ForOp"]>]> {
+ ParentOneOf<["ScopeOp", "WhileOp", "ForOp",
+ "DoWhileOp"]>]> {
let summary = "Represents the default branching behaviour of a region";
let description = [{
The `cir.yield` operation terminates regions on different CIR operations,
@@ -759,6 +761,84 @@ def BrCondOp : CIR_Op<"brcond",
}];
}
+//===----------------------------------------------------------------------===//
+// While & DoWhileOp
+//===----------------------------------------------------------------------===//
+
+class WhileOpBase<string mnemonic> : CIR_Op<mnemonic, [
+ LoopOpInterface,
+ NoRegionArguments,
+]> {
+ defvar isWhile = !eq(mnemonic, "while");
+ let summary = "C/C++ " # !if(isWhile, "while", "do-while") # " loop";
+ let builders = [
+ OpBuilder<(ins "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$condBuilder,
+ "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$bodyBuilder), [{
+ mlir::OpBuilder::InsertionGuard guard($_builder);
+ $_builder.createBlock($_state.addRegion());
+ }] # !if(isWhile, [{
+ condBuilder($_builder, $_state.location);
+ $_builder.createBlock($_state.addRegion());
+ bodyBuilder($_builder, $_state.location);
+ }], [{
+ bodyBuilder($_builder, $_state.location);
+ $_builder.createBlock($_state.addRegion());
+ condBuilder($_builder, $_state.location);
+ }])>
+ ];
+}
+
+def WhileOp : WhileOpBase<"while"> {
+ let regions = (region SizedRegion<1>:$cond, MinSizedRegion<1>:$body);
+ let assemblyFormat = "$cond `do` $body attr-dict";
+
+ let description = [{
+ Represents a C/C++ while loop. It consists of two regions:
+
+ - `cond`: single block region with the loop's condition. Should be
+ terminated with a `cir.condition` operation.
+ - `body`: contains the loop body and an arbitrary number of blocks.
+
+ Example:
+
+ ```mlir
+ cir.while {
+ cir.break
+ ^bb2:
+ cir.yield
+ } do {
+ cir.condition %cond : cir.bool
+ }
+ ```
+ }];
+}
+
+def DoWhileOp : WhileOpBase<"do"> {
+ let regions = (region MinSizedRegion<1>:$body, SizedRegion<1>:$cond);
+ let assemblyFormat = " $body `while` $cond attr-dict";
+
+ let extraClassDeclaration = [{
+ mlir::Region &getEntry() { return getBody(); }
+ }];
+
+ let description = [{
+ Represents a C/C++ do-while loop. Identical to `cir.while` but the
+ condition is evaluated after the body.
+
+ Example:
+
+ ```mlir
+ cir.do {
+ cir.break
+ ^bb2:
+ cir.yield
+ } while {
+ cir.condition %cond : cir.bool
+ }
+ ```
+ }];
+}
+
//===----------------------------------------------------------------------===//
// ForOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 87d10ff4cd954..7d11ea71e7637 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -399,6 +399,8 @@ class CIRGenFunction : public CIRGenTypeCache {
LValue emitBinaryOperatorLValue(const BinaryOperator *e);
+ mlir::LogicalResult emitDoStmt(const clang::DoStmt &s);
+
/// Emit an expression as an initializer for an object (variable, field, etc.)
/// at the given location. The expression is not necessarily the normal
/// initializer for the object, and the address is not necessarily
@@ -497,6 +499,8 @@ class CIRGenFunction : public CIRGenTypeCache {
/// inside a function, including static vars etc.
void emitVarDecl(const clang::VarDecl &d);
+ mlir::LogicalResult emitWhileStmt(const clang::WhileStmt &s);
+
/// ----------------------
/// CIR build helpers
/// -----------------
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index aa04ff6345fc6..b5c1f0ae2a7ef 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -75,6 +75,10 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
case Stmt::ForStmtClass:
return emitForStmt(cast<ForStmt>(*s));
+ case Stmt::WhileStmtClass:
+ return emitWhileStmt(cast<WhileStmt>(*s));
+ case Stmt::DoStmtClass:
+ return emitDoStmt(cast<DoStmt>(*s));
case Stmt::OMPScopeDirectiveClass:
case Stmt::OMPErrorDirectiveClass:
@@ -97,8 +101,6 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
case Stmt::SYCLKernelCallStmtClass:
case Stmt::IfStmtClass:
case Stmt::SwitchStmtClass:
- case Stmt::WhileStmtClass:
- case Stmt::DoStmtClass:
case Stmt::CoroutineBodyStmtClass:
case Stmt::CoreturnStmtClass:
case Stmt::CXXTryStmtClass:
@@ -387,3 +389,110 @@ mlir::LogicalResult CIRGenFunction::emitForStmt(const ForStmt &s) {
terminateBody(builder, forOp.getBody(), getLoc(s.getEndLoc()));
return mlir::success();
}
+
+mlir::LogicalResult CIRGenFunction::emitDoStmt(const DoStmt &s) {
+ cir::DoWhileOp doWhileOp;
+
+ // TODO: pass in array of attributes.
+ auto doStmtBuilder = [&]() -> mlir::LogicalResult {
+ mlir::LogicalResult loopRes = mlir::success();
+ assert(!cir::MissingFeatures::loopInfoStack());
+ // From LLVM: if there are any cleanups between here and the loop-exit
+ // scope, create a block to stage a loop exit along.
+ // We probably already do the right thing because of ScopeOp, but make
+ // sure we handle all cases.
+ assert(!cir::MissingFeatures::requiresCleanups());
+
+ doWhileOp = builder.createDoWhile(
+ getLoc(s.getSourceRange()),
+ /*condBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ assert(!cir::MissingFeatures::createProfileWeightsForLoop());
+ assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());
+ // C99 6.8.5p2/p4: The first substatement is executed if the
+ // expression compares unequal to 0. The condition must be a
+ // scalar type.
+ mlir::Value condVal = evaluateExprAsBool(s.getCond());
+ builder.createCondition(condVal);
+ },
+ /*bodyBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ // The scope of the do-while loop body is a nested scope.
+ if (emitStmt(s.getBody(), /*useCurrentScope=*/false).failed())
+ loopRes = mlir::failure();
+ emitStopPoint(&s);
+ });
+ return loopRes;
+ };
+
+ mlir::LogicalResult res = mlir::success();
+ mlir::Location scopeLoc = getLoc(s.getSourceRange());
+ builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ LexicalScope lexScope{
+ *this, loc, builder.getInsertionBlock()};
+ res = doStmtBuilder();
+ });
+
+ if (res.failed())
+ return res;
+
+ terminateBody(builder, doWhileOp.getBody(), getLoc(s.getEndLoc()));
+ return mlir::success();
+}
+
+mlir::LogicalResult CIRGenFunction::emitWhileStmt(const WhileStmt &s) {
+ cir::WhileOp whileOp;
+
+ // TODO: pass in array of attributes.
+ auto whileStmtBuilder = [&]() -> mlir::LogicalResult {
+ mlir::LogicalResult loopRes = mlir::success();
+ assert(!cir::MissingFeatures::loopInfoStack());
+ // From LLVM: if there are any cleanups between here and the loop-exit
+ // scope, create a block to stage a loop exit along.
+ // We probably already do the right thing because of ScopeOp, but make
+ // sure we handle all cases.
+ assert(!cir::MissingFeatures::requiresCleanups());
+
+ whileOp = builder.createWhile(
+ getLoc(s.getSourceRange()),
+ /*condBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ assert(!cir::MissingFeatures::createProfileWeightsForLoop());
+ assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());
+ mlir::Value condVal;
+ // If the for statement has a condition scope,
+ // emit the local variable declaration.
+ if (s.getConditionVariable())
+ emitDecl(*s.getConditionVariable());
+ // C99 6.8.5p2/p4: The first substatement is executed if the
+ // expression compares unequal to 0. The condition must be a
+ // scalar type.
+ condVal = evaluateExprAsBool(s.getCond());
+ builder.createCondition(condVal);
+ },
+ /*bodyBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ // The scope of the while loop body is a nested scope.
+ if (emitStmt(s.getBody(), /*useCurrentScope=*/false).failed())
+ loopRes = mlir::failure();
+ emitStopPoint(&s);
+ });
+ return loopRes;
+ };
+
+ mlir::LogicalResult res = mlir::success();
+ mlir::Location scopeLoc = getLoc(s.getSourceRange());
+ builder.create<cir::ScopeOp>(scopeLoc, /*scopeBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ LexicalScope lexScope{
+ *this, loc, builder.getInsertionBlock()};
+ res = whileStmtBuilder();
+ });
+
+ if (res.failed())
+ return res;
+
+ terminateBody(builder, whileOp.getBody(), getLoc(s.getEndLoc()));
+ return mlir::success();
+}
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index cdcfa77b66379..94d52521e38d5 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -539,9 +539,29 @@ Block *cir::BrCondOp::getSuccessorForOperands(ArrayRef<Attribute> operands) {
}
//===----------------------------------------------------------------------===//
-// ForOp
+// LoopOpInterface Methods
//===----------------------------------------------------------------------===//
+void cir::DoWhileOp::getSuccessorRegions(
+ mlir::RegionBranchPoint point,
+ llvm::SmallVectorImpl<mlir::RegionSuccessor> ®ions) {
+ LoopOpInterface::getLoopOpSuccessorRegions(*this, point, regions);
+}
+
+llvm::SmallVector<Region *> cir::DoWhileOp::getLoopRegions() {
+ return {&getBody()};
+}
+
+void cir::WhileOp::getSuccessorRegions(
+ mlir::RegionBranchPoint point,
+ llvm::SmallVectorImpl<mlir::RegionSuccessor> ®ions) {
+ LoopOpInterface::getLoopOpSuccessorRegions(*this, point, regions);
+}
+
+llvm::SmallVector<Region *> cir::WhileOp::getLoopRegions() {
+ return {&getBody()};
+}
+
void cir::ForOp::getSuccessorRegions(
mlir::RegionBranchPoint point,
llvm::SmallVectorImpl<mlir::RegionSuccessor> ®ions) {
diff --git a/clang/test/CIR/CodeGen/loop.cpp b/clang/test/CIR/CodeGen/loop.cpp
index f0b570a92964d..a950460e8838d 100644
--- a/clang/test/CIR/CodeGen/loop.cpp
+++ b/clang/test/CIR/CodeGen/loop.cpp
@@ -189,3 +189,79 @@ void l3() {
// OGCG: [[FOR_COND]]:
// OGCG: store i32 0, ptr %[[I]], align 4
// OGCG: br label %[[FOR_COND]]
+
+void test_do_while_false() {
+ do {
+ } while (0);
+}
+
+// CIR: cir.func @test_do_while_false()
+// CIR-NEXT: cir.scope {
+// CIR-NEXT: cir.do {
+// CIR-NEXT: cir.yield
+// CIR-NEXT: } while {
+// CIR-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CIR-NEXT: %[[FALSE:.*]] = cir.cast(int_to_bool, %[[ZERO]] : !s32i), !cir.bool
+// CIR-NEXT: cir.condition(%[[FALSE]])
+
+// LLVM: define void @test_do_while_false()
+// LLVM: br label %[[LABEL1:.*]]
+// LLVM: [[LABEL1]]:
+// LLVM: br label %[[LABEL3:.*]]
+// LLVM: [[LABEL2:.*]]:
+// LLVM: br i1 false, label %[[LABEL3]], label %[[LABEL4:.*]]
+// LLVM: [[LABEL3]]:
+// LLVM: br label %[[LABEL2]]
+// LLVM: [[LABEL4]]:
+// LLVM: br label %[[LABEL5:.*]]
+// LLVM: [[LABEL5]]:
+// LLVM: ret void
+
+// OGCG: define{{.*}} void @_Z19test_do_while_falsev()
+// OGCG: entry:
+// OGCG: br label %[[DO_BODY:.*]]
+// OGCG: [[DO_BODY]]:
+// OGCG: br label %[[DO_END:.*]]
+// OGCG: [[DO_END]]:
+// OGCG: ret void
+
+void test_empty_while_true() {
+ while (true) {
+ return;
+ }
+}
+
+// CIR: cir.func @test_empty_while_true()
+// CIR-NEXT: cir.scope {
+// CIR-NEXT: cir.while {
+// CIR-NEXT: %[[TRUE:.*]] = cir.const #true
+// CIR-NEXT: cir.condition(%[[TRUE]])
+// CIR-NEXT: } do {
+// CIR-NEXT: cir.scope {
+// CIR-NEXT: cir.return
+// CIR-NEXT: }
+// CIR-NEXT: cir.yield
+
+// LLVM: define void @test_empty_while_true()
+// LLVM: br label %[[LABEL1:.*]]
+// LLVM: [[LABEL1]]:
+// LLVM: br label %[[LABEL2:.*]]
+// LLVM: [[LABEL2]]:
+// LLVM: br i1 true, label %[[LABEL3:.*]], label %[[LABEL6:.*]]
+// LLVM: [[LABEL3]]:
+// LLVM: br label %[[LABEL4]]
+// LLVM: [[LABEL4]]:
+// LLVM: ret void
+// LLVM: [[LABEL5:.*]]:
+// LLVM-SAME: ; No predecessors!
+// LLVM: br label %[[LABEL2:.*]]
+// LLVM: [[LABEL6]]:
+// LLVM: br label %[[LABEL7:.*]]
+// LLVM: [[LABEL7]]:
+// LLVM: ret void
+
+// OGCG: define{{.*}} void @_Z21test_empty_while_truev()
+// OGCG: entry:
+// OGCG: br label %[[WHILE_BODY:.*]]
+// OGCG: [[WHILE_BODY]]:
+// OGCG: ret void
diff --git a/clang/test/CIR/Transforms/loop.cir b/clang/test/CIR/Transforms/loop.cir
index 4fde3a7bb43f1..d02412d049158 100644
--- a/clang/test/CIR/Transforms/loop.cir
+++ b/clang/test/CIR/Transforms/loop.cir
@@ -26,4 +26,45 @@ module {
// CHECK: cir.br ^bb[[#COND:]]
// CHECK: ^bb[[#EXIT]]:
// CHECK: cir.return
+// CHECK: }
+
+ // Test while cir.loop operation lowering.
+ cir.func @testWhile(%arg0 : !cir.bool) {
+ cir.while {
+ cir.condition(%arg0)
+ } do {
+ cir.yield
+ }
+ cir.return
+ }
+
+// CHECK: cir.func @testWhile(%arg0: !cir.bool) {
+// CHECK: cir.br ^bb[[#COND:]]
+// CHECK: ^bb[[#COND]]:
+// CHECK: cir.brcond %arg0 ^bb[[#BODY:]], ^bb[[#EXIT:]]
+// CHECK: ^bb[[#BODY]]:
+// CHECK: cir.br ^bb[[#COND:]]
+// CHECK: ^bb[[#EXIT]]:
+// CHECK: cir.return
+// CHECK: }
+
+
+ // Test do-while cir.loop operation lowering.
+ cir.func @testDoWhile(%arg0 : !cir.bool) {
+ cir.do {
+ cir.yield
+ } while {
+ cir.condition(%arg0)
+ }
+ cir.return
+ }
+
+// CHECK: cir.func @testDoWhile(%arg0: !cir.bool) {
+// CHECK: cir.br ^bb[[#BODY:]]
+// CHECK: ^bb[[#COND]]:
+// CHECK: cir.brcond %arg0 ^bb[[#BODY:]], ^bb[[#EXIT:]]
+// CHECK: ^bb[[#BODY]]:
+// CHECK: cir.br ^bb[[#COND:]]
+// CHECK: ^bb[[#EXIT]]:
+// CHECK: cir.return
// CHECK: }
``````````
</details>
https://github.com/llvm/llvm-project/pull/133157
More information about the cfe-commits
mailing list