Files
Tess Strickland 52cfd29cbb [vm,dynamic_modules] Add RecordCoverage instruction.
The RecordCoverage instruction has an A/E encoding. The A argument
is the type of coverage being recorded, whereas the E argument is
the logical index into the coverage array for updating whether that
source position has been hit.

Also adds new metadata to the bytecode component for the coverage
arrays associated with bytecode containing RecordCoverage instructions
and a new runtime entry for lazily allocate the coverage array for
an interpreted function when needed.

The type of coverage is encoded in the RecordCoverage instruction,
despite being redundant with the information in the coverage array, so that checking whether that type of coverage is currently enabled at
runtime doesn't require either accessing the coverage array (which may
be lazily allocated), forcing allocation of the coverage array just to
discover that type of coverage is currently disabled, or reading the
serialized bytecode component to avoid that forced allocation.

------

Other changes:

Source reporting now treats unexecuted interpreted functions when
not forcing compilation as if they were uncompiled native functions,
so that the source report from running the same code gives the same
result whether using the interpreter or the native compiler.

Bytecode closures are no longer skipped in source reports. Previously
any closure without a context scope was skipped, but bytecode closures
don't have those.

TEST=vm/cc/SourceReport_Coverage

Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try,vm-aot-dyn-linux-debug-x64-try,vm-aot-dyn-linux-product-x64-try
Change-Id: I7557e5dd4c98331c7ca2f5c867dd5f6d03e9d756
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501520
Reviewed-by: Alexander Markov <alexmarkov@google.com>
2026-05-19 04:27:39 -07:00

912 lines
21 KiB
Dart

// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/// Dart kernel bytecode instructions (described in runtime/vm/constants_kbc.h).
library;
/// Version of bytecode format
/// (should match runtime/vm/constants_kbc.h).
const int bytecodeFormatVersion = 3;
enum Opcode {
kTrap,
kUnused00,
// Prologue and stack management.
kEntry,
kEntry_Wide,
kEntryOptional,
kEntrySuspendable,
kLoadConstant,
kLoadConstant_Wide,
kFrame,
kFrame_Wide,
kCheckFunctionTypeArgs,
kCheckFunctionTypeArgs_Wide,
kCheckStack,
kNop,
kJumpIfUnchecked,
kJumpIfUnchecked_Wide,
// Object allocation.
kAllocate,
kAllocate_Wide,
kAllocateT,
kCreateArrayTOS,
// Closure allocation and access.
kAllocateClosure,
kAllocateClosure_Wide,
kLoadClosureElement,
kLoadClosureElement_Wide,
kStoreClosureElement,
kStoreClosureElement_Wide,
// Context allocation and access.
kAllocateContext,
kAllocateContext_Wide,
kCloneContext,
kCloneContext_Wide,
kLoadContextParent,
kStoreContextParent,
kLoadContextVar,
kLoadContextVar_Wide,
kUnused04, // Reserved for LoadContextVar0
kUnused05,
kStoreContextVar,
kStoreContextVar_Wide,
// Constants.
kPushConstant,
kPushConstant_Wide,
kUnused06, // Reserved for PushConstant0
kUnused07,
kPushTrue,
kPushFalse,
kPushInt,
kPushInt_Wide,
kUnused08, // Reserved for PushInt0
kUnused09, // Reserved for PushInt1
kUnused10, // Reserved for PushInt2
kUnused11,
kPushNull,
// Locals and expression stack.
kDrop1,
kPush,
kPush_Wide,
kUnused12, // Reserved for PushLocal0
kUnused13, // Reserved for PushLocal1
kUnused14, // Reserved for PushLocal2
kUnused15, // Reserved for PushLocal3
kUnused16, // Reserved for PushParamLast0
kUnused17, // Reserved for PushParamLast1
kPopLocal,
kPopLocal_Wide,
kLoadStatic,
kLoadStatic_Wide,
kStoreLocal,
kStoreLocal_Wide,
// Instance fields and arrays.
kLoadFieldTOS,
kLoadFieldTOS_Wide,
kStoreFieldTOS,
kStoreFieldTOS_Wide,
kStoreIndexedTOS,
kUnused20,
// Late variables.
kJumpIfInitialized,
kJumpIfInitialized_Wide,
kPushUninitializedSentinel,
kUnused21,
// Late fields.
kInitLateField,
kInitLateField_Wide,
// Static fields.
kStoreStaticTOS,
kStoreStaticTOS_Wide,
// Jumps.
kJump,
kJump_Wide,
kJumpIfNoAsserts,
kJumpIfNoAsserts_Wide,
kJumpIfNotZeroTypeArgs,
kJumpIfNotZeroTypeArgs_Wide,
kJumpIfEqStrict,
kJumpIfEqStrict_Wide,
kJumpIfNeStrict,
kJumpIfNeStrict_Wide,
kJumpIfTrue,
kJumpIfTrue_Wide,
kJumpIfFalse,
kJumpIfFalse_Wide,
kJumpIfNull,
kJumpIfNull_Wide,
kJumpIfNotNull,
kJumpIfNotNull_Wide,
kSuspend,
kSuspend_Wide,
// Calls.
kDirectCall,
kDirectCall_Wide,
kUncheckedDirectCall,
kUncheckedDirectCall_Wide,
kInterfaceCall,
kInterfaceCall_Wide,
kExternalCall,
kExternalCall_Wide,
kInstantiatedInterfaceCall,
kInstantiatedInterfaceCall_Wide,
kUncheckedClosureCall,
kUncheckedClosureCall_Wide,
kUncheckedInterfaceCall,
kUncheckedInterfaceCall_Wide,
kDynamicCall,
kDynamicCall_Wide,
kReturnTOS,
kUnused25,
// Types and type checks.
kAssertAssignable,
kAssertAssignable_Wide,
kAssertSubtype,
kUnused30,
kLoadTypeArgumentsField,
kLoadTypeArgumentsField_Wide,
kInstantiateType,
kInstantiateType_Wide,
kInstantiateTypeArgumentsTOS,
kInstantiateTypeArgumentsTOS_Wide,
kUnused32, // Reserved for IsType
kUnused33, // Reserved for IsType_Wide
kUnused34, // Reserved for IsSimpleType
kUnused35, // Reserved for IsSimpleType_Wide
// Exception handling.
kThrow,
kSetFrame,
kMoveSpecial,
kMoveSpecial_Wide,
// Bool operations.
kBooleanNegateTOS,
// Null operations.
kEqualsNull,
kNullCheck,
kNullCheck_Wide,
// Int operations.
kNegateInt,
kAddInt,
kSubInt,
kMulInt,
kTruncDivInt,
kModInt,
kBitAndInt,
kBitOrInt,
kBitXorInt,
kShlInt,
kShrInt,
kCompareIntEq,
kCompareIntGt,
kCompareIntLt,
kCompareIntGe,
kCompareIntLe,
// Double operations.
kNegateDouble,
kAddDouble,
kSubDouble,
kMulDouble,
kDivDouble,
kCompareDoubleEq,
kCompareDoubleGt,
kCompareDoubleLt,
kCompareDoubleGe,
kCompareDoubleLe,
// Records
kAllocateRecord,
kAllocateRecord_Wide,
kLoadRecordField,
kLoadRecordField_Wide,
// FFI
kFfiCall,
kFfiCall_Wide,
// Coverage
kRecordCoverage,
kRecordCoverage_Wide,
}
/// Compact variants of opcodes are always even.
/// Wide variant = opcode + kWideModifier.
const int kWideModifier = 1;
/// Opcode should fit into 1 byte.
const int kMaxOpcodes = 256;
enum Encoding {
k0, // No operands.
kA, // 1 operand: A = 8-bit unsigned.
kD, // 1 operand: D = 8/32-bit unsigned.
kX, // 1 operand: X = 8/32-bit signed.
kT, // 1 operand: T = 8/24-bit signed.
kAE, // 2 operands: A = 8-bit unsigned, E = 8/32-bit unsigned
kAY, // 2 operands: A = 8-bit unsigned, Y = 8/32-bit signed
kDF, // 2 operands: D = 8/32-bit unsigned, F = 8-bit unsigned
kABC, // 3 operands: A, B, C - 8-bit unsigned.
}
int instructionSize(Encoding encoding, bool isWide) {
switch (encoding) {
case Encoding.k0:
return 1;
case Encoding.kA:
return 2;
case Encoding.kD:
return isWide ? 5 : 2;
case Encoding.kX:
return isWide ? 5 : 2;
case Encoding.kT:
return isWide ? 4 : 2;
case Encoding.kAE:
return isWide ? 6 : 3;
case Encoding.kAY:
return isWide ? 6 : 3;
case Encoding.kDF:
return isWide ? 6 : 3;
case Encoding.kABC:
return 4;
}
}
enum Operand {
none, // ignored / non-existent operand
imm, // immediate operand
lit, // constant literal from object pool
reg, // register (unsigned FP relative local)
xeg, // x-register (signed FP relative local)
tgt, // jump target relative to the PC of the current instruction
spe, // SpecialIndex
}
class Format {
final Encoding encoding;
final List<Operand> operands;
const Format(this.encoding, this.operands);
}
const Map<Opcode, Format> BytecodeFormats = const {
Opcode.kTrap: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kEntry: const Format(Encoding.kD, const [
Operand.imm,
Operand.none,
Operand.none,
]),
Opcode.kEntryOptional: const Format(Encoding.kABC, const [
Operand.imm,
Operand.imm,
Operand.imm,
]),
Opcode.kEntrySuspendable: const Format(Encoding.kABC, const [
Operand.imm,
Operand.imm,
Operand.imm,
]),
Opcode.kLoadConstant: const Format(Encoding.kAE, const [
Operand.reg,
Operand.lit,
Operand.none,
]),
Opcode.kFrame: const Format(Encoding.kD, const [
Operand.imm,
Operand.none,
Operand.none,
]),
Opcode.kCheckFunctionTypeArgs: const Format(Encoding.kAE, const [
Operand.imm,
Operand.reg,
Operand.none,
]),
Opcode.kCheckStack: const Format(Encoding.kA, const [
Operand.imm,
Operand.none,
Operand.none,
]),
Opcode.kNop: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kAllocate: const Format(Encoding.kD, const [
Operand.lit,
Operand.none,
Operand.none,
]),
Opcode.kAllocateT: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kCreateArrayTOS: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kAllocateContext: const Format(Encoding.kAE, const [
Operand.imm,
Operand.imm,
Operand.none,
]),
Opcode.kCloneContext: const Format(Encoding.kAE, const [
Operand.imm,
Operand.imm,
Operand.none,
]),
Opcode.kLoadContextParent: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kStoreContextParent: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kLoadContextVar: const Format(Encoding.kAE, const [
Operand.imm,
Operand.imm,
Operand.none,
]),
Opcode.kStoreContextVar: const Format(Encoding.kAE, const [
Operand.imm,
Operand.imm,
Operand.none,
]),
Opcode.kPushConstant: const Format(Encoding.kD, const [
Operand.lit,
Operand.none,
Operand.none,
]),
Opcode.kPushNull: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kPushTrue: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kPushFalse: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kPushInt: const Format(Encoding.kX, const [
Operand.imm,
Operand.none,
Operand.none,
]),
Opcode.kDrop1: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kPush: const Format(Encoding.kX, const [
Operand.xeg,
Operand.none,
Operand.none,
]),
Opcode.kPopLocal: const Format(Encoding.kX, const [
Operand.xeg,
Operand.none,
Operand.none,
]),
Opcode.kStoreLocal: const Format(Encoding.kX, const [
Operand.xeg,
Operand.none,
Operand.none,
]),
Opcode.kLoadFieldTOS: const Format(Encoding.kD, const [
Operand.lit,
Operand.none,
Operand.none,
]),
Opcode.kStoreFieldTOS: const Format(Encoding.kD, const [
Operand.lit,
Operand.none,
Operand.none,
]),
Opcode.kStoreIndexedTOS: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kJumpIfInitialized: const Format(Encoding.kT, const [
Operand.tgt,
Operand.none,
Operand.none,
]),
Opcode.kPushUninitializedSentinel: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kInitLateField: const Format(Encoding.kD, const [
Operand.lit,
Operand.none,
Operand.none,
]),
Opcode.kLoadStatic: const Format(Encoding.kD, const [
Operand.lit,
Operand.none,
Operand.none,
]),
Opcode.kStoreStaticTOS: const Format(Encoding.kD, const [
Operand.lit,
Operand.none,
Operand.none,
]),
Opcode.kJump: const Format(Encoding.kT, const [
Operand.tgt,
Operand.none,
Operand.none,
]),
Opcode.kJumpIfNoAsserts: const Format(Encoding.kT, const [
Operand.tgt,
Operand.none,
Operand.none,
]),
Opcode.kJumpIfNotZeroTypeArgs: const Format(Encoding.kT, const [
Operand.tgt,
Operand.none,
Operand.none,
]),
Opcode.kJumpIfEqStrict: const Format(Encoding.kT, const [
Operand.tgt,
Operand.none,
Operand.none,
]),
Opcode.kJumpIfNeStrict: const Format(Encoding.kT, const [
Operand.tgt,
Operand.none,
Operand.none,
]),
Opcode.kJumpIfTrue: const Format(Encoding.kT, const [
Operand.tgt,
Operand.none,
Operand.none,
]),
Opcode.kJumpIfFalse: const Format(Encoding.kT, const [
Operand.tgt,
Operand.none,
Operand.none,
]),
Opcode.kJumpIfNull: const Format(Encoding.kT, const [
Operand.tgt,
Operand.none,
Operand.none,
]),
Opcode.kJumpIfNotNull: const Format(Encoding.kT, const [
Operand.tgt,
Operand.none,
Operand.none,
]),
Opcode.kJumpIfUnchecked: const Format(Encoding.kT, const [
Operand.tgt,
Operand.none,
Operand.none,
]),
Opcode.kSuspend: const Format(Encoding.kT, const [
Operand.tgt,
Operand.none,
Operand.none,
]),
Opcode.kInterfaceCall: const Format(Encoding.kDF, const [
Operand.lit,
Operand.imm,
Operand.none,
]),
Opcode.kInstantiatedInterfaceCall: const Format(Encoding.kDF, const [
Operand.lit,
Operand.imm,
Operand.none,
]),
Opcode.kDynamicCall: const Format(Encoding.kDF, const [
Operand.lit,
Operand.imm,
Operand.none,
]),
Opcode.kExternalCall: const Format(Encoding.kD, const [
Operand.lit,
Operand.none,
Operand.none,
]),
Opcode.kReturnTOS: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kAssertAssignable: const Format(Encoding.kAE, const [
Operand.imm,
Operand.lit,
Operand.none,
]),
Opcode.kAssertSubtype: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kLoadTypeArgumentsField: const Format(Encoding.kD, const [
Operand.lit,
Operand.none,
Operand.none,
]),
Opcode.kInstantiateType: const Format(Encoding.kD, const [
Operand.lit,
Operand.none,
Operand.none,
]),
Opcode.kInstantiateTypeArgumentsTOS: const Format(Encoding.kAE, const [
Operand.imm,
Operand.lit,
Operand.none,
]),
Opcode.kThrow: const Format(Encoding.kA, const [
Operand.imm,
Operand.none,
Operand.none,
]),
Opcode.kMoveSpecial: const Format(Encoding.kAY, const [
Operand.spe,
Operand.xeg,
Operand.none,
]),
Opcode.kSetFrame: const Format(Encoding.kA, const [
Operand.imm,
Operand.none,
Operand.none,
]),
Opcode.kBooleanNegateTOS: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kEqualsNull: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kNullCheck: const Format(Encoding.kD, const [
Operand.lit,
Operand.none,
Operand.none,
]),
Opcode.kNegateInt: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kAddInt: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kSubInt: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kMulInt: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kTruncDivInt: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kModInt: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kBitAndInt: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kBitOrInt: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kBitXorInt: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kShlInt: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kShrInt: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kCompareIntEq: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kCompareIntGt: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kCompareIntLt: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kCompareIntGe: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kCompareIntLe: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kDirectCall: const Format(Encoding.kDF, const [
Operand.lit,
Operand.imm,
Operand.none,
]),
Opcode.kUncheckedDirectCall: const Format(Encoding.kDF, const [
Operand.lit,
Operand.imm,
Operand.none,
]),
Opcode.kAllocateClosure: const Format(Encoding.kD, const [
Operand.lit,
Operand.none,
Operand.none,
]),
Opcode.kLoadClosureElement: const Format(Encoding.kD, const [
Operand.imm,
Operand.none,
Operand.none,
]),
Opcode.kStoreClosureElement: const Format(Encoding.kD, const [
Operand.imm,
Operand.none,
Operand.none,
]),
Opcode.kUncheckedClosureCall: const Format(Encoding.kDF, const [
Operand.lit,
Operand.imm,
Operand.none,
]),
Opcode.kUncheckedInterfaceCall: const Format(Encoding.kDF, const [
Operand.lit,
Operand.imm,
Operand.none,
]),
Opcode.kNegateDouble: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kAddDouble: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kSubDouble: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kMulDouble: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kDivDouble: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kCompareDoubleEq: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kCompareDoubleGt: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kCompareDoubleLt: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kCompareDoubleGe: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kCompareDoubleLe: const Format(Encoding.k0, const [
Operand.none,
Operand.none,
Operand.none,
]),
Opcode.kAllocateRecord: const Format(Encoding.kD, const [
Operand.lit,
Operand.none,
Operand.none,
]),
Opcode.kLoadRecordField: const Format(Encoding.kD, const [
Operand.imm,
Operand.none,
Operand.none,
]),
Opcode.kFfiCall: const Format(Encoding.kD, const [
Operand.lit,
Operand.none,
Operand.none,
]),
Opcode.kRecordCoverage: const Format(Encoding.kAE, const [
Operand.imm,
Operand.imm,
Operand.none,
]),
};
// Should match constant in runtime/vm/stack_frame_kbc.h.
const int kParamEndSlotFromFp = 4;
enum SpecialIndex { exception, stackTrace }
/// Returns [true] if there is a wide variant for the given opcode.
bool hasWideVariant(Opcode opcode) {
final encoding = BytecodeFormats[opcode]!.encoding;
switch (encoding) {
case Encoding.k0:
case Encoding.kA:
case Encoding.kABC:
return false;
case Encoding.kD:
case Encoding.kX:
case Encoding.kT:
case Encoding.kAE:
case Encoding.kAY:
case Encoding.kDF:
return true;
}
}
bool isWideOpcode(Opcode opcode) {
return (BytecodeFormats[opcode] == null) &&
hasWideVariant(Opcode.values[opcode.index - kWideModifier]);
}
Opcode fromWideOpcode(Opcode opcode) {
assert(isWideOpcode(opcode));
return Opcode.values[opcode.index - kWideModifier];
}
void verifyBytecodeInstructionDeclarations() {
const String kWideSuffix = '_Wide';
for (Opcode opcode in Opcode.values) {
final format = BytecodeFormats[opcode];
if (opcode.toString().endsWith(kWideSuffix)) {
if (format != null) {
throw 'Bytecode format should not be defined for wide opcode $opcode.';
}
final Opcode compact = Opcode.values[opcode.index - kWideModifier];
if (compact.toString() + kWideSuffix != opcode.toString()) {
throw 'Wide opcode $opcode should immediately follow its compact opcode (previous opcode is $compact).';
}
if (!hasWideVariant(compact)) {
throw 'Wide opcode $opcode should not be defined for opcode $compact with encoding ${BytecodeFormats[compact]!.encoding}.';
}
}
if (format == null) {
continue;
}
if (hasWideVariant(opcode)) {
if (Opcode.values[opcode.index + kWideModifier].toString() !=
opcode.toString() + kWideSuffix) {
throw 'Opcode $opcode$kWideSuffix should immedialy follow $opcode.';
}
if (opcode.index.isOdd) {
throw 'Opcode $opcode (${format.encoding}) has a wide variant and should be even';
}
}
}
if (Opcode.values.length > kMaxOpcodes) {
throw 'Too many opcodes';
}
}
bool isJump(Opcode opcode) => BytecodeFormats[opcode]!.encoding == Encoding.kT;
bool isThrow(Opcode opcode) => opcode == Opcode.kThrow;
bool isCall(Opcode opcode) {
switch (opcode) {
case Opcode.kDirectCall:
case Opcode.kUncheckedDirectCall:
case Opcode.kInterfaceCall:
case Opcode.kInstantiatedInterfaceCall:
case Opcode.kUncheckedClosureCall:
case Opcode.kUncheckedInterfaceCall:
case Opcode.kDynamicCall:
return true;
default:
return false;
}
}
bool isReturn(Opcode opcode) => (opcode == Opcode.kReturnTOS);
bool isControlFlow(Opcode opcode) =>
isJump(opcode) || isThrow(opcode) || isCall(opcode) || isReturn(opcode);
bool isPush(Opcode opcode) {
switch (opcode) {
case Opcode.kPush:
case Opcode.kPushConstant:
case Opcode.kPushNull:
case Opcode.kPushTrue:
case Opcode.kPushFalse:
case Opcode.kPushInt:
case Opcode.kPushUninitializedSentinel:
return true;
default:
return false;
}
}
// Bytecode instructions reference constant pool indices using
// unsigned 32-bit operands.
const int constantPoolIndexLimit = 1 << 32;
// Local variables are referenced using 32-bit signed operands.
const int localVariableIndexLimit = 1 << 31;
// Captured variables are referenced using 32-bit unsigned operands.
const int capturedVariableIndexLimit = 1 << 32;
// Context IDs are referenced using 8-bit unsigned operands.
const int contextIdLimit = 1 << 8;
// Number of arguments is encoded as 8-bit unsigned operand.
const int argumentsLimit = 1 << 8;
// Base class for exceptions thrown when certain limit of bytecode
// format is exceeded.
abstract class BytecodeLimitExceededException {}