[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:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
9e8fdef17c
commit
54e607854a
@@ -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}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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* {
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user