Skip to content

Commit af4da78

Browse files
alexmarkovCommit Bot
authored and
Commit Bot
committed
[vm] New async/async* implementation in JIT mode
The new implementation is based on suspend/resume stubs and doesn't use desugaring of async functions on kernel AST. Previously, new implementation of async/async* was only supported in AOT mode. This change adds all necessary bits for the JIT mode: * Suspending variable-length frames (for unoptimized code). * Handling of Code and pool pointers in Dart stack frames. * OSR. * Deoptimization. * Hot reload. * Debugger. The new implementation is not enabled in JIT mode yet. Design doc: go/compact-async-await. TEST=ci Issue: #48378 Change-Id: I477d6684bdce7cbc1edb179ae2271ff598b7dcc5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/246081 Reviewed-by: Martin Kustermann <[email protected]> Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Alexander Markov <[email protected]> Reviewed-by: Slava Egorov <[email protected]>
1 parent 8f9e40b commit af4da78

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+4142
-2465
lines changed

pkg/front_end/test/fasta/testing/suite.dart

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ import 'package:front_end/src/fasta/util/parser_ast.dart'
6666
import 'package:front_end/src/fasta/util/parser_ast_helper.dart';
6767
import 'package:kernel/ast.dart'
6868
show
69-
AwaitExpression,
7069
BasicLiteral,
7170
Class,
7271
Component,
@@ -87,9 +86,7 @@ import 'package:kernel/ast.dart'
8786
TreeNode,
8887
UnevaluatedConstant,
8988
VariableDeclaration,
90-
Version,
91-
Visitor,
92-
VisitorVoidMixin;
89+
Version;
9390
import 'package:kernel/binary/ast_to_binary.dart' show BinaryPrinter;
9491
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
9592
import 'package:kernel/core_types.dart' show CoreTypes;
@@ -2421,13 +2418,6 @@ class Transform extends Step<ComponentResult, ComponentResult, FastaContext> {
24212418
backendTarget.performModularTransformations = false;
24222419
}
24232420
}
2424-
List<String> errors = VerifyTransformed.verify(component, backendTarget);
2425-
if (errors.isNotEmpty) {
2426-
return new Result<ComponentResult>(
2427-
result,
2428-
context.expectationSet["TransformVerificationError"],
2429-
errors.join('\n'));
2430-
}
24312421
if (backendTarget is TestTarget &&
24322422
backendTarget.hasGlobalTransformation) {
24332423
component =
@@ -2492,34 +2482,6 @@ class Verify extends Step<ComponentResult, ComponentResult, FastaContext> {
24922482
}
24932483
}
24942484

