[vm/bytecode] Add DebugCheck bytecode instruction

DebugCheck bytecode instruction is generated after parameter variables
are declared and copied into their locations in the prologue.
It helps debugger to stop in the beginning of a function at the point
where parameters can be inspected. It is generated only if
'--bytecode-options=debugger-stops' is specified.

Change-Id: I0f3b1ea8dc45d762a5dcee75b5d3a4ffc0b2a1b1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/108371
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov 2019-07-09 19:14:53 +00:00 committed by commit-bot@chromium.org
parent fc542be6b4
commit 9503969664
12 changed files with 62 additions and 26 deletions

View file

@ -146,8 +146,8 @@ abstract class Compiler {
if (options.bytecode && errors.isEmpty) { if (options.bytecode && errors.isEmpty) {
await runWithFrontEndCompilerContext(script, options, component, () { await runWithFrontEndCompilerContext(script, options, component, () {
// TODO(alexmarkov): disable source positions, local variables info // TODO(alexmarkov): disable source positions, local variables info,
// and source files in VM PRODUCT mode. // debugger stops and source files in VM PRODUCT mode.
// TODO(alexmarkov): disable asserts if they are not enabled in VM. // TODO(alexmarkov): disable asserts if they are not enabled in VM.
// TODO(rmacnak): disable annotations if mirrors are not enabled. // TODO(rmacnak): disable annotations if mirrors are not enabled.
generateBytecode(component, generateBytecode(component,
@ -156,6 +156,7 @@ abstract class Compiler {
environmentDefines: options.environmentDefines, environmentDefines: options.environmentDefines,
emitSourcePositions: true, emitSourcePositions: true,
emitLocalVarInfo: true, emitLocalVarInfo: true,
emitDebuggerStops: true,
emitSourceFiles: true, emitSourceFiles: true,
emitAnnotations: true)); emitAnnotations: true));
}); });

View file

@ -513,6 +513,11 @@ class BytecodeAssembler {
_emitInstructionA(Opcode.kCheckStack, ra); _emitInstructionA(Opcode.kCheckStack, ra);
} }
void emitDebugCheck() {
emitSourcePosition();
_emitInstruction0(Opcode.kDebugCheck);
}
void emitCheckFunctionTypeArgs(int ra, int re) { void emitCheckFunctionTypeArgs(int ra, int re) {
emitSourcePosition(); emitSourcePosition();
_emitInstructionAE(Opcode.kCheckFunctionTypeArgs, ra, re); _emitInstructionAE(Opcode.kCheckFunctionTypeArgs, ra, re);

View file

@ -10,7 +10,7 @@ library vm.bytecode.dbc;
/// Before bumping current bytecode version format, make sure that /// Before bumping current bytecode version format, make sure that
/// all users have switched to a VM which is able to consume new /// all users have switched to a VM which is able to consume new
/// version of bytecode. /// version of bytecode.
const int currentBytecodeFormatVersion = 12; const int currentBytecodeFormatVersion = 13;
enum Opcode { enum Opcode {
kUnusedOpcode000, kUnusedOpcode000,
@ -99,8 +99,6 @@ enum Opcode {
kUnusedOpcode083, kUnusedOpcode083,
kUnusedOpcode084, kUnusedOpcode084,
// Bytecode instructions since bytecode format v7:
kTrap, kTrap,
// Prologue and stack management. // Prologue and stack management.
@ -117,7 +115,7 @@ enum Opcode {
kCheckFunctionTypeArgs, kCheckFunctionTypeArgs,
kCheckFunctionTypeArgs_Wide, kCheckFunctionTypeArgs_Wide,
kCheckStack, kCheckStack,
kUnused01, kDebugCheck,
kUnused02, // Reserved for CheckParameterTypes kUnused02, // Reserved for CheckParameterTypes
kUnused03, // Reserved for CheckParameterTypes_Wide kUnused03, // Reserved for CheckParameterTypes_Wide
@ -370,6 +368,8 @@ const Map<Opcode, Format> BytecodeFormats = const {
Encoding.kAE, const [Operand.imm, Operand.reg, Operand.none]), Encoding.kAE, const [Operand.imm, Operand.reg, Operand.none]),
Opcode.kCheckStack: const Format( Opcode.kCheckStack: const Format(
Encoding.kA, const [Operand.imm, Operand.none, Operand.none]), Encoding.kA, const [Operand.imm, Operand.none, Operand.none]),
Opcode.kDebugCheck: const Format(
Encoding.k0, const [Operand.none, Operand.none, Operand.none]),
Opcode.kAllocate: const Format( Opcode.kAllocate: const Format(
Encoding.kD, const [Operand.lit, Operand.none, Operand.none]), Encoding.kD, const [Operand.lit, Operand.none, Operand.none]),
Opcode.kAllocateT: const Format( Opcode.kAllocateT: const Format(

View file

@ -1534,12 +1534,6 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
} else { } else {
asm.emitEntry(locals.frameSize); asm.emitEntry(locals.frameSize);
} }
// TODO(alexmarkov): Introduce a new bytecode triggering a debug check in
// the interpreter. Its token position should correspond to the declaration
// position of the last parameter, which the debugger can inspect at the
// point of the debug check.
// TODO(regis): Support the new bytecode in the interpreter and dissociate
// the debug check from the CheckStack bytecode.
asm.emitCheckStack(0); asm.emitCheckStack(0);
if (isClosure) { if (isClosure) {
@ -1666,6 +1660,23 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
function.positionalParameters.forEach(_copyParamIfCaptured); function.positionalParameters.forEach(_copyParamIfCaptured);
locals.sortedNamedParameters.forEach(_copyParamIfCaptured); locals.sortedNamedParameters.forEach(_copyParamIfCaptured);
} }
if (options.emitDebuggerStops) {
// DebugCheck instruction should be emitted after parameter variables
// are declared and copied into context.
// The debugger expects the source position to correspond to the
// declaration position of the last parameter, if any, or of the function.
if (options.emitSourcePositions && function != null) {
var pos = function.fileOffset;
if (function.namedParameters.isNotEmpty) {
pos = function.namedParameters.last.fileOffset;
} else if (function.positionalParameters.isNotEmpty) {
pos = function.positionalParameters.last.fileOffset;
}
_recordSourcePosition(pos);
}
asm.emitDebugCheck();
}
} }
void _copyParamIfCaptured(VariableDeclaration variable) { void _copyParamIfCaptured(VariableDeclaration variable) {

View file

@ -9,6 +9,7 @@ class BytecodeOptions {
static Map<String, String> commandLineFlags = { static Map<String, String> commandLineFlags = {
'annotations': 'Emit Dart annotations', 'annotations': 'Emit Dart annotations',
'local-var-info': 'Emit debug information about local variables', 'local-var-info': 'Emit debug information about local variables',
'debugger-stops': 'Emit bytecode instructions for stopping in the debugger',
'show-bytecode-size-stat': 'Show bytecode size breakdown', 'show-bytecode-size-stat': 'Show bytecode size breakdown',
'source-positions': 'Emit source positions', 'source-positions': 'Emit source positions',
}; };
@ -18,6 +19,7 @@ class BytecodeOptions {
bool emitSourcePositions; bool emitSourcePositions;
bool emitSourceFiles; bool emitSourceFiles;
bool emitLocalVarInfo; bool emitLocalVarInfo;
bool emitDebuggerStops;
bool emitAnnotations; bool emitAnnotations;
bool omitAssertSourcePositions; bool omitAssertSourcePositions;
bool showBytecodeSizeStatistics; bool showBytecodeSizeStatistics;
@ -29,6 +31,7 @@ class BytecodeOptions {
this.emitSourcePositions = false, this.emitSourcePositions = false,
this.emitSourceFiles = false, this.emitSourceFiles = false,
this.emitLocalVarInfo = false, this.emitLocalVarInfo = false,
this.emitDebuggerStops = false,
this.emitAnnotations = false, this.emitAnnotations = false,
this.omitAssertSourcePositions = false, this.omitAssertSourcePositions = false,
this.showBytecodeSizeStatistics = false, this.showBytecodeSizeStatistics = false,
@ -49,6 +52,9 @@ class BytecodeOptions {
case 'local-var-info': case 'local-var-info':
emitLocalVarInfo = true; emitLocalVarInfo = true;
break; break;
case 'debugger-stops':
emitDebuggerStops = true;
break;
case 'annotations': case 'annotations':
emitAnnotations = true; emitAnnotations = true;
break; break;

View file

@ -873,6 +873,15 @@ Fragment BaseFlowGraphBuilder::BuildFfiAsFunctionInternalCall(
return code; return code;
} }
Fragment BaseFlowGraphBuilder::DebugStepCheck(TokenPosition position) {
#ifdef PRODUCT
return Fragment();
#else
return Fragment(new (Z) DebugStepCheckInstr(
position, RawPcDescriptors::kRuntimeCall, GetNextDeoptId()));
#endif
}
} // namespace kernel } // namespace kernel
} // namespace dart } // namespace dart

View file

@ -301,6 +301,8 @@ class BaseFlowGraphBuilder {
const Class& klass, const Class& klass,
intptr_t argument_count); intptr_t argument_count);
Fragment DebugStepCheck(TokenPosition position);
protected: protected:
intptr_t AllocateBlockId() { return ++last_used_block_id_; } intptr_t AllocateBlockId() { return ++last_used_block_id_; }

View file

@ -730,6 +730,13 @@ void BytecodeFlowGraphBuilder::BuildCheckStack() {
} }
} }
void BytecodeFlowGraphBuilder::BuildDebugCheck() {
if (is_generating_interpreter()) {
UNIMPLEMENTED(); // TODO(alexmarkov): interpreter
}
code_ += B->DebugStepCheck(position_);
}
void BytecodeFlowGraphBuilder::BuildPushConstant() { void BytecodeFlowGraphBuilder::BuildPushConstant() {
PushConstant(ConstantAt(DecodeOperandD())); PushConstant(ConstantAt(DecodeOperandD()));
} }

View file

@ -1220,15 +1220,6 @@ bool FlowGraphBuilder::NeedsDebugStepCheck(Value* value,
return definition->IsLoadLocal(); return definition->IsLoadLocal();
} }
Fragment FlowGraphBuilder::DebugStepCheck(TokenPosition position) {
#ifdef PRODUCT
return Fragment();
#else
return Fragment(new (Z) DebugStepCheckInstr(
position, RawPcDescriptors::kRuntimeCall, GetNextDeoptId()));
#endif
}
Fragment FlowGraphBuilder::EvaluateAssertion() { Fragment FlowGraphBuilder::EvaluateAssertion() {
const Class& klass = const Class& klass =
Class::ZoneHandle(Z, Library::LookupCoreClass(Symbols::AssertionError())); Class::ZoneHandle(Z, Library::LookupCoreClass(Symbols::AssertionError()));

View file

@ -212,7 +212,6 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
bool NeedsDebugStepCheck(const Function& function, TokenPosition position); bool NeedsDebugStepCheck(const Function& function, TokenPosition position);
bool NeedsDebugStepCheck(Value* value, TokenPosition position); bool NeedsDebugStepCheck(Value* value, TokenPosition position);
Fragment DebugStepCheck(TokenPosition position);
// Truncates (instead of deoptimizing) if the origin does not fit into the // Truncates (instead of deoptimizing) if the origin does not fit into the
// target representation. // target representation.

View file

@ -570,7 +570,7 @@ namespace dart {
V(CheckFunctionTypeArgs, A_E, ORDN, num, reg, ___) \ V(CheckFunctionTypeArgs, A_E, ORDN, num, reg, ___) \
V(CheckFunctionTypeArgs_Wide, A_E, WIDE, num, reg, ___) \ V(CheckFunctionTypeArgs_Wide, A_E, WIDE, num, reg, ___) \
V(CheckStack, A, ORDN, num, ___, ___) \ V(CheckStack, A, ORDN, num, ___, ___) \
V(Unused01, 0, RESV, ___, ___, ___) \ V(DebugCheck, 0, ORDN, ___, ___, ___) \
V(Unused02, 0, RESV, ___, ___, ___) \ V(Unused02, 0, RESV, ___, ___, ___) \
V(Unused03, 0, RESV, ___, ___, ___) \ V(Unused03, 0, RESV, ___, ___, ___) \
V(Allocate, D, ORDN, lit, ___, ___) \ V(Allocate, D, ORDN, lit, ___, ___) \
@ -749,7 +749,7 @@ class KernelBytecode {
// Maximum bytecode format version supported by VM. // Maximum bytecode format version supported by VM.
// The range of supported versions should include version produced by bytecode // The range of supported versions should include version produced by bytecode
// generator (currentBytecodeFormatVersion in pkg/vm/lib/bytecode/dbc.dart). // generator (currentBytecodeFormatVersion in pkg/vm/lib/bytecode/dbc.dart).
static const intptr_t kMaxSupportedBytecodeFormatVersion = 12; static const intptr_t kMaxSupportedBytecodeFormatVersion = 13;
enum Opcode { enum Opcode {
#define DECLARE_BYTECODE(name, encoding, kind, op1, op2, op3) k##name, #define DECLARE_BYTECODE(name, encoding, kind, op1, op2, op3) k##name,
@ -969,7 +969,7 @@ class KernelBytecode {
case KernelBytecode::kStoreLocal_Wide: case KernelBytecode::kStoreLocal_Wide:
case KernelBytecode::kStoreStaticTOS: case KernelBytecode::kStoreStaticTOS:
case KernelBytecode::kStoreStaticTOS_Wide: case KernelBytecode::kStoreStaticTOS_Wide:
case KernelBytecode::kCheckStack: case KernelBytecode::kDebugCheck:
case KernelBytecode::kDirectCall: case KernelBytecode::kDirectCall:
case KernelBytecode::kDirectCall_Wide: case KernelBytecode::kDirectCall_Wide:
case KernelBytecode::kInterfaceCall: case KernelBytecode::kInterfaceCall:

View file

@ -1750,7 +1750,6 @@ SwitchDispatch:
{ {
BYTECODE(CheckStack, A); BYTECODE(CheckStack, A);
DEBUG_CHECK;
{ {
// Check the interpreter's own stack limit for actual interpreter's stack // Check the interpreter's own stack limit for actual interpreter's stack
// overflows, and also the thread's stack limit for scheduled interrupts. // overflows, and also the thread's stack limit for scheduled interrupts.
@ -1775,6 +1774,12 @@ SwitchDispatch:
DISPATCH(); DISPATCH();
} }
{
BYTECODE(DebugCheck, 0);
DEBUG_CHECK;
DISPATCH();
}
{ {
BYTECODE(CheckFunctionTypeArgs, A_E); BYTECODE(CheckFunctionTypeArgs, A_E);
const intptr_t declared_type_args_len = rA; const intptr_t declared_type_args_len = rA;