| // Copyright (c) 2018, 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 vm.bytecode.dbc; |
| |
| /// Version of bytecode format, produced by default. |
| /// Before bumping current bytecode version format, make sure that |
| /// all users have switched to a VM which is able to consume new |
| /// version of bytecode. |
| const int currentBytecodeFormatVersion = 27; |
| |
| enum Opcode { |
| kUnusedOpcode000, |
| kUnusedOpcode001, |
| kUnusedOpcode002, |
| kUnusedOpcode003, |
| kUnusedOpcode004, |
| kUnusedOpcode005, |
| kUnusedOpcode006, |
| kUnusedOpcode007, |
| kUnusedOpcode008, |
| kUnusedOpcode009, |
| kUnusedOpcode010, |
| kUnusedOpcode011, |
| kUnusedOpcode012, |
| kUnusedOpcode013, |
| kUnusedOpcode014, |
| kUnusedOpcode015, |
| kUnusedOpcode016, |
| kUnusedOpcode017, |
| kUnusedOpcode018, |
| kUnusedOpcode019, |
| kUnusedOpcode020, |
| kUnusedOpcode021, |
| kUnusedOpcode022, |
| kUnusedOpcode023, |
| kUnusedOpcode024, |
| kUnusedOpcode025, |
| kUnusedOpcode026, |
| kUnusedOpcode027, |
| kUnusedOpcode028, |
| kUnusedOpcode029, |
| kUnusedOpcode030, |
| kUnusedOpcode031, |
| kUnusedOpcode032, |
| kUnusedOpcode033, |
| kUnusedOpcode034, |
| kUnusedOpcode035, |
| kUnusedOpcode036, |
| kUnusedOpcode037, |
| kUnusedOpcode038, |
| kUnusedOpcode039, |
| kUnusedOpcode040, |
| kUnusedOpcode041, |
| kUnusedOpcode042, |
| kUnusedOpcode043, |
| kUnusedOpcode044, |
| kUnusedOpcode045, |
| kUnusedOpcode046, |
| kUnusedOpcode047, |
| kUnusedOpcode048, |
| kUnusedOpcode049, |
| kUnusedOpcode050, |
| kUnusedOpcode051, |
| kUnusedOpcode052, |
| kUnusedOpcode053, |
| kUnusedOpcode054, |
| kUnusedOpcode055, |
| kUnusedOpcode056, |
| kUnusedOpcode057, |
| kUnusedOpcode058, |
| kUnusedOpcode059, |
| kUnusedOpcode060, |
| kUnusedOpcode061, |
| kUnusedOpcode062, |
| kUnusedOpcode063, |
| kUnusedOpcode064, |
| kUnusedOpcode065, |
| kUnusedOpcode066, |
| kUnusedOpcode067, |
| kUnusedOpcode068, |
| kUnusedOpcode069, |
| kUnusedOpcode070, |
| kUnusedOpcode071, |
| kUnusedOpcode072, |
| kUnusedOpcode073, |
| kUnusedOpcode074, |
| kUnusedOpcode075, |
| kUnusedOpcode076, |
| kUnusedOpcode077, |
| kUnusedOpcode078, |
| kUnusedOpcode079, |
| kUnusedOpcode080, |
| kUnusedOpcode081, |
| |
| // Late variables. |
| kJumpIfInitialized, |
| kJumpIfInitialized_Wide, |
| kPushUninitializedSentinel, |
| |
| kTrap, |
| |
| // Prologue and stack management. |
| kEntry, |
| kEntry_Wide, |
| kEntryFixed, |
| kEntryFixed_Wide, |
| kEntryOptional, |
| kUnused00, // Reserved for EntryNoLocals. |
| kLoadConstant, |
| kLoadConstant_Wide, |
| kFrame, |
| kFrame_Wide, |
| kCheckFunctionTypeArgs, |
| kCheckFunctionTypeArgs_Wide, |
| kCheckStack, |
| kDebugCheck, |
| kJumpIfUnchecked, |
| kJumpIfUnchecked_Wide, |
| |
| // Object allocation. |
| kAllocate, |
| kAllocate_Wide, |
| kAllocateT, |
| kCreateArrayTOS, |
| kAllocateClosure, |
| kAllocateClosure_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 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, |
| |
| // Calls. |
| kDirectCall, |
| kDirectCall_Wide, |
| kUncheckedDirectCall, |
| kUncheckedDirectCall_Wide, |
| kInterfaceCall, |
| kInterfaceCall_Wide, |
| kUnused23, // Reserved for InterfaceCall1 |
| kUnused24, // Reserved for InterfaceCall1_Wide |
| kInstantiatedInterfaceCall, |
| kInstantiatedInterfaceCall_Wide, |
| kUncheckedClosureCall, |
| kUncheckedClosureCall_Wide, |
| kUncheckedInterfaceCall, |
| kUncheckedInterfaceCall_Wide, |
| kDynamicCall, |
| kDynamicCall_Wide, |
| kNativeCall, |
| kNativeCall_Wide, |
| kReturnTOS, |
| kUnused29, |
| |
| // Types and type checks. |
| kAssertAssignable, |
| kAssertAssignable_Wide, |
| kUnused30, // Reserved for AsSimpleType |
| kUnused31, // Reserved for AsSimpleType_Wide |
| kAssertBoolean, |
| kAssertSubtype, |
| 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, |
| } |
| |
| /// 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; |
| } |
| throw 'Unexpected instruction encoding $encoding'; |
| } |
| |
| 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.kEntryFixed: const Format( |
| Encoding.kAE, const [Operand.imm, Operand.imm, Operand.none]), |
| Opcode.kEntryOptional: 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.kDebugCheck: 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.kInitLateField: const Format( |
| Encoding.kD, const [Operand.lit, Operand.none, Operand.none]), |
| Opcode.kPushUninitializedSentinel: 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.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.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.kNativeCall: 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.kAssertBoolean: const Format( |
| Encoding.kA, const [Operand.imm, Operand.none, 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.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]), |
| }; |
| |
| // 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; |
| } |
| throw 'Unexpected instruction encoding $encoding'; |
| } |
| |
| 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: |
| case Opcode.kNativeCall: |
| 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 {} |