mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:49:00 +00:00
[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:
parent
fc542be6b4
commit
9503969664
12 changed files with 62 additions and 26 deletions
|
@ -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));
|
||||||
});
|
});
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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_; }
|
||||||
|
|
||||||
|
|
|
@ -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()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()));
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue