Skip to content

[CIR] Upstream support for while and do..while loops #133157

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 1, 2025

Conversation

andykaylor
Copy link
Contributor

This adds basic support for while and do..while loops. Support for break and continue are left for a subsequent patch.

This adds basic support for while and do..while loops. Support for break
and continue are left for a subsequent patch.
@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Mar 26, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 26, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clangir

Author: Andy Kaylor (andykaylor)

Changes

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 --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> &regions) {
+  LoopOpInterface::getLoopOpSuccessorRegions(*this, point, regions);
+}
+
+llvm::SmallVector<Region *> cir::DoWhileOp::getLoopRegions() {
+  return {&getBody()};
+}
+
+void cir::WhileOp::getSuccessorRegions(
+    mlir::RegionBranchPoint point,
+    llvm::SmallVectorImpl<mlir::RegionSuccessor> &regions) {
+  LoopOpInterface::getLoopOpSuccessorRegions(*this, point, regions);
+}
+
+llvm::SmallVector<Region *> cir::WhileOp::getLoopRegions() {
+  return {&getBody()};
+}
+
 void cir::ForOp::getSuccessorRegions(
     mlir::RegionBranchPoint point,
     llvm::SmallVectorImpl<mlir::RegionSuccessor> &regions) {
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:  }

@andykaylor
Copy link
Contributor Author

@mmha

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

Looks straight-forward to me, one minor nit!

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,
Copy link
Member

Choose a reason for hiding this comment

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

Might be worth trying to fit 80-col here, tho it looks hard to get something

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, we haven't been fitting the .td files in 80 columns, though I have also thought we probably should do so.

It looks like tablegen will accept it like this:

  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);
      }])>
  ];

What do you think? Is that readable?

Copy link
Contributor

Choose a reason for hiding this comment

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

Other option is to add type alias. Something like BuilderCallbackRef.

Then you need to define it before CIROps.h.inc is included in CIRDialect.h:

using BuilderCallbackRef =  llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>;

This turned out to be useful in vast on multiple places to make builders more readable.

Copy link
Member

Choose a reason for hiding this comment

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

What do you think? Is that readable?

Works for me!

Copy link
Member

Choose a reason for hiding this comment

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

I'm also fine if Henrich suggestion comes as a follow up PR!

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,
Copy link
Contributor

Choose a reason for hiding this comment

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

Other option is to add type alias. Something like BuilderCallbackRef.

Then you need to define it before CIROps.h.inc is included in CIRDialect.h:

using BuilderCallbackRef =  llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>;

This turned out to be useful in vast on multiple places to make builders more readable.

Comment on lines 545 to 563
void cir::DoWhileOp::getSuccessorRegions(
mlir::RegionBranchPoint point,
llvm::SmallVectorImpl<mlir::RegionSuccessor> &regions) {
LoopOpInterface::getLoopOpSuccessorRegions(*this, point, regions);
}

llvm::SmallVector<Region *> cir::DoWhileOp::getLoopRegions() {
return {&getBody()};
}

void cir::WhileOp::getSuccessorRegions(
mlir::RegionBranchPoint point,
llvm::SmallVectorImpl<mlir::RegionSuccessor> &regions) {
LoopOpInterface::getLoopOpSuccessorRegions(*this, point, regions);
}

llvm::SmallVector<Region *> cir::WhileOp::getLoopRegions() {
return {&getBody()};
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can be deduplicated as extraClassDefinition in WhileOpBase as:

let extraClassDefinition = [{
void $cppClass::getSuccessorRegions(
    mlir::RegionBranchPoint point,
    llvm::SmallVectorImpl<mlir::RegionSuccessor> &regions) {
  LoopOpInterface::getLoopOpSuccessorRegions(*this, point, regions);
}
llvm::SmallVector<Region *> $cppClass::getLoopRegions() {
  return {&getBody()};
}
}];

@andykaylor
Copy link
Contributor Author

@bcardosolopes @xlauko Are you happy with this after my latest refactoring?

@bcardosolopes
Copy link
Member

@bcardosolopes @xlauko Are you happy with this after my latest refactoring?

Yep!

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

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

No real concerns here, happy when others are.

@xlauko
Copy link
Contributor

xlauko commented Apr 1, 2025

LGTM!

@andykaylor andykaylor merged commit 9f3d8e8 into llvm:main Apr 1, 2025
11 checks passed
Ankur-0429 pushed a commit to Ankur-0429/llvm-project that referenced this pull request Apr 2, 2025
This adds basic support for while and do..while loops. Support for break
and continue are left for a subsequent patch.
@andykaylor andykaylor deleted the cir-while branch April 10, 2025 21:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants