Skip to content

Commit f91089f

Browse files
alexmarkovcommit-bot@chromium.org
authored andcommitted
[vm/bytecode] Add yield point markers to source positions
Issue: #36427 Change-Id: I384161fd27b977e05796666c9a1a1e336d4d6440 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107572 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
1 parent d946b3a commit f91089f

File tree

7 files changed

+138
-50
lines changed

7 files changed

+138
-50
lines changed

pkg/vm/lib/bytecode/assembler.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ class BytecodeAssembler {
7979
}
8080
}
8181

82+
void emitYieldPointSourcePosition() {
83+
if (!isUnreachable) {
84+
sourcePositions.addYieldPoint(offset, currentSourcePosition);
85+
}
86+
}
87+
8288
void _emitByte(int abyte) {
8389
assert(_isUint8(abyte));
8490
bytecode.add(abyte);

pkg/vm/lib/bytecode/declarations.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,7 @@ class Code {
760760

761761
bool get hasExceptionsTable => exceptionsTable.blocks.isNotEmpty;
762762
bool get hasSourcePositions =>
763-
sourcePositions != null && sourcePositions.mapping.isNotEmpty;
763+
sourcePositions != null && sourcePositions.isNotEmpty;
764764
bool get hasLocalVariables =>
765765
localVariables != null && localVariables.isNotEmpty;
766766
bool get hasNullableFields => nullableFields.isNotEmpty;
@@ -1056,7 +1056,7 @@ class ClosureCode {
10561056

10571057
bool get hasExceptionsTable => exceptionsTable.blocks.isNotEmpty;
10581058
bool get hasSourcePositions =>
1059-
sourcePositions != null && sourcePositions.mapping.isNotEmpty;
1059+
sourcePositions != null && sourcePositions.isNotEmpty;
10601060
bool get hasLocalVariables =>
10611061
localVariables != null && localVariables.isNotEmpty;
10621062

pkg/vm/lib/bytecode/gen_bytecode.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1485,7 +1485,7 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
14851485
}
14861486

14871487
SourcePositions finalizeSourcePositions() {
1488-
if (asm.sourcePositions.mapping.isEmpty) {
1488+
if (asm.sourcePositions.isEmpty) {
14891489
return null;
14901490
}
14911491
bytecodeComponent.sourcePositions.add(asm.sourcePositions);
@@ -3692,6 +3692,10 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
36923692
return;
36933693
}
36943694

3695+
if (options.emitSourcePositions) {
3696+
asm.emitYieldPointSourcePosition();
3697+
}
3698+
36953699
// 0 is reserved for normal entry, yield points are counted from 1.
36963700
final int yieldIndex = yieldPoints.length + 1;
36973701
final Label continuationLabel = new Label(allowsBackwardJumps: true);

pkg/vm/lib/bytecode/source_positions.dart

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ import 'bytecode_serialization.dart'
1515

1616
/// Maintains mapping between bytecode instructions and source positions.
1717
class SourcePositions {
18-
final Map<int, int> mapping = <int, int>{}; // PC -> fileOffset
18+
// Special value of fileOffset which marks yield point.
19+
static const yieldPointMarker = -2;
20+
21+
final List<int> _positions = <int>[]; // Pairs (PC, fileOffset).
1922
int _lastPc = 0;
2023
int _lastOffset = 0;
2124

@@ -25,20 +28,37 @@ class SourcePositions {
2528
assert(pc > _lastPc);
2629
assert(fileOffset >= 0);
2730
if (fileOffset != _lastOffset) {
28-
mapping[pc] = fileOffset;
31+
_positions.add(pc);
32+
_positions.add(fileOffset);
2933
_lastPc = pc;
3034
_lastOffset = fileOffset;
3135
}
3236
}
3337

38+
void addYieldPoint(int pc, int fileOffset) {
39+
assert(pc > _lastPc);
40+
assert(fileOffset >= 0);
41+
_positions.add(pc);
42+
_positions.add(yieldPointMarker);
43+
_positions.add(pc);
44+
_positions.add(fileOffset);
45+
_lastPc = pc;
46+
_lastOffset = fileOffset;
47+
}
48+
49+
bool get isEmpty => _positions.isEmpty;
50+
bool get isNotEmpty => !isEmpty;
51+
3452
void write(BufferedWriter writer) {
35-
writer.writePackedUInt30(mapping.length);
53+
writer.writePackedUInt30(_positions.length ~/ 2);
3654
final encodePC = new PackedUInt30DeltaEncoder();
3755
final encodeOffset = new SLEB128DeltaEncoder();
38-
mapping.forEach((int pc, int fileOffset) {
56+
for (int i = 0; i < _positions.length; i += 2) {
57+
final int pc = _positions[i];
58+
final int fileOffset = _positions[i + 1];
3959
encodePC.write(writer, pc);
4060
encodeOffset.write(writer, fileOffset);
41-
});
61+
}
4262
}
4363

4464
SourcePositions.read(BufferedReader reader) {
@@ -48,16 +68,29 @@ class SourcePositions {
4868
for (int i = 0; i < length; ++i) {
4969
int pc = decodePC.read(reader);
5070
int fileOffset = decodeOffset.read(reader);
51-
add(pc, fileOffset);
71+
_positions.add(pc);
72+
_positions.add(fileOffset);
5273
}
5374
}
5475

5576
@override
56-
String toString() => mapping.toString();
77+
String toString() => _positions.toString();
5778

5879
Map<int, String> getBytecodeAnnotations() {
59-
return mapping.map((int pc, int fileOffset) =>
60-
new MapEntry(pc, 'source position $fileOffset'));
80+
final map = <int, String>{};
81+
for (int i = 0; i < _positions.length; i += 2) {
82+
final int pc = _positions[i];
83+
final int fileOffset = _positions[i + 1];
84+
final entry = (fileOffset == yieldPointMarker)
85+
? 'yield point'
86+
: 'source position $fileOffset';
87+
if (map[pc] == null) {
88+
map[pc] = entry;
89+
} else {
90+
map[pc] = "${map[pc]}; $entry";
91+
}
92+
}
93+
return map;
6194
}
6295
}
6396

runtime/vm/compiler/frontend/bytecode_reader.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,10 @@ class BytecodeReader : public AllStatic {
331331

332332
class BytecodeSourcePositionsIterator : ValueObject {
333333
public:
334+
// This constant should match corresponding constant in class SourcePositions
335+
// (pkg/vm/lib/bytecode/source_positions.dart).
336+
static const intptr_t kYieldPointMarker = -2;
337+
334338
BytecodeSourcePositionsIterator(Zone* zone, const Bytecode& bytecode)
335339
: reader_(ExternalTypedData::Handle(zone, bytecode.GetBinary(zone))) {
336340
if (bytecode.HasSourcePositions()) {
@@ -347,18 +351,27 @@ class BytecodeSourcePositionsIterator : ValueObject {
347351
--pairs_remaining_;
348352
cur_bci_ += reader_.ReadUInt();
349353
cur_token_pos_ += reader_.ReadSLEB128();
354+
is_yield_point_ = false;
355+
if (cur_token_pos_ == kYieldPointMarker) {
356+
const bool result = MoveNext();
357+
is_yield_point_ = true;
358+
return result;
359+
}
350360
return true;
351361
}
352362

353363
uword PcOffset() const { return cur_bci_; }
354364

355365
TokenPosition TokenPos() const { return TokenPosition(cur_token_pos_); }
356366

367+
bool IsYieldPoint() const { return is_yield_point_; }
368+
357369
private:
358370
Reader reader_;
359371
intptr_t pairs_remaining_ = 0;
360372
intptr_t cur_bci_ = 0;
361373
intptr_t cur_token_pos_ = 0;
374+
bool is_yield_point_ = false;
362375
};
363376

364377
#if !defined(PRODUCT)

runtime/vm/debugger.cc

Lines changed: 69 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,25 @@ bool ActivationFrame::HandlesException(const Instance& exc_obj) {
948948
return false;
949949
}
950950

951+
intptr_t ActivationFrame::GetAwaitJumpVariable() {
952+
GetVarDescriptors();
953+
intptr_t var_desc_len = var_descriptors_.Length();
954+
intptr_t await_jump_var = -1;
955+
for (intptr_t i = 0; i < var_desc_len; i++) {
956+
RawLocalVarDescriptors::VarInfo var_info;
957+
var_descriptors_.GetInfo(i, &var_info);
958+
const int8_t kind = var_info.kind();
959+
if (var_descriptors_.GetName(i) == Symbols::AwaitJumpVar().raw()) {
960+
ASSERT(kind == RawLocalVarDescriptors::kContextVar);
961+
ASSERT(!ctx_.IsNull());
962+
Object& await_jump_index = Object::Handle(ctx_.At(var_info.index()));
963+
ASSERT(await_jump_index.IsSmi());
964+
await_jump_var = Smi::Cast(await_jump_index).Value();
965+
}
966+
}
967+
return await_jump_var;
968+
}
969+
951970
void ActivationFrame::ExtractTokenPositionFromAsyncClosure() {
952971
// Attempt to determine the token pos and try index from the async closure.
953972
Thread* thread = Thread::Current();
@@ -958,28 +977,46 @@ void ActivationFrame::ExtractTokenPositionFromAsyncClosure() {
958977
// This should only be called on frames that aren't active on the stack.
959978
ASSERT(fp() == 0);
960979

980+
if (function_.is_declared_in_bytecode()) {
981+
#if !defined(DART_PRECOMPILED_RUNTIME)
982+
const auto& bytecode = Bytecode::Handle(zone, function_.bytecode());
983+
if (!bytecode.HasSourcePositions()) {
984+
return;
985+
}
986+
const intptr_t await_jump_var = GetAwaitJumpVariable();
987+
if (await_jump_var < 0) {
988+
return;
989+
}
990+
// Yield points are counted from 1 (0 is reserved for normal entry).
991+
intptr_t yield_point_index = 1;
992+
kernel::BytecodeSourcePositionsIterator iter(zone, bytecode);
993+
while (iter.MoveNext()) {
994+
if (iter.IsYieldPoint()) {
995+
if (yield_point_index == await_jump_var) {
996+
token_pos_ = iter.TokenPos();
997+
token_pos_initialized_ = true;
998+
try_index_ = bytecode.GetTryIndexAtPc(bytecode.PayloadStart() +
999+
iter.PcOffset());
1000+
return;
1001+
}
1002+
++yield_point_index;
1003+
}
1004+
}
1005+
return;
1006+
#else
1007+
UNREACHABLE();
1008+
#endif // !defined(DART_PRECOMPILED_RUNTIME)
1009+
}
1010+
1011+
ASSERT(!IsInterpreted());
9611012
ASSERT(script.kind() == RawScript::kKernelTag);
9621013
const Array& await_to_token_map =
9631014
Array::Handle(zone, script.yield_positions());
9641015
if (await_to_token_map.IsNull()) {
9651016
// No mapping.
9661017
return;
9671018
}
968-
GetVarDescriptors();
969-
intptr_t var_desc_len = var_descriptors_.Length();
970-
intptr_t await_jump_var = -1;
971-
for (intptr_t i = 0; i < var_desc_len; i++) {
972-
RawLocalVarDescriptors::VarInfo var_info;
973-
var_descriptors_.GetInfo(i, &var_info);
974-
const int8_t kind = var_info.kind();
975-
if (var_descriptors_.GetName(i) == Symbols::AwaitJumpVar().raw()) {
976-
ASSERT(kind == RawLocalVarDescriptors::kContextVar);
977-
ASSERT(!ctx_.IsNull());
978-
Object& await_jump_index = Object::Handle(ctx_.At(var_info.index()));
979-
ASSERT(await_jump_index.IsSmi());
980-
await_jump_var = Smi::Cast(await_jump_index).Value();
981-
}
982-
}
1019+
const intptr_t await_jump_var = GetAwaitJumpVariable();
9831020
if (await_jump_var < 0) {
9841021
return;
9851022
}
@@ -1011,29 +1048,6 @@ void ActivationFrame::ExtractTokenPositionFromAsyncClosure() {
10111048
ASSERT(token_pos.IsSmi());
10121049
token_pos_ = TokenPosition(Smi::Cast(token_pos).Value());
10131050
token_pos_initialized_ = true;
1014-
if (IsInterpreted()) {
1015-
#if !defined(DART_PRECOMPILED_RUNTIME)
1016-
// In order to determine the try index, we need to map the token position
1017-
// to a pc offset, and then a pc offset to the try index.
1018-
// TODO(regis): Should we set the token position fields in pc descriptors?
1019-
uword pc_offset = kUwordMax;
1020-
kernel::BytecodeSourcePositionsIterator iter(zone, bytecode());
1021-
while (iter.MoveNext()) {
1022-
// PcOffsets are monotonic in source positions, so we get the lowest one.
1023-
if (iter.TokenPos() == token_pos_) {
1024-
pc_offset = iter.PcOffset();
1025-
break;
1026-
}
1027-
}
1028-
if (pc_offset < kUwordMax) {
1029-
try_index_ =
1030-
bytecode().GetTryIndexAtPc(bytecode().PayloadStart() + pc_offset);
1031-
}
1032-
#else
1033-
UNREACHABLE();
1034-
#endif // !defined(DART_PRECOMPILED_RUNTIME)
1035-
return;
1036-
}
10371051
GetPcDescriptors();
10381052
PcDescriptors::Iterator iter(pc_desc_, RawPcDescriptors::kAnyKind);
10391053
while (iter.MoveNext()) {
@@ -4170,6 +4184,23 @@ bool Debugger::IsAtAsyncJump(ActivationFrame* top_frame) {
41704184
if (!closure_or_null.IsNull()) {
41714185
ASSERT(closure_or_null.IsInstance());
41724186
ASSERT(Instance::Cast(closure_or_null).IsClosure());
4187+
if (top_frame->function().is_declared_in_bytecode()) {
4188+
#if !defined(DART_PRECOMPILED_RUNTIME)
4189+
const auto& bytecode =
4190+
Bytecode::Handle(zone, top_frame->function().bytecode());
4191+
const TokenPosition token_pos = top_frame->TokenPos();
4192+
kernel::BytecodeSourcePositionsIterator iter(zone, bytecode);
4193+
while (iter.MoveNext()) {
4194+
if (iter.IsYieldPoint() && (iter.TokenPos() == token_pos)) {
4195+
return true;
4196+
}
4197+
}
4198+
return false;
4199+
#else
4200+
UNREACHABLE();
4201+
#endif // !defined(DART_PRECOMPILED_RUNTIME)
4202+
}
4203+
ASSERT(!top_frame->IsInterpreted());
41734204
const Script& script = Script::Handle(zone, top_frame->SourceScript());
41744205
ASSERT(script.kind() == RawScript::kKernelTag);
41754206
// Are we at a yield point (previous await)?

runtime/vm/debugger.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ class ActivationFrame : public ZoneAllocated {
388388
RawObject* GetAsyncStreamControllerStream();
389389
RawObject* GetAsyncCompleterAwaiter(const Object& completer);
390390
RawObject* GetAsyncCompleter();
391+
intptr_t GetAwaitJumpVariable();
391392
void ExtractTokenPositionFromAsyncClosure();
392393

393394
bool IsAsyncMachinery() const;

0 commit comments

Comments
 (0)