2495-
/// Visitor that checks that the component has been transformed properly.
2496-
// TODO(johnniwinther): Add checks for all nodes that are unsupported after
2497-
// transformation.
2498-
class VerifyTransformed extends Visitor<void> with VisitorVoidMixin {
2499-
final Target target;
2500-
List<String> errors = [];
2501-
2502-
VerifyTransformed(this.target);
2503-
2504-
@override
2505-
void defaultNode(Node node) {
2506-
node.visitChildren(this);
2507-
}
2508-
2509-
@override
2510-
void visitAwaitExpression(AwaitExpression node) {
2511-
if (target is VmTarget) {
2512-
errors.add("ERROR: Untransformed await expression: $node");
2513-
}
2514-
}
2515-
2516-
static List<String> verify(Component component, Target target) {
2517-
VerifyTransformed visitor = new VerifyTransformed(target);
2518-
component.accept(visitor);
2519-
return visitor.errors;
2520-
}
2521-
}
2522-
25232485
mixin TestTarget on Target {
25242486
bool performModularTransformations = false;
25252487

runtime/vm/compiler/assembler/assembler_arm.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1587,7 +1587,7 @@ void Assembler::CheckCodePointer() {
15871587
target::Instructions::HeaderSize() - kHeapObjectTag;
15881588
mov(R0, Operand(PC));
15891589
AddImmediate(R0, -offset);
1590-
ldr(IP, FieldAddress(CODE_REG, target::Code::saved_instructions_offset()));
1590+
ldr(IP, FieldAddress(CODE_REG, target::Code::instructions_offset()));
15911591
cmp(R0, Operand(IP));
15921592
b(&instructions_ok, EQ);
15931593
bkpt(1);
@@ -3308,6 +3308,10 @@ void Assembler::Ret(Condition cond /* = AL */) {
33083308
READS_RETURN_ADDRESS_FROM_LR(bx(LR, cond));
33093309
}
33103310

3311+
void Assembler::SetReturnAddress(Register value) {
3312+
RESTORES_RETURN_ADDRESS_FROM_REGISTER_TO_LR(MoveRegister(LR, value));
3313+
}
3314+
33113315
void Assembler::ReserveAlignedFrameSpace(intptr_t frame_space) {
33123316
// Reserve space for arguments and align frame before entering
33133317
// the C++ world.

runtime/vm/compiler/assembler/assembler_arm.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,11 @@ class Assembler : public AssemblerBase {
13001300
void EnterFrame(RegList regs, intptr_t frame_space);
13011301
void LeaveFrame(RegList regs, bool allow_pop_pc = false);
13021302
void Ret(Condition cond = AL);
1303+
1304+
// Sets the return address to [value] as if there was a call.
1305+
// On ARM sets LR.
1306+
void SetReturnAddress(Register value);
1307+
13031308
void ReserveAlignedFrameSpace(intptr_t frame_space);
13041309

13051310
// In debug mode, this generates code to check that:

runtime/vm/compiler/assembler/assembler_arm64.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1540,7 +1540,7 @@ void Assembler::CheckCodePointer() {
15401540
const intptr_t entry_offset =
15411541
CodeSize() + target::Instructions::HeaderSize() - kHeapObjectTag;
15421542
adr(R0, Immediate(-entry_offset));
1543-
ldr(TMP, FieldAddress(CODE_REG, target::Code::saved_instructions_offset()));
1543+
ldr(TMP, FieldAddress(CODE_REG, target::Code::instructions_offset()));
15441544
cmp(R0, Operand(TMP));
15451545
b(&instructions_ok, EQ);
15461546
brk(1);
@@ -1583,6 +1583,10 @@ void Assembler::RestoreCSP() {
15831583
mov(CSP, SP);
15841584
}
15851585

1586+
void Assembler::SetReturnAddress(Register value) {
1587+
RESTORES_RETURN_ADDRESS_FROM_REGISTER_TO_LR(MoveRegister(LR, value));
1588+
}
1589+
15861590
void Assembler::EnterFrame(intptr_t frame_size) {
15871591
SPILLS_LR_TO_FRAME(PushPair(FP, LR)); // low: FP, high: LR.
15881592
mov(FP, SP);

runtime/vm/compiler/assembler/assembler_arm64.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2121,6 +2121,10 @@ class Assembler : public AssemblerBase {
21212121
void LeaveFrame();
21222122
void Ret() { ret(); }
21232123

2124+
// Sets the return address to [value] as if there was a call.
2125+
// On ARM64 sets LR.
2126+
void SetReturnAddress(Register value);
2127+
21242128
// Emit code to transition between generated mode and native mode.
21252129
//
21262130
// These require and ensure that CSP and SP are equal and aligned and require

runtime/vm/compiler/assembler/assembler_ia32.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,13 @@ class Assembler : public AssemblerBase {
573573
*/
574574

575575
void Ret() { ret(); }
576+
577+
// Sets the return address to [value] as if there was a call.
578+
// On IA32 pushes [value].
579+
void SetReturnAddress(Register value) {
580+
PushRegister(value);
581+
}
582+
576583
void CompareRegisters(Register a, Register b);
577584
void CompareObjectRegisters(Register a, Register b) {
578585
CompareRegisters(a, b);

runtime/vm/compiler/assembler/assembler_riscv.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2727,6 +2727,9 @@ void Assembler::AddImmediate(Register rd,
27272727
Register rs1,
27282728
intx_t imm,
27292729
OperandSize sz) {
2730+
if ((imm == 0) && (rd == rs1)) {
2731+
return;
2732+
}
27302733
if (IsITypeImm(imm)) {
27312734
addi(rd, rs1, imm);
27322735
} else {
@@ -3646,7 +3649,7 @@ void Assembler::CheckCodePointer() {
36463649
intx_t hi = (imm - lo) << (XLEN - 32) >> (XLEN - 32);
36473650
auipc(TMP, hi);
36483651
addi(TMP, TMP, lo);
3649-
lx(TMP2, FieldAddress(CODE_REG, target::Code::saved_instructions_offset()));
3652+
lx(TMP2, FieldAddress(CODE_REG, target::Code::instructions_offset()));
36503653
beq(TMP, TMP2, &instructions_ok, kNearJump);
36513654
ebreak();
36523655
Bind(&instructions_ok);

runtime/vm/compiler/assembler/assembler_riscv.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,12 @@ class Assembler : public MicroAssembler {
12311231
void LeaveFrame();
12321232
void Ret() { ret(); }
12331233

1234+
// Sets the return address to [value] as if there was a call.
1235+
// On RISC-V sets RA.
1236+
void SetReturnAddress(Register value) {
1237+
mv(RA, value);
1238+
}
1239+
12341240
// Emit code to transition between generated mode and native mode.
12351241
//
12361242
// These require and ensure that CSP and SP are equal and aligned and require

runtime/vm/compiler/assembler/assembler_x64.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2069,7 +2069,7 @@ void Assembler::CheckCodePointer() {
20692069
leaq(RAX, Address::AddressRIPRelative(-header_to_rip_offset));
20702070
ASSERT(CodeSize() == (header_to_rip_offset - header_to_entry_offset));
20712071
}
2072-
cmpq(RAX, FieldAddress(CODE_REG, target::Code::saved_instructions_offset()));
2072+
cmpq(RAX, FieldAddress(CODE_REG, target::Code::instructions_offset()));
20732073
j(EQUAL, &instructions_ok);
20742074
int3();
20752075
Bind(&instructions_ok);

runtime/vm/compiler/assembler/assembler_x64.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,13 @@ class Assembler : public AssemblerBase {
711711

712712
// Methods for High-level operations and implemented on all architectures.
713713
void Ret() { ret(); }
714+
715+
// Sets the return address to [value] as if there was a call.
716+
// On X64 pushes [value].
717+
void SetReturnAddress(Register value) {
718+
PushRegister(value);
719+
}
720+
714721
void CompareRegisters(Register a, Register b);
715722
void CompareObjectRegisters(Register a, Register b) { OBJ(cmp)(a, b); }
716723
void BranchIf(Condition condition,

runtime/vm/compiler/backend/constant_propagator.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1456,6 +1456,10 @@ void ConstantPropagator::VisitCall1ArgStub(Call1ArgStubInstr* instr) {
14561456
SetValue(instr, non_constant_);
14571457
}
14581458

1459+
void ConstantPropagator::VisitSuspend(SuspendInstr* instr) {
1460+
SetValue(instr, non_constant_);
1461+
}
1462+
14591463
void ConstantPropagator::VisitLoadThread(LoadThreadInstr* instr) {
14601464
SetValue(instr, non_constant_);
14611465
}

runtime/vm/compiler/backend/flow_graph_compiler_arm.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,8 @@ void FlowGraphCompiler::EmitPrologue() {
373373
Register value_reg = slot_index == args_desc_slot ? ARGS_DESC_REG : R0;
374374
__ StoreToOffset(value_reg, FP, slot_index * compiler::target::kWordSize);
375375
}
376-
} else if (parsed_function().suspend_state_var() != nullptr) {
376+
} else if (parsed_function().suspend_state_var() != nullptr &&
377+
!flow_graph().IsCompiledForOsr()) {
377378
// Initialize synthetic :suspend_state variable early
378379
// as it may be accessed by GC and exception handling before
379380
// InitSuspendableFunction stub is called.

runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,8 @@ void FlowGraphCompiler::EmitPrologue() {
363363
slot_index == args_desc_slot ? ARGS_DESC_REG : NULL_REG;
364364
__ StoreToOffset(value_reg, FP, slot_index * kWordSize);
365365
}
366-
} else if (parsed_function().suspend_state_var() != nullptr) {
366+
} else if (parsed_function().suspend_state_var() != nullptr &&
367+
!flow_graph().IsCompiledForOsr()) {
367368
// Initialize synthetic :suspend_state variable early
368369
// as it may be accessed by GC and exception handling before
369370
// InitSuspendableFunction stub is called.

runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,8 @@ void FlowGraphCompiler::EmitPrologue() {
450450
Register value_reg = slot_index == args_desc_slot ? ARGS_DESC_REG : EAX;
451451
__ movl(compiler::Address(EBP, slot_index * kWordSize), value_reg);
452452
}
453-
} else if (parsed_function().suspend_state_var() != nullptr) {
453+
} else if (parsed_function().suspend_state_var() != nullptr &&
454+
!flow_graph().IsCompiledForOsr()) {
454455
// Initialize synthetic :suspend_state variable early
455456
// as it may be accessed by GC and exception handling before
456457
// InitSuspendableFunction stub is called.

runtime/vm/compiler/backend/flow_graph_compiler_riscv.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,8 @@ void FlowGraphCompiler::EmitPrologue() {
357357
__ StoreToOffset(value_reg, SP,
358358
(slot_index + fp_to_sp_delta) * kWordSize);
359359
}
360-
} else if (parsed_function().suspend_state_var() != nullptr) {
360+
} else if (parsed_function().suspend_state_var() != nullptr &&
361+
!flow_graph().IsCompiledForOsr()) {
361362
// Initialize synthetic :suspend_state variable early
362363
// as it may be accessed by GC and exception handling before
363364
// InitSuspendableFunction stub is called.

runtime/vm/compiler/backend/flow_graph_compiler_x64.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,8 @@ void FlowGraphCompiler::EmitPrologue() {
369369
Register value_reg = slot_index == args_desc_slot ? ARGS_DESC_REG : RAX;
370370
__ movq(compiler::Address(RBP, slot_index * kWordSize), value_reg);
371371
}
372-
} else if (parsed_function().suspend_state_var() != nullptr) {
372+
} else if (parsed_function().suspend_state_var() != nullptr &&
373+
!flow_graph().IsCompiledForOsr()) {
373374
// Initialize synthetic :suspend_state variable early
374375
// as it may be accessed by GC and exception handling before
375376
// InitSuspendableFunction stub is called.

runtime/vm/compiler/backend/il.cc

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7298,10 +7298,6 @@ LocationSummary* Call1ArgStubInstr::MakeLocationSummary(Zone* zone,
72987298
locs->set_in(0, Location::RegisterLocation(
72997299
InitSuspendableFunctionStubABI::kTypeArgsReg));
73007300
break;
7301-
case StubId::kAwait:
7302-
case StubId::kYieldAsyncStar:
7303-
locs->set_in(0, Location::RegisterLocation(SuspendStubABI::kArgumentReg));
7304-
break;
73057301
}
73067302
locs->set_out(0, Location::RegisterLocation(CallingConventions::kReturnReg));
73077303
return locs;
@@ -7314,12 +7310,34 @@ void Call1ArgStubInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
73147310
case StubId::kInitAsync:
73157311
stub = object_store->init_async_stub();
73167312
break;
7317-
case StubId::kAwait:
7318-
stub = object_store->await_stub();
7319-
break;
73207313
case StubId::kInitAsyncStar:
73217314
stub = object_store->init_async_star_stub();
73227315
break;
7316+
}
7317+
compiler->GenerateStubCall(source(), stub, UntaggedPcDescriptors::kOther,
7318+
locs(), deopt_id(), env());
7319+
}
7320+
7321+
LocationSummary* SuspendInstr::MakeLocationSummary(Zone* zone, bool opt) const {
7322+
const intptr_t kNumInputs = 1;
7323+
const intptr_t kNumTemps = 0;
7324+
LocationSummary* locs = new (zone)
7325+
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
7326+
locs->set_in(0, Location::RegisterLocation(SuspendStubABI::kArgumentReg));
7327+
locs->set_out(0, Location::RegisterLocation(CallingConventions::kReturnReg));
7328+
return locs;
7329+
}
7330+
7331+
void SuspendInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
7332+
// Use deopt_id as a yield index.
7333+
compiler->EmitYieldPositionMetadata(source(), deopt_id());
7334+
7335+
ObjectStore* object_store = compiler->isolate_group()->object_store();
7336+
Code& stub = Code::ZoneHandle(compiler->zone());
7337+
switch (stub_id_) {
7338+
case StubId::kAwait:
7339+
stub = object_store->await_stub();
7340+
break;
73237341
case StubId::kYieldAsyncStar:
73247342
stub = object_store->yield_async_star_stub();
73257343
break;
@@ -7328,18 +7346,18 @@ void Call1ArgStubInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
73287346
locs(), deopt_id(), env());
73297347

73307348
#if defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_IA32)
7331-
if ((stub_id_ == StubId::kAwait) || (stub_id_ == StubId::kYieldAsyncStar)) {
7332-
// On x86 (X64 and IA32) mismatch between calls and returns
7333-
// significantly regresses performance. So suspend stub
7334-
// does not return directly to the caller. Instead, a small
7335-
// epilogue is generated right after the call to suspend stub,
7336-
// and resume stub adjusts resume PC to skip this epilogue.
7337-
const intptr_t start = compiler->assembler()->CodeSize();
7338-
__ LeaveFrame();
7339-
__ ret();
7340-
RELEASE_ASSERT(compiler->assembler()->CodeSize() - start ==
7341-
SuspendStubABI::kResumePcDistance);
7342-
}
7349+
// On x86 (X64 and IA32) mismatch between calls and returns
7350+
// significantly regresses performance. So suspend stub
7351+
// does not return directly to the caller. Instead, a small
7352+
// epilogue is generated right after the call to suspend stub,
7353+
// and resume stub adjusts resume PC to skip this epilogue.
7354+
const intptr_t start = compiler->assembler()->CodeSize();
7355+
__ LeaveFrame();
7356+
__ ret();
7357+
RELEASE_ASSERT(compiler->assembler()->CodeSize() - start ==
7358+
SuspendStubABI::kResumePcDistance);
7359+
compiler->EmitCallsiteMetadata(source(), resume_deopt_id(),
7360+
UntaggedPcDescriptors::kOther, locs(), env());
73437361
#endif
73447362
}
73457363

0 commit comments

Comments
 (0)