Skip to content

Commit f786bc8

Browse files
sitio-coutolanza
authored andcommitted
[CIR][IR] Deprecate cir.yield nosuspend
This changes the `cir.await` operation to expect a `cir.condition` as the terminator for the ready region. This simplifies the `cir.await` while also simplifying the `cir.yield`. If `cir.condition` holds a true value, then the `cir.await` will continue the coroutine, otherwise, it will suspend its execution. The `cir.condition` op was also updated to allow `cir.await` as its parent operation. ghstack-source-id: 1ebeb2c Pull Request resolved: #396
1 parent 759a192 commit f786bc8

File tree

7 files changed

+80
-99
lines changed

7 files changed

+80
-99
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -593,25 +593,50 @@ def ConditionOp : CIR_Op<"condition", [
593593
]> {
594594
let summary = "Loop continuation condition.";
595595
let description = [{
596-
The `cir.condition` termintes loop's conditional regions. It takes a single
597-
`cir.bool` operand. if the operand is true, the loop continues, otherwise
598-
it terminates.
596+
The `cir.condition` terminates conditional regions. It takes a single
597+
`cir.bool` operand and, depending on its value, may branch to different
598+
regions:
599+
600+
- When in the `cond` region of a `cir.loop`, it continues the loop
601+
if true, or exits it if false.
602+
- When in the `ready` region of a `cir.await`, it branches to the `resume`
603+
region when true, and to the `suspend` region when false.
604+
605+
Example:
606+
607+
```mlir
608+
cir.loop for(cond : {
609+
cir.condition(%arg0) // Branches to `step` region or exits.
610+
}, step : {
611+
[...]
612+
}) {
613+
[...]
614+
}
615+
616+
cir.await(user, ready : {
617+
cir.condition(%arg0) // Branches to `resume` or `suspend` region.
618+
}, suspend : {
619+
[...]
620+
}, resume : {
621+
[...]
622+
},)
623+
```
599624
}];
600625
let arguments = (ins CIR_BoolType:$condition);
601626
let assemblyFormat = " `(` $condition `)` attr-dict ";
627+
let hasVerifier = 1;
602628
}
603629

604630
//===----------------------------------------------------------------------===//
605631
// YieldOp
606632
//===----------------------------------------------------------------------===//
607633

608634
def YieldOpKind_FT : I32EnumAttrCase<"Fallthrough", 2, "fallthrough">;
609-
def YieldOpKind_NS : I32EnumAttrCase<"NoSuspend", 4, "nosuspend">;
610635

