[vm,dyn_modules] Add Nops as needed to avoid overwriting source positions.

Previously, the onus was on the BytecodeGenerator to emit Nops in cases
where a source position may be overwritten by the next emitted
instruction.

Instead, change SourcePositions.add to return whether or not the
requested mapping would overwrite an existing one. With that,
Assembler.emitSourcePosition[ForCall] now emits Nop instructions if
needed to ensure that the requested source position is recorded.

TEST=pkg/vm_service/test/set_sdk_library_debuggable_test

Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try
Change-Id: Ieebef0b59dbee3c21e598acfc45c5b47a7c19543
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/484100
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Tess Strickland
2026-02-27 07:23:12 -08:00
committed by Commit Queue
parent 5f2007d73d
commit 276118d5f0
3 changed files with 32 additions and 25 deletions
+17 -13
View File
@@ -84,13 +84,20 @@ class BytecodeAssembler {
@pragma('vm:prefer-inline')
void _emitSourcePosition() {
final position = currentSourcePosition == TreeNode.noOffset
? SourcePositions.noSourcePosition
: currentSourcePosition;
if (_emitSourcePositions && !isUnreachable) {
sourcePositions.add(
offset,
currentSourcePosition == TreeNode.noOffset
? SourcePositions.noSourcePosition
: currentSourcePosition,
currentSourcePositionFlags);
bool added =
sourcePositions.add(offset, position, currentSourcePositionFlags);
if (!added) {
// There's already an entry for the current PC, so emit a Nop instruction
// to provide a new pc offset for the requested source position.
_emitNop();
added =
sourcePositions.add(offset, position, currentSourcePositionFlags);
assert(added);
}
}
}
@@ -707,14 +714,11 @@ class BytecodeAssembler {
_emitInstructionA(Opcode.kCheckStack, ra);
}
// Nops are never created by the bytecode generator, but rather by
// the assembler in order to provide unique pc offsets for source
// positions.
@pragma('vm:prefer-inline')
void emitNop() {
// Currently, the only reason the bytecode generator emits a no-op
// instruction is when the next instruction to emit is unknown (e.g.,
// at the end of a visit<X> method), but a source position must be
// emitted after the last known instruction and not overwritten by
// the next emitted instruction.
emitSourcePosition();
void _emitNop() {
_emitInstruction0(Opcode.kNop);
}
@@ -3591,11 +3591,10 @@ class BytecodeGenerator extends RecursiveVisitor {
_genDirectCallWithArgs(target, args,
isFactory: target.isFactory, node: node);
if (target == debugger) {
// The debugger needs a pause right after stepping out from the debugger
// function. Just using asm.emitSourcePosition() won't work here, because
// the next emitted instruction may have its own source position
// and thus would overwrite that one.
asm.emitNop();
// The debugger needs a pause for the current source position right after
// stepping out from the debugger function.
assert(asm.currentSourcePosition != TreeNode.noOffset);
asm.emitSourcePosition();
}
}
+11 -7
View File
@@ -44,8 +44,10 @@ class SourcePositions extends BytecodeDeclaration {
return (value >> _numFlags, value & _flagMask);
}
// Maps the given PC to the given file offset. If there is already an entry
// for the given PC, then the new information overwrites the old information.
// Adds a mapping from the PC to the given file offset as long as there's
// no mapping for that PC already, otherwise no change is made. Returns
// whether the requested mapping exists, which can be either because a new
// mapping was created or the mapping already existed before the request.
//
// Marks the source position as synthetic (not to be used by the debugger
// or coverage calculations) if [(flags & syntheticFlag) != 0].
@@ -54,7 +56,7 @@ class SourcePositions extends BytecodeDeclaration {
//
// Assumes that the pc is greater than or equal to the pc used in the most
// recent call to add, if any.
void add(int pc, int fileOffset, int flags) {
bool add(int pc, int fileOffset, int flags) {
assert(fileOffset >= 0 || fileOffset == noSourcePosition);
assert((flags & ~_flagMask) == 0);
if (_lastPcAdded > pc) {
@@ -67,16 +69,18 @@ class SourcePositions extends BytecodeDeclaration {
final lastPc = _positions[i];
final lastFileOffset = _positions[i + 1];
if (lastFileOffset == encodedFileOffset) {
// The last entry covers this PC offset as well.
return;
// The last entry covers this PC offset as well, or this is a repeated
// request for the same (pc, offset) mapping.
return true;
}
if (lastPc == pc) {
// The new entry for this PC overwrites the old one.
_positions.removeRange(i, i + 2);
// There's already a mapping for (pc, lastFileOffset).
return false;
}
}
_positions.add(pc);
_positions.add(encodedFileOffset);
return true;
}
bool get isEmpty => _positions.isEmpty;