Skip to content

Commit aceeba7

Browse files
mkustermanncommit-bot@chromium.org
authored andcommitted
[vm/async] Encode the yield index -> token position in PcDescriptors.
Right now `Script::yield_positions()` is an array mapping function start token positions to arrays. Those arrays contain token positions and are indexed by yield index. => This `Script::yield_positions()` is not available in AOT mode. The fast async stack implementation will need to be able to find out where an async closure was suspended. It does so by looking at the ":await_jump_var", which contains the yield index. It then needs to associate the yield index to token position of the yield. This CL adds an entry into the PcDescriptors for every yield in a async/async* function and removes `Script::yield_positions()`. The entry will associate the yield index with the token position. Flutter gallery total size impact for flutter-release: - armv7: +0.016% - armv8: +0.045% Issue #37668 Change-Id: I0b2ce41e85d8f5d590201bf2fb091578d7379890 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/125408 Commit-Queue: Martin Kustermann <[email protected]> Reviewed-by: Clement Skau <[email protected]> Reviewed-by: Alexander Markov <[email protected]>
1 parent 129565a commit aceeba7

36 files changed

+385
-208
lines changed

pkg/vm/lib/bytecode/assembler.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ class BytecodeAssembler {
107107
}
108108
}
109109

110-
void emitYieldPointSourcePosition() {
110+
void emitYieldPointSourcePosition(int yieldSourcePosition) {
111111
if (!isUnreachable) {
112-
sourcePositions.addYieldPoint(offset, currentSourcePosition);
112+
sourcePositions.addYieldPoint(offset, yieldSourcePosition);
113113
}
114114
}
115115

pkg/vm/lib/bytecode/gen_bytecode.dart

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,7 +1299,7 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
12991299
}
13001300
}
13011301

1302-
void _genReturnTOS() {
1302+
void _genReturnTOS([int yieldSourcePosition = null]) {
13031303
if (options.causalAsyncStacks &&
13041304
parentFunction != null &&
13051305
(parentFunction.dartAsyncMarker == AsyncMarker.Async ||
@@ -1312,6 +1312,9 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
13121312
asm.currentSourcePosition = savedSourcePosition;
13131313
}
13141314

1315+
if (yieldSourcePosition != null && options.emitSourcePositions) {
1316+
asm.emitYieldPointSourcePosition(yieldSourcePosition);
1317+
}
13151318
asm.emitReturnTOS();
13161319
}
13171320

@@ -4560,10 +4563,6 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
45604563
return;
45614564
}
45624565

4563-
if (options.emitSourcePositions) {
4564-
asm.emitYieldPointSourcePosition();
4565-
}
4566-
45674566
// 0 is reserved for normal entry, yield points are counted from 1.
45684567
final int yieldIndex = yieldPoints.length + 1;
45694568
final Label continuationLabel = new Label(allowsBackwardJumps: true);
@@ -4584,7 +4583,7 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
45844583
// return <expression>
45854584
// Note: finally blocks are *not* executed on the way out.
45864585
_generateNode(node.expression);
4587-
_genReturnTOS();
4586+
_genReturnTOS(node.fileOffset);
45884587

45894588
asm.bind(continuationLabel);
45904589

runtime/vm/code_descriptors.cc

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,26 @@ void DescriptorList::AddDescriptor(RawPcDescriptors::Kind kind,
1313
intptr_t pc_offset,
1414
intptr_t deopt_id,
1515
TokenPosition token_pos,
16-
intptr_t try_index) {
16+
intptr_t try_index,
17+
intptr_t yield_index) {
18+
// yield index 0 is reserved for normal entry.
19+
RELEASE_ASSERT(yield_index != 0);
20+
1721
ASSERT((kind == RawPcDescriptors::kRuntimeCall) ||
1822
(kind == RawPcDescriptors::kBSSRelocation) ||
19-
(kind == RawPcDescriptors::kOther) || (deopt_id != DeoptId::kNone));
23+
(kind == RawPcDescriptors::kOther) ||
24+
(yield_index != RawPcDescriptors::kInvalidYieldIndex) ||
25+
(deopt_id != DeoptId::kNone));
2026

21-
// When precompiling, we only use pc descriptors for exceptions and
22-
// relocations.
27+
// When precompiling, we only use pc descriptors for exceptions,
28+
// relocations and yield indices.
2329
if (!FLAG_precompiled_mode || try_index != -1 ||
30+
yield_index != RawPcDescriptors::kInvalidYieldIndex ||
2431
kind == RawPcDescriptors::kBSSRelocation) {
25-
int32_t merged_kind_try =
26-
RawPcDescriptors::MergedKindTry::Encode(kind, try_index);
32+
const int32_t kind_and_metadata =
33+
RawPcDescriptors::KindAndMetadata::Encode(kind, try_index, yield_index);
2734

28-
PcDescriptors::EncodeInteger(&encoded_data_, merged_kind_try);
35+
PcDescriptors::EncodeInteger(&encoded_data_, kind_and_metadata);
2936
PcDescriptors::EncodeInteger(&encoded_data_, pc_offset - prev_pc_offset);
3037
prev_pc_offset = pc_offset;
3138

runtime/vm/code_descriptors.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ class DescriptorList : public ZoneAllocated {
3030
intptr_t pc_offset,
3131
intptr_t deopt_id,
3232
TokenPosition token_pos,
33-
intptr_t try_index);
33+
intptr_t try_index,
34+
intptr_t yield_index);
3435

3536
RawPcDescriptors* FinalizePcDescriptors(uword entry_point);
3637

runtime/vm/code_descriptors_test.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ ISOLATE_UNIT_TEST_CASE(DescriptorList_TokenPositions) {
143143

144144
for (intptr_t i = 0; i < num_token_positions; i++) {
145145
descriptors->AddDescriptor(RawPcDescriptors::kRuntimeCall, 0, 0,
146-
TokenPosition(token_positions[i]), 0);
146+
TokenPosition(token_positions[i]), 0, 1);
147147
}
148148

149149
const PcDescriptors& finalized_descriptors =

runtime/vm/compiler/aot/precompiler.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1842,7 +1842,6 @@ void Precompiler::DropLibraryEntries() {
18421842
script.set_compile_time_constants(Array::null_array());
18431843
script.set_line_starts(null_typed_data);
18441844
script.set_debug_positions(Array::null_array());
1845-
script.set_yield_positions(Array::null_array());
18461845
script.set_kernel_program_info(null_info);
18471846
script.set_source(String::null_string());
18481847
}

runtime/vm/compiler/backend/flow_graph_compiler.cc

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,12 @@ void FlowGraphCompiler::EmitCallsiteMetadata(TokenPosition token_pos,
491491
}
492492
}
493493