611636
def YieldOpKind : I32EnumAttr<
612637
"YieldOpKind",
613638
"yield kind",
614-
[YieldOpKind_FT, YieldOpKind_NS]> {
639+
[YieldOpKind_FT]> {
615640
let cppNamespace = "::mlir::cir";
616641
}
617642

@@ -630,8 +655,6 @@ def YieldOp : CIR_Op<"yield", [ReturnLike, Terminator,
630655
Optionally, `cir.yield` can be annotated with extra kind specifiers:
631656
- `fallthrough`: execution falls to the next region in `cir.switch` case list.
632657
Only available inside `cir.switch` regions.
633-
- `nosuspend`: specific to the `ready` region inside `cir.await` op, it makes
634-
control-flow to be transfered back to the parent, preventing suspension.
635658

636659
As a general rule, `cir.yield` must be explicitly used whenever a region has
637660
more than one block and no terminator, or within `cir.switch` regions not
@@ -651,16 +674,6 @@ def YieldOp : CIR_Op<"yield", [ReturnLike, Terminator,
651674
}, ...
652675
]
653676

654-
cir.await(init, ready : {
655-
// Call std::suspend_always::await_ready
656-
%18 = cir.call @_ZNSt14suspend_always11await_readyEv(...)
657-
cir.if %18 {
658-
// yields back to the parent.
659-
cir.yield nosuspend
660-
}
661-
cir.yield // control-flow to the next region for suspension.
662-
}, ...)
663-
664677
cir.scope {
665678
...
666679
cir.yield
@@ -704,9 +717,6 @@ def YieldOp : CIR_Op<"yield", [ReturnLike, Terminator,
704717
bool isFallthrough() {
705718
return !isPlain() && *getKind() == YieldOpKind::Fallthrough;
706719
}
707-
bool isNoSuspend() {
708-
return !isPlain() && *getKind() == YieldOpKind::NoSuspend;
709-
}
710720
}];
711721

712722
let hasVerifier = 1;

clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -428,28 +428,8 @@ buildSuspendExpression(CIRGenFunction &CGF, CGCoroData &Coro,
428428
CGF.getLoc(S.getSourceRange()), Kind,
429429
/*readyBuilder=*/
430430
[&](mlir::OpBuilder &b, mlir::Location loc) {
431-
auto *cond = S.getReadyExpr();
432-
cond = cond->IgnoreParens();
433-
mlir::Value condV = CGF.evaluateExprAsBool(cond);
434-
435-
builder.create<mlir::cir::IfOp>(
436-
loc, condV, /*withElseRegion=*/false,
437-
/*thenBuilder=*/
438-
[&](mlir::OpBuilder &b, mlir::Location loc) {
439-
// If expression is ready, no need to suspend,
440-
// `YieldOpKind::NoSuspend` tells control flow to return to
441-
// parent, no more regions to be executed.
442-
builder.create<mlir::cir::YieldOp>(
443-
loc, mlir::cir::YieldOpKind::NoSuspend);
444-
});
445-
446-
if (!condV) {
447-
awaitBuild = mlir::failure();
448-
return;
449-
}
450-
451-
// Signals the parent that execution flows to next region.
452-
builder.create<mlir::cir::YieldOp>(loc);
431+
Expr *condExpr = S.getReadyExpr()->IgnoreParens();
432+
builder.createCondition(CGF.evaluateExprAsBool(condExpr));
453433
},
454434
/*suspendBuilder=*/
455435
[&](mlir::OpBuilder &b, mlir::Location loc) {

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 23 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
1515
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
1616
#include "clang/CIR/Dialect/IR/CIRTypes.h"
17+
#include "llvm/Support/ErrorHandling.h"
1718
#include <optional>
1819

1920
#include "mlir/Dialect/Func/IR/FuncOps.h"
@@ -246,13 +247,19 @@ LogicalResult BreakOp::verify() {
246247

247248
void ConditionOp::getSuccessorRegions(
248249
ArrayRef<Attribute> operands, SmallVectorImpl<RegionSuccessor> &regions) {
249-
auto loopOp = cast<LoopOp>(getOperation()->getParentOp());
250-
251250
// TODO(cir): The condition value may be folded to a constant, narrowing
252251
// down its list of possible successors.
253-
// Condition may branch to the body or to the parent op.
254-
regions.emplace_back(&loopOp.getBody(), loopOp.getBody().getArguments());
255-
regions.emplace_back(loopOp->getResults());
252+
253+
// Parent is a loop: condition may branch to the body or to the parent op.
254+
if (auto loopOp = dyn_cast<LoopOp>(getOperation()->getParentOp())) {
255+
regions.emplace_back(&loopOp.getBody(), loopOp.getBody().getArguments());
256+
regions.emplace_back(loopOp->getResults());
257+
}
258+
259+
// Parent is an await: condition may branch to resume or suspend regions.
260+
auto await = cast<AwaitOp>(getOperation()->getParentOp());
261+
regions.emplace_back(&await.getResume(), await.getResume().getArguments());
262+
regions.emplace_back(&await.getSuspend(), await.getSuspend().getArguments());
256263
}
257264

258265
MutableOperandRange
@@ -261,6 +268,12 @@ ConditionOp::getMutableSuccessorOperands(RegionBranchPoint point) {
261268
return MutableOperandRange(getOperation(), 0, 0);
262269
}
263270

271+
LogicalResult ConditionOp::verify() {
272+
if (!isa<LoopOp, AwaitOp>(getOperation()->getParentOp()))
273+
return emitOpError("condition must be within a conditional region");
274+
return success();
275+
}
276+
264277
//===----------------------------------------------------------------------===//
265278
// ConstantOp
266279
//===----------------------------------------------------------------------===//
@@ -786,34 +799,6 @@ void TernaryOp::build(OpBuilder &builder, OperationState &result, Value cond,
786799
//===----------------------------------------------------------------------===//
787800

788801
mlir::LogicalResult YieldOp::verify() {
789-
auto isDominatedByProperAwaitRegion = [&](Operation *parentOp,
790-
mlir::Region *currRegion) {
791-
while (!llvm::isa<cir::FuncOp>(parentOp)) {
792-
auto awaitOp = dyn_cast<cir::AwaitOp>(parentOp);
793-
if (awaitOp) {
794-
if (currRegion && currRegion == &awaitOp.getResume()) {
795-
emitOpError() << "kind 'nosuspend' can only be used in 'ready' and "
796-
"'suspend' regions";
797-
return false;
798-
}
799-
return true;
800-
}
801-
802-
currRegion = parentOp->getParentRegion();
803-
parentOp = parentOp->getParentOp();
804-
}
805-
806-
emitOpError() << "shall be dominated by 'cir.await'";
807-
return false;
808-
};
809-
810-
if (isNoSuspend()) {
811-
if (!isDominatedByProperAwaitRegion(getOperation()->getParentOp(),
812-
getOperation()->getParentRegion()))
813-
return mlir::failure();
814-
return mlir::success();
815-
}
816-
817802
if (isFallthrough()) {
818803
if (!llvm::isa<SwitchOp>(getOperation()->getParentOp()))
819804
return emitOpError() << "fallthrough only expected within 'cir.switch'";
@@ -2223,7 +2208,11 @@ void AwaitOp::getSuccessorRegions(mlir::RegionBranchPoint point,
22232208
regions.push_back(RegionSuccessor(&this->getResume()));
22242209
}
22252210

2226-
LogicalResult AwaitOp::verify() { return success(); }
2211+
LogicalResult AwaitOp::verify() {
2212+
if (!isa<ConditionOp>(this->getReady().back().getTerminator()))
2213+
return emitOpError("ready region must end with cir.condition");
2214+
return success();
2215+
}
22272216

22282217
//===----------------------------------------------------------------------===//
22292218
// CIR defined traits

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,8 +1391,6 @@ class CIRSwitchOpLowering
13911391
case mlir::cir::YieldOpKind::Fallthrough:
13921392
fallthroughYieldOp = yieldOp;
13931393
break;
1394-
default:
1395-
return op->emitError("invalid yield kind in case statement");
13961394
}
13971395
}
13981396
}

clang/test/CIR/CodeGen/coro-task.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,7 @@ VoidTask silly_task() {
206206
// CHECK: %[[#TmpCallRes:]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[#SuspendAlwaysAddr]])
207207
// CHECK: cir.yield %[[#TmpCallRes]] : !cir.bool
208208
// CHECK: }
209-
// CHECK: cir.if %[[#ReadyVeto]] {
210-
// CHECK: cir.yield nosuspend
211-
// CHECK: }
212-
// CHECK: cir.yield
209+
// CHECK: cir.condition(%[[#ReadyVeto]])
213210

214211
// Second region `suspend` contains the actual suspend logic.
215212
//

clang/test/CIR/IR/await.cir

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: cir-opt %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
4+
cir.func coroutine @checkPrintParse(%arg0 : !cir.bool) {
5+
cir.await(user, ready : {
6+
cir.condition(%arg0)
7+
}, suspend : {
8+
cir.yield
9+
}, resume : {
10+
cir.yield
11+
},)
12+
cir.return
13+
}
14+
15+
// CHECK: cir.func coroutine @checkPrintParse
16+
// CHECK: cir.await(user, ready : {
17+
// CHECK: cir.condition(%arg0)
18+
// CHECK: }, suspend : {
19+
// CHECK: cir.yield
20+
// CHECK: }, resume : {
21+
// CHECK: cir.yield
22+
// CHECK: },)

clang/test/CIR/IR/invalid.cir

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -494,27 +494,12 @@ cir.func coroutine @bad_task() { // expected-error {{coroutine body must use at
494494

495495
// -----
496496

497-
cir.func coroutine @bad_yield() {
497+
cir.func coroutine @missing_condition() {
498498
cir.scope {
499-
cir.await(user, ready : {
499+
cir.await(user, ready : { // expected-error {{ready region must end with cir.condition}}
500500
cir.yield
501501
}, suspend : {
502502
cir.yield
503-
}, resume : {
504-
cir.yield nosuspend // expected-error {{kind 'nosuspend' can only be used in 'ready' and 'suspend' regions}}
505-
},)
506-
}
507-
cir.return
508-
}
509-
510-
// -----
511-
512-
cir.func coroutine @good_yield() {
513-
cir.scope {
514-
cir.await(user, ready : {
515-
cir.yield nosuspend
516-
}, suspend : {
517-
cir.yield nosuspend
518503
}, resume : {
519504
cir.yield
520505
},)

0 commit comments

Comments
 (0)