Author: Andy Kaylor Date: 2025-04-01T13:03:24-07:00 New Revision: 9f3d8e8fb8d389176e12c06de59cce3fd1ab8db2
URL: https://github.com/llvm/llvm-project/commit/9f3d8e8fb8d389176e12c06de59cce3fd1ab8db2 DIFF: https://github.com/llvm/llvm-project/commit/9f3d8e8fb8d389176e12c06de59cce3fd1ab8db2.diff LOG: [CIR] Upstream support for while and do..while loops (#133157) This adds basic support for while and do..while loops. Support for break and continue are left for a subsequent patch. Added: Modified: clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h clang/include/clang/CIR/Dialect/IR/CIRDialect.h clang/include/clang/CIR/Dialect/IR/CIROps.td clang/lib/CIR/CodeGen/CIRGenFunction.h clang/lib/CIR/CodeGen/CIRGenStmt.cpp clang/lib/CIR/Dialect/IR/CIRDialect.cpp clang/test/CIR/CodeGen/loop.cpp clang/test/CIR/Transforms/loop.cir Removed: ################################################################################ diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 8b17cb7446afa..e666be0b25d75 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -111,6 +111,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/CIRDialect.h b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h index da3b41371b9ab..4d7f537418a90 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h @@ -32,6 +32,9 @@ #include "clang/CIR/Interfaces/CIRLoopOpInterface.h" #include "clang/CIR/Interfaces/CIROpInterfaces.h" +using BuilderCallbackRef = + llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>; + // TableGen'erated files for MLIR dialects require that a macro be defined when // they are included. GET_OP_CLASSES tells the file to define the classes for // the operations of that dialect. diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 455cc2b8b0277..3965372755685 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 diff erent CIR operations, @@ -759,11 +761,106 @@ def BrCondOp : CIR_Op<"brcond", }]; } +//===----------------------------------------------------------------------===// +// Common loop op definitions +//===----------------------------------------------------------------------===// + +class LoopOpBase<string mnemonic> : CIR_Op<mnemonic, [ + LoopOpInterface, + NoRegionArguments, +]> { + let extraClassDefinition = [{ + void $cppClass::getSuccessorRegions( + mlir::RegionBranchPoint point, + llvm::SmallVectorImpl<mlir::RegionSuccessor> ®ions) { + LoopOpInterface::getLoopOpSuccessorRegions(*this, point, regions); + } + llvm::SmallVector<Region *> $cppClass::getLoopRegions() { + return {&getBody()}; + } + }]; +} + +//===----------------------------------------------------------------------===// +// While & DoWhileOp +//===----------------------------------------------------------------------===// + +class WhileOpBase<string mnemonic> : LoopOpBase<mnemonic> { + defvar isWhile = !eq(mnemonic, "while"); + let summary = "C/C++ " # !if(isWhile, "while", "do-while") # " loop"; + let builders = [ + OpBuilder<(ins "BuilderCallbackRef":$condBuilder, + "BuilderCallbackRef":$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 //===----------------------------------------------------------------------===// -def ForOp : CIR_Op<"for", [LoopOpInterface, NoRegionArguments]> { +def ForOp : LoopOpBase<"for"> { let summary = "C/C++ for loop counterpart"; let description = [{ Represents a C/C++ for loop. It consists of three regions: diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 3b8171eea9ee0..5cae4d5da9516 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 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 @@ -493,6 +495,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..4ace083e3c081 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -538,20 +538,6 @@ Block *cir::BrCondOp::getSuccessorForOperands(ArrayRef<Attribute> operands) { return nullptr; } -//===----------------------------------------------------------------------===// -// ForOp -//===----------------------------------------------------------------------===// - -void cir::ForOp::getSuccessorRegions( - mlir::RegionBranchPoint point, - llvm::SmallVectorImpl<mlir::RegionSuccessor> ®ions) { - LoopOpInterface::getLoopOpSuccessorRegions(*this, point, regions); -} - -llvm::SmallVector<Region *> cir::ForOp::getLoopRegions() { - return {&getBody()}; -} - //===----------------------------------------------------------------------===// // GlobalOp //===----------------------------------------------------------------------===// 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: } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits