| // 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, | |
| // Prologue and stack management. | |
| kDup, | |
| 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, | |
| // More FFI | |
| kResolveNativeFunction, | |
| kResolveNativeFunction_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, | |
| ]), | |
| Opcode.kDup: const Format(Encoding.k0, const [ | |
| Operand.none, | |
| Operand.none, | |
| Operand.none, | |
| ]), | |
| Opcode.kResolveNativeFunction: const Format(Encoding.kD, const [ | |
| Operand.lit, | |
| 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; | |
| } | |
| } | |
| 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 {} |