494+
void FlowGraphCompiler::EmitYieldPositionMetadata(TokenPosition token_pos,
495+
intptr_t yield_index) {
496+
AddDescriptor(RawPcDescriptors::kOther, assembler()->CodeSize(),
497+
DeoptId::kNone, token_pos, CurrentTryIndex(), yield_index);
498+
}
499+
494500
void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) {
495501
if (!is_optimizing()) {
496502
if (instr->CanBecomeDeoptimizationTarget() && !instr->IsGoto()) {
@@ -731,12 +737,13 @@ void FlowGraphCompiler::AddDescriptor(RawPcDescriptors::Kind kind,
731737
intptr_t pc_offset,
732738
intptr_t deopt_id,
733739
TokenPosition token_pos,
734-
intptr_t try_index) {
740+
intptr_t try_index,
741+
intptr_t yield_index) {
735742
code_source_map_builder_->NoteDescriptor(kind, pc_offset, token_pos);
736743
// Don't emit deopt-descriptors in AOT mode.
737744
if (FLAG_precompiled_mode && (kind == RawPcDescriptors::kDeopt)) return;
738745
pc_descriptors_list_->AddDescriptor(kind, pc_offset, deopt_id, token_pos,
739-
try_index);
746+
try_index, yield_index);
740747
}
741748

742749
// Uses current pc position and try-index.

runtime/vm/compiler/backend/flow_graph_compiler.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,8 @@ class FlowGraphCompiler : public ValueObject {
681681
LocationSummary* locs,
682682
Environment* env = nullptr);
683683

684+
void EmitYieldPositionMetadata(TokenPosition token_pos, intptr_t yield_index);
685+
684686
void EmitComment(Instruction* instr);
685687

686688
// Returns stack size (number of variables on stack for unoptimized
@@ -716,11 +718,13 @@ class FlowGraphCompiler : public ValueObject {
716718
void AddCurrentDescriptor(RawPcDescriptors::Kind kind,
717719
intptr_t deopt_id,
718720
TokenPosition token_pos);
719-
void AddDescriptor(RawPcDescriptors::Kind kind,
720-
intptr_t pc_offset,
721-
intptr_t deopt_id,
722-
TokenPosition token_pos,
723-
intptr_t try_index);
721+
void AddDescriptor(
722+
RawPcDescriptors::Kind kind,
723+
intptr_t pc_offset,
724+
intptr_t deopt_id,
725+
TokenPosition token_pos,
726+
intptr_t try_index,
727+
intptr_t yield_index = RawPcDescriptors::kInvalidYieldIndex);
724728

725729
void AddNullCheck(intptr_t pc_offset,
726730
TokenPosition token_pos,

runtime/vm/compiler/backend/il.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2677,15 +2677,23 @@ inline Definition* Instruction::ArgumentAt(intptr_t index) const {
26772677

26782678
class ReturnInstr : public TemplateInstruction<1, NoThrow> {
26792679
public:
2680-
ReturnInstr(TokenPosition token_pos, Value* value, intptr_t deopt_id)
2681-
: TemplateInstruction(deopt_id), token_pos_(token_pos) {
2680+
// The [yield_index], if provided, will cause the instruction to emit extra
2681+
// yield_index -> pc offset into the [PcDescriptors].
2682+
ReturnInstr(TokenPosition token_pos,
2683+
Value* value,
2684+
intptr_t deopt_id,
2685+
intptr_t yield_index = RawPcDescriptors::kInvalidYieldIndex)
2686+
: TemplateInstruction(deopt_id),
2687+
token_pos_(token_pos),
2688+
yield_index_(yield_index) {
26822689
SetInputAt(0, value);
26832690
}
26842691

26852692
DECLARE_INSTRUCTION(Return)
26862693

26872694
virtual TokenPosition token_pos() const { return token_pos_; }
26882695
Value* value() const { return inputs_[0]; }
2696+
intptr_t yield_index() const { return yield_index_; }
26892697

26902698
virtual bool CanBecomeDeoptimizationTarget() const {
26912699
// Return instruction might turn into a Goto instruction after inlining.
@@ -2697,8 +2705,17 @@ class ReturnInstr : public TemplateInstruction<1, NoThrow> {
26972705

26982706
virtual bool HasUnknownSideEffects() const { return false; }
26992707

2708+
virtual bool AttributesEqual(Instruction* other) const {
2709+
auto other_return = other->AsReturn();
2710+
return token_pos() == other_return->token_pos() &&
2711+
yield_index() == other_return->yield_index();
2712+
}
2713+
2714+
PRINT_OPERANDS_TO_SUPPORT
2715+
27002716
private:
27012717
const TokenPosition token_pos_;
2718+
const intptr_t yield_index_;
27022719

27032720
DISALLOW_COPY_AND_ASSIGN(ReturnInstr);
27042721
};

runtime/vm/compiler/backend/il_arm.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ void ReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
139139
__ Bind(&stack_ok);
140140
#endif
141141
ASSERT(__ constant_pool_allowed());
142+
if (yield_index() != RawPcDescriptors::kInvalidYieldIndex) {
143+
compiler->EmitYieldPositionMetadata(token_pos(), yield_index());
144+
}
142145
__ LeaveDartFrameAndReturn(); // Disallows constant pool use.
143146
// This ReturnInstr may be emitted out of order by the optimizer. The next
144147
// block may be a target expecting a properly set constant pool pointer.

runtime/vm/compiler/backend/il_arm64.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ void ReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
138138
__ Bind(&stack_ok);
139139
#endif
140140
ASSERT(__ constant_pool_allowed());
141+
if (yield_index() != RawPcDescriptors::kInvalidYieldIndex) {
142+
compiler->EmitYieldPositionMetadata(token_pos(), yield_index());
143+
}
141144
__ LeaveDartFrame(); // Disallows constant pool use.
142145
__ ret();
143146
// This ReturnInstr may be emitted out of order by the optimizer. The next

runtime/vm/compiler/backend/il_ia32.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ void ReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
133133
__ int3();
134134
__ Bind(&done);
135135
#endif
136+
if (yield_index() != RawPcDescriptors::kInvalidYieldIndex) {
137+
compiler->EmitYieldPositionMetadata(token_pos(), yield_index());
138+
}
136139
__ LeaveFrame();
137140
__ ret();
138141
}

runtime/vm/compiler/backend/il_printer.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,13 @@ void NativeEntryInstr::PrintTo(BufferFormatter* f) const {
10611061
BlockEntryWithInitialDefs::PrintInitialDefinitionsTo(f);
10621062
}
10631063

1064+
void ReturnInstr::PrintOperandsTo(BufferFormatter* f) const {
1065+
Instruction::PrintOperandsTo(f);
1066+
if (yield_index() != RawPcDescriptors::kInvalidYieldIndex) {
1067+
f->Print(", yield_index = %" Pd "", yield_index());
1068+
}
1069+
}
1070+
10641071
void NativeReturnInstr::PrintOperandsTo(BufferFormatter* f) const {
10651072
value()->PrintTo(f);
10661073
}

runtime/vm/compiler/backend/il_x64.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ void ReturnInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
134134
__ Bind(&done);
135135
#endif
136136
ASSERT(__ constant_pool_allowed());
137+
if (yield_index() != RawPcDescriptors::kInvalidYieldIndex) {
138+
compiler->EmitYieldPositionMetadata(token_pos(), yield_index());
139+
}
137140
__ LeaveDartFrame(); // Disallows constant pool use.
138141
__ ret();
139142
// This ReturnInstr may be emitted out of order by the optimizer. The next
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
#include <utility>
6+
7+
#include "vm/compiler/backend/il_test_helper.h"
8+
#include "vm/compiler/compiler_pass.h"
9+
#include "vm/object.h"
10+
#include "vm/unit_test.h"
11+
12+
namespace dart {
13+
14+
using Pair = std::pair<intptr_t, TokenPosition>;
15+
using YieldPoints = ZoneGrowableArray<Pair>;
16+
17+
int LowestFirst(const Pair* a, const Pair* b) {
18+
return a->first - b->first;
19+
}
20+
21+
static YieldPoints* GetYieldPointsFromGraph(FlowGraph* flow_graph) {
22+
auto array = new (flow_graph->zone()) YieldPoints();
23+
const auto& blocks = flow_graph->reverse_postorder();
24+
for (auto block : blocks) {
25+
ForwardInstructionIterator it(block);
26+
while (!it.Done()) {
27+
if (auto return_instr = it.Current()->AsReturn()) {
28+
if (return_instr->yield_index() !=
29+
RawPcDescriptors::kInvalidYieldIndex) {
30+
ASSERT(return_instr->yield_index() > 0);
31+
array->Add(
32+
Pair(return_instr->yield_index(), return_instr->token_pos()));
33+
}
34+
}
35+
it.Advance();
36+
}
37+
}
38+
array->Sort(LowestFirst);
39+
return array;
40+
}
41+
42+
static YieldPoints* GetYieldPointsFromCode(const Code& code) {
43+
auto array = new YieldPoints();
44+
const auto& pc_descriptor = PcDescriptors::Handle(code.pc_descriptors());
45+
PcDescriptors::Iterator it(pc_descriptor, RawPcDescriptors::kOther);
46+
while (it.MoveNext()) {
47+
if (it.YieldIndex() != RawPcDescriptors::kInvalidYieldIndex) {
48+
array->Add(Pair(it.YieldIndex(), it.TokenPos()));
49+
}
50+
}
51+
array->Sort(LowestFirst);
52+
return array;
53+
}
54+
55+
void RunTestInMode(CompilerPass::PipelineMode mode) {
56+
const char* kScript =
57+
R"(
58+
import 'dart:async';
59+
60+
Future foo() async {
61+
print('pos-0');
62+
await 0;
63+
print('pos-1');
64+
await 1;
65+
print('pos-2');
66+
await 2;
67+
}
68+
)";
69+
70+
SetupCoreLibrariesForUnitTest();
71+
72+
const auto& root_library = Library::Handle(LoadTestScript(kScript));
73+
// Ensure the outer function was compiled once, ensuring we have a closure
74+
// function for the inner closure.
75+
Invoke(root_library, "foo");
76+
77+
const auto& outer_function =
78+
Function::Handle(GetFunction(root_library, "foo"));
79+
80+
// Grab the inner, lazily created, closure from the object store.
81+
const auto& closures = GrowableObjectArray::Handle(
82+
Isolate::Current()->object_store()->closure_functions());
83+
ASSERT(!closures.IsNull());
84+
auto& closure = Object::Handle();
85+
for (intptr_t i = 0; i < closures.Length(); ++i) {
86+
closure = closures.At(i);
87+
if (Function::Cast(closure).parent_function() == outer_function.raw()) {
88+
break;
89+
}
90+
closure = Object::null();
91+
}
92+
RELEASE_ASSERT(closure.IsFunction());
93+
const auto& function = Function::Cast(closure);
94+
95+
// Ensure we have 3 different return instructions with yield indices attached
96+
// to them.
97+
TestPipeline pipeline(function, mode);
98+
FlowGraph* flow_graph = pipeline.RunPasses({
99+
CompilerPass::kComputeSSA,
100+
});
101+
102+
auto validate_indices = [](const YieldPoints& yield_points) {
103+
EXPECT_EQ(3, yield_points.length());
104+
105+
EXPECT_EQ(1, yield_points[0].first);
106+
EXPECT_EQ(88, yield_points[0].second.value());
107+
EXPECT_EQ(2, yield_points[1].first);
108+
EXPECT_EQ(129, yield_points[1].second.value());
109+
EXPECT_EQ(3, yield_points[2].first);
110+
EXPECT_EQ(170, yield_points[2].second.value());
111+
};
112+
113+
validate_indices(*GetYieldPointsFromGraph(flow_graph));
114+
115+
// Ensure we have 3 different yield indices attached to the code via pc
116+
// descriptors.
117+
const auto& error = Error::Handle(
118+
Compiler::EnsureUnoptimizedCode(Thread::Current(), function));
119+
RELEASE_ASSERT(error.IsNull());
120+
const auto& code = Code::Handle(function.CurrentCode());
121+
validate_indices(*GetYieldPointsFromCode(code));
122+
}
123+
124+
ISOLATE_UNIT_TEST_CASE(IRTest_YieldIndexAvailableJIT) {
125+
RunTestInMode(CompilerPass::kJIT);
126+
}
127+
128+
ISOLATE_UNIT_TEST_CASE(IRTest_YieldIndexAvailableAOT) {
129+
RunTestInMode(CompilerPass::kAOT);
130+
}
131+
132+
} // namespace dart

0 commit comments

Comments
 (0)