[modular_aot] Code generation for async/async*/sync*/await/yield/yield*

TEST=ci

Issue: https://github.com/dart-lang/sdk/issues/61635
Change-Id: Ie8da89e09bfad68c65980abc1df69f9273bd6d00
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/497400
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov
2026-04-28 05:45:31 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 9e8fdef17c
commit 54e607854a
20 changed files with 439 additions and 138 deletions
+35 -10
View File
@@ -116,7 +116,7 @@ class AstToIr extends ast.RecursiveVisitor {
}
if (builder.hasOpenBlock) {
builder.addNullConstant();
_buildReturn();
builder.addReturn();
}
return builder.done();
}
@@ -152,13 +152,6 @@ class AstToIr extends ast.RecursiveVisitor {
}
}
void _buildReturn() {
if (function.isSuspendable) {
builder.addLeaveSuspendableFunction(function.returnType);
}
builder.addReturn();
}
void _buildImplicitGetter(ast.Field node) {
final field = CField(node);
if (node.isStatic) {
@@ -506,7 +499,7 @@ class AstToIr extends ast.RecursiveVisitor {
final value = builder.pop();
_generateNonLocalControlTransfer(node, null, () {
builder.push(value);
_buildReturn();
builder.addReturn();
});
}
@@ -1824,7 +1817,39 @@ class AstToIr extends ast.RecursiveVisitor {
builder.pop();
return;
}
builder.addSuspend(node.isYieldStar ? .yieldStar : .yield, const TopType());
switch (function.asyncMarker) {
case .AsyncStar:
// yield/yield* statement acts as a return statement if subscription
// to the async* Stream is cancelled.
final canceledBlock = builder.newTargetBlock();
final continueBlock = builder.newTargetBlock();
// Suspend will evaluate to true if subscription to async* Stream is cancelled.
builder.addSuspend(
node.isYieldStar ? .asyncYieldStar : .asyncYield,
const BoolType(),
);
builder.addBranch(canceledBlock, continueBlock);
builder.startBlock(canceledBlock);
_generateNonLocalControlTransfer(node, null, () {
builder.addNullConstant();
builder.addReturn();
});
builder.startBlock(continueBlock);
break;
case .SyncStar:
builder.addSuspend(
node.isYieldStar ? .syncYieldStar : .syncYield,
const TopType(),
);
break;
default:
throw 'Unexpected YieldStatement in $function with ${function.asyncMarker}';
}
}
}
-13
View File
@@ -622,19 +622,6 @@ class FlowGraphBuilder {
appendInstruction(instr);
}
/// Append [LeaveSuspendableFunction] to the graph.
void addLeaveSuspendableFunction(CType type) {
final returnValue = pop();
final instr = LeaveSuspendableFunction(
graph,
currentSourcePosition,
type,
returnValue,
);
push(instr);
appendInstruction(instr);
}
/// Append [Suspend] to the graph.
Suspend addSuspend(SuspendOpcode op, CType type) {
final typeArguments = (op == .awaitWithTypeCheck) ? pop() : null;
+5 -7
View File
@@ -413,11 +413,6 @@ final class FlowGraphChecker extends Pass implements InstructionVisitor<void> {
verifyTypeArgumentsInput(instr.typeArguments, instr);
}
@override
void visitLeaveSuspendableFunction(LeaveSuspendableFunction instr) {
assert(graph.function.isSuspendable);
}
@override
void visitSuspend(Suspend instr) {
assert(graph.function.isSuspendable);
@@ -426,8 +421,11 @@ final class FlowGraphChecker extends Pass implements InstructionVisitor<void> {
case .await || .awaitWithTypeCheck:
assert(asyncMarker == .Async || asyncMarker == .AsyncStar);
break;
case .yield || .yieldStar:
assert(asyncMarker == .AsyncStar || asyncMarker == .SyncStar);
case .asyncYield || .asyncYieldStar:
assert(asyncMarker == .AsyncStar);
break;
case .syncYield || .syncYieldStar:
assert(asyncMarker == .SyncStar);
break;
}
if (instr.op == .awaitWithTypeCheck) {
+9 -21
View File
@@ -605,6 +605,8 @@ final class Return extends Instruction
setInputAt(0, value);
}
Definition get value => inputDefAt(0);
@override
R accept<R>(InstructionVisitor<R> v) => v.visitReturn(this);
}
@@ -1412,29 +1414,15 @@ final class EnterSuspendableFunction extends Instruction
R accept<R>(InstructionVisitor<R> v) => v.visitEnterSuspendableFunction(this);
}
/// Leave a suspendable function.
final class LeaveSuspendableFunction extends Definition
with CanThrow, HasSideEffects {
@override
final CType type;
LeaveSuspendableFunction(
super.graph,
super.sourcePosition,
this.type,
Definition returnValue,
) : super(inputCount: 1) {
setInputAt(0, returnValue);
}
Definition get returnValue => inputDefAt(0);
@override
R accept<R>(InstructionVisitor<R> v) => v.visitLeaveSuspendableFunction(this);
enum SuspendOpcode {
await,
awaitWithTypeCheck,
asyncYield,
asyncYieldStar,
syncYield,
syncYieldStar,
}
enum SuspendOpcode { await, awaitWithTypeCheck, yield, yieldStar }
/// A point where execution of a suspendable function can be suspended
/// and resumed (await, yield or yield*).
final class Suspend extends Definition with CanThrow, HasSideEffects {
+33
View File
@@ -75,6 +75,9 @@ sealed class CType {
/// Return non-nullable variant of this type (if possible).
CType get toNonNullableType;
/// Returns true if value of this type can be `Future`.
bool get canBeFuture;
@override
bool operator ==(Object other) =>
other is CType &&
@@ -104,6 +107,9 @@ final class IntType extends CType {
@override
CType get toNonNullableType => this;
@override
bool get canBeFuture => false;
@override
String toString() => 'int';
}
@@ -127,6 +133,9 @@ final class DoubleType extends CType {
@override
CType get toNonNullableType => this;
@override
bool get canBeFuture => false;
@override
String toString() => 'double';
}
@@ -150,6 +159,9 @@ final class BoolType extends CType {
@override
CType get toNonNullableType => this;
@override
bool get canBeFuture => false;
@override
String toString() => 'bool';
}
@@ -173,6 +185,9 @@ final class StringType extends CType {
@override
CType get toNonNullableType => this;
@override
bool get canBeFuture => false;
@override
String toString() => 'String';
}
@@ -196,6 +211,9 @@ final class ObjectType extends CType {
@override
CType get toNonNullableType => this;
@override
bool get canBeFuture => true;
@override
String toString() => 'Object';
}
@@ -216,6 +234,9 @@ final class NullType extends CType {
@override
CType get toNonNullableType => const NeverType();
@override
bool get canBeFuture => false;
@override
String toString() => 'Null';
}
@@ -236,6 +257,9 @@ final class NeverType extends CType {
@override
CType get toNonNullableType => this;
@override
bool get canBeFuture => false;
@override
String toString() => 'Never';
}
@@ -258,6 +282,9 @@ final class TopType extends CType {
@override
CType get toNonNullableType => const ObjectType();
@override
bool get canBeFuture => true;
@override
String toString() => '<top>';
}
@@ -278,6 +305,9 @@ final class StaticType extends CType {
@override
CType get toNonNullableType => CType.fromStaticType(dartType.toNonNull());
@override
bool get canBeFuture => true;
@override
String toString() => dartType.getDisplayString();
}
@@ -301,6 +331,9 @@ sealed class ExtendedType extends CType {
@override
CType get toNonNullableType => this;
@override
bool get canBeFuture => false;
@override
bool operator ==(Object other) =>
other is ExtendedType && this.kind == other.kind;
-3
View File
@@ -44,7 +44,6 @@ abstract interface class InstructionVisitor<R> {
R visitAllocateMapLiteral(AllocateMapLiteral instr);
R visitStringInterpolation(StringInterpolation instr);
R visitEnterSuspendableFunction(EnterSuspendableFunction instr);
R visitLeaveSuspendableFunction(LeaveSuspendableFunction instr);
R visitSuspend(Suspend instr);
R visitBinaryIntOp(BinaryIntOp instr);
R visitUnaryIntOp(UnaryIntOp instr);
@@ -120,8 +119,6 @@ abstract mixin class DefaultInstructionVisitor<R>
defaultInstruction(instr);
R visitEnterSuspendableFunction(EnterSuspendableFunction instr) =>
defaultInstruction(instr);
R visitLeaveSuspendableFunction(LeaveSuspendableFunction instr) =>
defaultInstruction(instr);
R visitSuspend(Suspend instr) => defaultInstruction(instr);
R visitBinaryIntOp(BinaryIntOp instr) => defaultInstruction(instr);
R visitUnaryIntOp(UnaryIntOp instr) => defaultInstruction(instr);
@@ -450,11 +450,6 @@ final class ConstantPropagation extends Pass
@override
void visitEnterSuspendableFunction(EnterSuspendableFunction instr) {}
@override
void visitLeaveSuspendableFunction(LeaveSuspendableFunction instr) {
_setNonConstant(instr);
}
@override
void visitSuspend(Suspend instr) {
_setNonConstant(instr);
-4
View File
@@ -280,10 +280,6 @@ final class Simplification extends Pass
Instruction visitEnterSuspendableFunction(EnterSuspendableFunction instr) =>
instr;
@override
Instruction visitLeaveSuspendableFunction(LeaveSuspendableFunction instr) =>
instr;
@override
Instruction visitSuspend(Suspend instr) => instr;
+14
View File
@@ -23,11 +23,25 @@ Future<void> async5<T>(T x) async {
}
Stream<int> asyncStar1(int a) async* {
print('before');
yield a;
print('after');
}
Stream<int> asyncStar2(Stream<int> a) async* {
print('before');
yield* a;
print('after');
}
Stream<int> asyncStar3(int a) async* {
try {
print('before');
yield a;
print('after');
} finally {
print('finally');
}
}
Iterable<int> syncStar1(int a) sync* {
+64 -26
View File
@@ -3,8 +3,7 @@ B0 = EntryBlock()
v1 = Constant(<int>)
v3 = Constant(42)
EnterSuspendableFunction(v1)
v4 = LeaveSuspendableFunction(v3)
Return(v4)
Return(v3)
--- async2
B0 = EntryBlock()
@@ -15,8 +14,7 @@ B0 = EntryBlock()
v6 = Suspend await(v1)
v8 = Suspend await(v2)
v9 = BinaryIntOp +(v6, v8)
v10 = LeaveSuspendableFunction(v9)
Return(v10)
Return(v9)
--- async3
B0 = EntryBlock()
@@ -27,8 +25,7 @@ B0 = EntryBlock()
EnterSuspendableFunction(v2)
v6 = Suspend awaitWithTypeCheck(v1, v5)
DirectCall print(v6)
v11 = LeaveSuspendableFunction(v10)
Return(v11)
Return(v10)
--- async4
B0 = EntryBlock()
@@ -40,8 +37,7 @@ B0 = EntryBlock()
EnterSuspendableFunction(v6)
v8 = TypeArguments(v5, v4, <async4.T%>)
v10 = DirectCall _GrowableList._literal1(v8, v2)
v11 = LeaveSuspendableFunction(v10)
Return(v11)
Return(v10)
--- async5
B0 = EntryBlock()
@@ -54,28 +50,72 @@ B0 = EntryBlock()
v9 = TypeArguments(v8, v4, <async5.T%>)
v10 = Suspend awaitWithTypeCheck(v2, v9)
DirectCall print(v10)
v14 = LeaveSuspendableFunction(v8)
Return(v14)
Return(v8)
--- asyncStar1
B0 = EntryBlock()
B0 = EntryBlock() dominates:(B8, B7)
v2 = Constant(<int>)
v6 = Constant(null)
v4 = Constant("before")
v11 = Constant(null)
v13 = Constant("after")
v1 = Parameter(a)
EnterSuspendableFunction(v2)
Suspend yield(v1)
v7 = LeaveSuspendableFunction(v6)
Return(v7)
DirectCall print(v4)
v9 = Suspend asyncYield(v1)
Branch(v9, true: B7, false: B8)
B7 = TargetBlock() idom:B0
Return(v11)
B8 = TargetBlock() idom:B0
DirectCall print(v13)
Return(v11)
--- asyncStar2
B0 = EntryBlock()
B0 = EntryBlock() dominates:(B8, B7)
v2 = Constant(<int>)
v6 = Constant(null)
v4 = Constant("before")
v11 = Constant(null)
v13 = Constant("after")
v1 = Parameter(a)
EnterSuspendableFunction(v2)
Suspend yieldStar(v1)
v7 = LeaveSuspendableFunction(v6)
Return(v7)
DirectCall print(v4)
v9 = Suspend asyncYieldStar(v1)
Branch(v9, true: B7, false: B8)
B7 = TargetBlock() idom:B0
Return(v11)
B8 = TargetBlock() idom:B0
DirectCall print(v13)
Return(v11)
--- asyncStar3
B0 = EntryBlock() dominates:(B5, B4)
v2 = Constant(<int>)
v7 = Constant("before")
v16 = Constant("after")
v22 = Constant("finally")
v28 = Constant(null)
v1 = Parameter(a)
EnterSuspendableFunction(v2)
TryEntry(try-body: B4, catch-block: B5)
B4 = TargetBlock() exception-handler:B5 idom:B0 dominates:(B11, B10)
DirectCall print(v7)
v12 = Suspend asyncYield(v1)
Branch(v12, true: B10, false: B11)
B10 = TargetBlock() exception-handler:B5 idom:B4 dominates:(B14)
Goto(B14)
B14 = JoinBlock(B10) idom:B10
DirectCall print(v22)
Return(v28)
B11 = TargetBlock() exception-handler:B5 idom:B4 dominates:(B18)
DirectCall print(v16)
Goto(B18)
B18 = JoinBlock(B11) idom:B11
DirectCall print(v22)
Return(v28)
B5 = CatchBlock() idom:B0
v20 = Parameter(#exception)
v21 = Parameter(#stackTrace)
DirectCall print(v22)
Throw(v20, v21)
--- syncStar1
B0 = EntryBlock()
@@ -83,9 +123,8 @@ B0 = EntryBlock()
v6 = Constant(null)
v1 = Parameter(a)
EnterSuspendableFunction(v2)
Suspend yield(v1)
v7 = LeaveSuspendableFunction(v6)
Return(v7)
Suspend syncYield(v1)
Return(v6)
--- syncStar2
B0 = EntryBlock()
@@ -93,9 +132,8 @@ B0 = EntryBlock()
v6 = Constant(null)
v1 = Parameter(a)
EnterSuspendableFunction(v2)
Suspend yieldStar(v1)
v7 = LeaveSuspendableFunction(v6)
Return(v7)
Suspend syncYieldStar(v1)
Return(v6)
--- main
B0 = EntryBlock()