// 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. #ifndef RUNTIME_VM_CONSTANTS_KBC_H_ #define RUNTIME_VM_CONSTANTS_KBC_H_ #include "platform/assert.h" #include "platform/globals.h" #include "platform/utils.h" namespace dart { // clang-format off // List of KernelBytecode instructions. // // INTERPRETER STATE // // current frame info (see stack_frame_kbc.h for layout) // v-----^-----v // ~----+----~ ~----+-------+-------+-~ ~-+-------+-------+-~ // ~ | ~ ~ | FP[0] | FP[1] | ~ ~ | SP[-1]| SP[0] | // ~----+----~ ~----+-------+-------+-~ ~-+-------+-------+-~ // ^ ^ // FP SP // // // The state of execution is captured in few interpreter registers: // // FP - base of the current frame // SP - top of the stack (TOS) for the current frame // PP - object pool for the currently execution function // // Frame info stored below FP additionally contains pointers to the currently // executing function and code (see stack_frame_dbc.h for more information). // // In the unoptimized code most of bytecodes take operands implicitly from // stack and store results again on the stack. Constant operands are usually // taken from the object pool by index. // // ENCODING // // Each instruction starts with opcode byte. Certain instructions have // wide encoding variant. In such case, the least significant bit of opcode is // not set for compact variant and set for wide variant. // // The following operand encodings are used: // // 0........8.......16.......24.......32.......40.......48 // +--------+ // | opcode | 0: no operands // +--------+ // // +--------+--------+ // | opcode | A | A: unsigned 8-bit operand // +--------+--------+ // // +--------+--------+ // | opcode | D | D: unsigned 8/32-bit operand // +--------+--------+ // // +--------+----------------------------------+ // | opcode | D | D (wide) // +--------+----------------------------------+ // // +--------+--------+ // | opcode | X | X: signed 8/32-bit operand // +--------+--------+ // // +--------+----------------------------------+ // | opcode | X | X (wide) // +--------+----------------------------------+ // // +--------+--------+ // | opcode | T | T: signed 8/24-bit operand // +--------+--------+ // // +--------+--------------------------+ // | opcode | T | T (wide) // +--------+--------------------------+ // // +--------+--------+--------+ // | opcode | A | E | A_E: unsigned 8-bit operand and // +--------+--------+--------+ unsigned 8/32-bit operand // // +--------+--------+----------------------------------+ // | opcode | A | E | A_E (wide) // +--------+--------+----------------------------------+ // // +--------+--------+--------+ // | opcode | A | Y | A_Y: unsigned 8-bit operand and // +--------+--------+--------+ signed 8/32-bit operand // // +--------+--------+----------------------------------+ // | opcode | A | Y | A_Y (wide) // +--------+--------+----------------------------------+ // // +--------+--------+--------+ // | opcode | D | F | D_F: unsigned 8/32-bit operand and // +--------+--------+--------+ unsigned 8-bit operand // // +--------+----------------------------------+--------+ // | opcode | D | F | D_F (wide) // +--------+----------------------------------+--------+ // // +--------+--------+--------+--------+ // | opcode | A | B | C | A_B_C: 3 unsigned 8-bit operands // +--------+--------+--------+--------+ // // // INSTRUCTIONS // // - Trap // // Unreachable instruction. // // - Entry rD // // Function prologue for the function // rD - number of local slots to reserve; // // - EntryFixed A, D // // Function prologue for functions without optional arguments. // Checks number of arguments. // A - expected number of positional arguments; // D - number of local slots to reserve; // // - EntryOptional A, B, C // // Function prologue for the function with optional or named arguments: // A - expected number of positional arguments; // B - number of optional arguments; // C - number of named arguments; // // Only one of B and C can be not 0. // // If B is not 0 then EntryOptional bytecode is followed by B LoadConstant // bytecodes specifying default values for optional arguments. // // If C is not 0 then EntryOptional is followed by 2 * C LoadConstant // bytecodes. // Bytecode at 2 * i specifies name of the i-th named argument and at // 2 * i + 1 default value. rA part of the LoadConstant bytecode specifies // the location of the parameter on the stack. Here named arguments are // sorted alphabetically to enable linear matching similar to how function // prologues are implemented on other architectures. // // Note: Unlike Entry bytecode EntryOptional does not setup the frame for // local variables this is done by a separate bytecode Frame, which should // follow EntryOptional and its LoadConstant instructions. // // - LoadConstant rA, D // // Used in conjunction with EntryOptional instruction to describe names and // default values of optional parameters. // // - Frame D // // Reserve and initialize with null space for D local variables. // // - CheckFunctionTypeArgs A, D // // Check for a passed-in type argument vector of length A and // store it at FP[D]. // // - CheckStack A // // Compare SP against isolate stack limit and call StackOverflow handler if // necessary. Should be used in prologue (A = 0), or at the beginning of // a loop with depth A. // // - Allocate D // // Allocate object of class PP[D] with no type arguments. // // - AllocateT // // Allocate object of class SP[0] with type arguments SP[-1]. // // - CreateArrayTOS // // Allocate array of length SP[0] with type arguments SP[-1]. // // - AllocateContext A, D // // Allocate Context object holding D context variables. // A is a static ID of the context. Static ID of a context may be used to // disambiguate accesses to different context objects. // Context objects with the same ID should have the same number of // context variables. // // - CloneContext A, D // // Clone Context object SP[0] holding D context variables. // A is a static ID of the context. Cloned context has the same ID. // // - LoadContextParent // // Load parent from context SP[0]. // // - StoreContextParent // // Store context SP[0] into `parent` field of context SP[-1]. // // - LoadContextVar A, D // // Load value from context SP[0] at index D. // A is a static ID of the context. // // - StoreContextVar A, D // // Store value SP[0] into context SP[-1] at index D. // A is a static ID of the context. // // - PushConstant D // // Push value at index D from constant pool onto the stack. // // - PushNull // // Push `null` onto the stack. // // - PushTrue // // Push `true` onto the stack. // // - PushFalse // // Push `false` onto the stack. // // - PushInt rX // // Push int rX onto the stack. // // - Drop1 // // Drop 1 value from the stack // // - Push rX // // Push FP[rX] to the stack. // // - StoreLocal rX; PopLocal rX // // Store top of the stack into FP[rX] and pop it if needed. // // - LoadFieldTOS D // // Push value at offset (in words) PP[D] from object SP[0]. // // - StoreFieldTOS D // // Store value SP[0] into object SP[-1] at offset (in words) PP[D]. // // - StoreIndexedTOS // // Store SP[0] into array SP[-2] at index SP[-1]. No typechecking is done. // SP[-2] is assumed to be a RawArray, SP[-1] to be a smi. // // - PushStatic D // // Pushes value of the static field PP[D] on to the stack. // // - StoreStaticTOS D // // Stores TOS into the static field PP[D]. // // - Jump target // // Jump to the given target. Target is specified as offset from the PC of the // jump instruction. // // - JumpIfNoAsserts target // // Jump to the given target if assertions are not enabled. // Target is specified as offset from the PC of the jump instruction. // // - JumpIfNotZeroTypeArgs target // // Jump to the given target if number of passed function type // arguments is not zero. // Target is specified as offset from the PC of the jump instruction. // // - JumpIfEqStrict target; JumpIfNeStrict target // // Jump to the given target if SP[-1] is the same (JumpIfEqStrict) / // not the same (JumpIfNeStrict) object as SP[0]. // // - JumpIfTrue target; JumpIfFalse target // - JumpIfNull target; JumpIfNotNull target // // Jump to the given target if SP[0] is true/false/null/not null. // // - IndirectStaticCall ArgC, D // // Invoke the function given by the ICData in SP[0] with arguments // SP[-(1+ArgC)], ..., SP[-1] and argument descriptor PP[D], which // indicates whether the first argument is a type argument vector. // // - DirectCall ArgC, D // // Invoke the function PP[D] with arguments // SP[-(ArgC-1)], ..., SP[0] and argument descriptor PP[D+1]. // // - InterfaceCall ArgC, D // // Lookup and invoke method using ICData in PP[D] // with arguments SP[-(1+ArgC)], ..., SP[-1]. // Method has to be declared (explicitly or implicitly) in an interface // implemented by a receiver, and passed arguments are valid for the // interface method declaration. // The ICData indicates whether the first argument is a type argument vector. // // - UncheckedInterfaceCall ArgC, D // // Same as InterfaceCall, but can omit type checks of generic-covariant // parameters. // // - DynamicCall ArgC, D // // Lookup and invoke method using ICData in PP[D] // with arguments SP[-(1+ArgC)], ..., SP[-1]. // The ICData indicates whether the first argument is a type argument vector. // // - NativeCall D // // Invoke native function described by array at pool[D]. // array[0] is wrapper, array[1] is function, array[2] is argc_tag. // // - ReturnTOS // // Return to the caller using a value from the top-of-stack as a result. // // Note: return instruction knows how many arguments to remove from the // stack because it can look at the call instruction at caller's PC and // take argument count from it. // // - AssertAssignable A, D // // Assert that instance SP[-4] is assignable to variable named SP[0] of // type SP[-1] with instantiator type arguments SP[-3] and function type // arguments SP[-2] using SubtypeTestCache PP[D]. // If A is 1, then the instance may be a Smi. // // Instance remains on stack. Other arguments are consumed. // // - AssertBoolean A // // Assert that TOS is a boolean (A = 1) or that TOS is not null (A = 0). // // - AssertSubtype // // Assert that one type is a subtype of another. Throws a TypeError // otherwise. The stack has the following arguments on it: // // SP[-4] instantiator type args // SP[-3] function type args // SP[-2] sub_type // SP[-1] super_type // SP[-0] dst_name // // All 5 arguments are consumed from the stack and no results is pushed. // // - LoadTypeArgumentsField D // // Load instantiator type arguments from an instance SP[0]. // PP[D] = offset (in words) of type arguments field corresponding // to an instance's class. // // - InstantiateType D // // Instantiate type PP[D] with instantiator type arguments SP[-1] and // function type arguments SP[0]. // // - InstantiateTypeArgumentsTOS A, D // // Instantiate type arguments PP[D] with instantiator type arguments SP[-1] // and function type arguments SP[0]. A != 0 indicates that resulting type // arguments are all dynamic if both instantiator and function type // arguments are all dynamic. // // - Throw A // // Throw (Rethrow if A != 0) exception. Exception object and stack object // are taken from TOS. // // - MoveSpecial A, rX // // Copy value from special variable to FP[rX]. Currently only // used to pass exception object (A = 0) and stack trace object (A = 1) to // catch handler. // // - SetFrame A // // Reinitialize SP assuming that current frame has size A. // Used to drop temporaries from the stack in the exception handler. // // - BooleanNegateTOS // // SP[0] = !SP[0] // // - EqualsNull // // SP[0] = (SP[0] == null) ? true : false // // - NegateInt // // Equivalent to invocation of unary int operator-. // Receiver should have static type int. // Check SP[0] for null; SP[0] = -SP[0]. // // - AddInt; SubInt; MulInt; TruncDivInt; ModInt; BitAndInt; BitOrInt; // BitXorInt; ShlInt; ShrInt // // Equivalent to invocation of binary int operator +, -, *, ~/, %, &, |, // ^, << or >>. Receiver and argument should have static type int. // Check SP[-1] and SP[0] for null; push SP[-1] SP[0]. // // - CompareIntEq; CompareIntGt; CompareIntLt; CompareIntGe; CompareIntLe // // Equivalent to invocation of binary int operator ==, >, <, >= or <=. // Receiver and argument should have static type int. // Check SP[-1] and SP[0] for null; push SP[-1] SP[0] ? true : false. // // - NegateDouble // // Equivalent to invocation of unary double operator-. // Receiver should have static type double. // Check SP[0] for null; SP[0] = -SP[0]. // // - AddDouble; SubDouble; MulDouble; DivDouble // // Equivalent to invocation of binary int operator +, -, *, /. // Receiver and argument should have static type double. // Check SP[-1] and SP[0] for null; push SP[-1] SP[0]. // // - CompareDoubleEq; CompareDoubleGt; CompareDoubleLt; CompareDoubleGe; // CompareDoubleLe // // Equivalent to invocation of binary double operator ==, >, <, >= or <=. // Receiver and argument should have static type double. // Check SP[-1] and SP[0] for null; push SP[-1] SP[0] ? true : false. // // - AllocateClosure D // // Allocate closure object for closure function ConstantPool[D]. // // BYTECODE LIST FORMAT // // KernelBytecode list below is specified using the following format: // // V(BytecodeName, OperandForm, BytecodeKind, Op1, Op2, Op3) // // - OperandForm specifies operand encoding and should be one of 0, A, D, X, T, // A_E, A_Y, D_F or A_B_C (see ENCODING section above). // // - BytecodeKind is one of WIDE, RESV (reserved), ORDN (ordinary) // // - Op1, Op2, Op3 specify operand meaning. Possible values: // // ___ ignored / non-existent operand // num 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 // // TODO(vegorov) jump targets should be encoded relative to PC of the next // instruction because PC is incremented immediately after fetch // and before decoding. // #define PUBLIC_KERNEL_BYTECODES_LIST(V) \ V(UnusedOpcode000, 0, RESV, ___, ___, ___) \ V(UnusedOpcode001, 0, RESV, ___, ___, ___) \ V(UnusedOpcode002, 0, RESV, ___, ___, ___) \ V(UnusedOpcode003, 0, RESV, ___, ___, ___) \ V(UnusedOpcode004, 0, RESV, ___, ___, ___) \ V(UnusedOpcode005, 0, RESV, ___, ___, ___) \ V(UnusedOpcode006, 0, RESV, ___, ___, ___) \ V(UnusedOpcode007, 0, RESV, ___, ___, ___) \ V(UnusedOpcode008, 0, RESV, ___, ___, ___) \ V(UnusedOpcode009, 0, RESV, ___, ___, ___) \ V(UnusedOpcode010, 0, RESV, ___, ___, ___) \ V(UnusedOpcode011, 0, RESV, ___, ___, ___) \ V(UnusedOpcode012, 0, RESV, ___, ___, ___) \ V(UnusedOpcode013, 0, RESV, ___, ___, ___) \ V(UnusedOpcode014, 0, RESV, ___, ___, ___) \ V(UnusedOpcode015, 0, RESV, ___, ___, ___) \ V(UnusedOpcode016, 0, RESV, ___, ___, ___) \ V(UnusedOpcode017, 0, RESV, ___, ___, ___) \ V(UnusedOpcode018, 0, RESV, ___, ___, ___) \ V(UnusedOpcode019, 0, RESV, ___, ___, ___) \ V(UnusedOpcode020, 0, RESV, ___, ___, ___) \ V(UnusedOpcode021, 0, RESV, ___, ___, ___) \ V(UnusedOpcode022, 0, RESV, ___, ___, ___) \ V(UnusedOpcode023, 0, RESV, ___, ___, ___) \ V(UnusedOpcode024, 0, RESV, ___, ___, ___) \ V(UnusedOpcode025, 0, RESV, ___, ___, ___) \ V(UnusedOpcode026, 0, RESV, ___, ___, ___) \ V(UnusedOpcode027, 0, RESV, ___, ___, ___) \ V(UnusedOpcode028, 0, RESV, ___, ___, ___) \ V(UnusedOpcode029, 0, RESV, ___, ___, ___) \ V(UnusedOpcode030, 0, RESV, ___, ___, ___) \ V(UnusedOpcode031, 0, RESV, ___, ___, ___) \ V(UnusedOpcode032, 0, RESV, ___, ___, ___) \ V(UnusedOpcode033, 0, RESV, ___, ___, ___) \ V(UnusedOpcode034, 0, RESV, ___, ___, ___) \ V(UnusedOpcode035, 0, RESV, ___, ___, ___) \ V(UnusedOpcode036, 0, RESV, ___, ___, ___) \ V(UnusedOpcode037, 0, RESV, ___, ___, ___) \ V(UnusedOpcode038, 0, RESV, ___, ___, ___) \ V(UnusedOpcode039, 0, RESV, ___, ___, ___) \ V(UnusedOpcode040, 0, RESV, ___, ___, ___) \ V(UnusedOpcode041, 0, RESV, ___, ___, ___) \ V(UnusedOpcode042, 0, RESV, ___, ___, ___) \ V(UnusedOpcode043, 0, RESV, ___, ___, ___) \ V(UnusedOpcode044, 0, RESV, ___, ___, ___) \ V(UnusedOpcode045, 0, RESV, ___, ___, ___) \ V(UnusedOpcode046, 0, RESV, ___, ___, ___) \ V(UnusedOpcode047, 0, RESV, ___, ___, ___) \ V(UnusedOpcode048, 0, RESV, ___, ___, ___) \ V(UnusedOpcode049, 0, RESV, ___, ___, ___) \ V(UnusedOpcode050, 0, RESV, ___, ___, ___) \ V(UnusedOpcode051, 0, RESV, ___, ___, ___) \ V(UnusedOpcode052, 0, RESV, ___, ___, ___) \ V(UnusedOpcode053, 0, RESV, ___, ___, ___) \ V(UnusedOpcode054, 0, RESV, ___, ___, ___) \ V(UnusedOpcode055, 0, RESV, ___, ___, ___) \ V(UnusedOpcode056, 0, RESV, ___, ___, ___) \ V(UnusedOpcode057, 0, RESV, ___, ___, ___) \ V(UnusedOpcode058, 0, RESV, ___, ___, ___) \ V(UnusedOpcode059, 0, RESV, ___, ___, ___) \ V(UnusedOpcode060, 0, RESV, ___, ___, ___) \ V(UnusedOpcode061, 0, RESV, ___, ___, ___) \ V(UnusedOpcode062, 0, RESV, ___, ___, ___) \ V(UnusedOpcode063, 0, RESV, ___, ___, ___) \ V(UnusedOpcode064, 0, RESV, ___, ___, ___) \ V(UnusedOpcode065, 0, RESV, ___, ___, ___) \ V(UnusedOpcode066, 0, RESV, ___, ___, ___) \ V(UnusedOpcode067, 0, RESV, ___, ___, ___) \ V(UnusedOpcode068, 0, RESV, ___, ___, ___) \ V(UnusedOpcode069, 0, RESV, ___, ___, ___) \ V(UnusedOpcode070, 0, RESV, ___, ___, ___) \ V(UnusedOpcode071, 0, RESV, ___, ___, ___) \ V(UnusedOpcode072, 0, RESV, ___, ___, ___) \ V(UnusedOpcode073, 0, RESV, ___, ___, ___) \ V(UnusedOpcode074, 0, RESV, ___, ___, ___) \ V(UnusedOpcode075, 0, RESV, ___, ___, ___) \ V(UnusedOpcode076, 0, RESV, ___, ___, ___) \ V(UnusedOpcode077, 0, RESV, ___, ___, ___) \ V(UnusedOpcode078, 0, RESV, ___, ___, ___) \ V(UnusedOpcode079, 0, RESV, ___, ___, ___) \ V(UnusedOpcode080, 0, RESV, ___, ___, ___) \ V(UnusedOpcode081, 0, RESV, ___, ___, ___) \ V(UnusedOpcode082, 0, RESV, ___, ___, ___) \ V(UnusedOpcode083, 0, RESV, ___, ___, ___) \ V(UnusedOpcode084, 0, RESV, ___, ___, ___) \ V(Trap, 0, ORDN, ___, ___, ___) \ V(Entry, D, ORDN, num, ___, ___) \ V(Entry_Wide, D, WIDE, num, ___, ___) \ V(EntryFixed, A_E, ORDN, num, num, ___) \ V(EntryFixed_Wide, A_E, WIDE, num, num, ___) \ V(EntryOptional, A_B_C, ORDN, num, num, num) \ V(Unused00, 0, RESV, ___, ___, ___) \ V(LoadConstant, A_E, ORDN, reg, lit, ___) \ V(LoadConstant_Wide, A_E, WIDE, reg, lit, ___) \ V(Frame, D, ORDN, num, ___, ___) \ V(Frame_Wide, D, WIDE, num, ___, ___) \ V(CheckFunctionTypeArgs, A_E, ORDN, num, reg, ___) \ V(CheckFunctionTypeArgs_Wide, A_E, WIDE, num, reg, ___) \ V(CheckStack, A, ORDN, num, ___, ___) \ V(Unused01, 0, RESV, ___, ___, ___) \ V(Unused02, 0, RESV, ___, ___, ___) \ V(Unused03, 0, RESV, ___, ___, ___) \ V(Allocate, D, ORDN, lit, ___, ___) \ V(Allocate_Wide, D, WIDE, lit, ___, ___) \ V(AllocateT, 0, ORDN, ___, ___, ___) \ V(CreateArrayTOS, 0, ORDN, ___, ___, ___) \ V(AllocateClosure, D, ORDN, lit, ___, ___) \ V(AllocateClosure_Wide, D, WIDE, lit, ___, ___) \ V(AllocateContext, A_E, ORDN, num, ___, ___) \ V(AllocateContext_Wide, A_E, WIDE, num, ___, ___) \ V(CloneContext, A_E, ORDN, num, ___, ___) \ V(CloneContext_Wide, A_E, WIDE, num, ___, ___) \ V(LoadContextParent, 0, ORDN, ___, ___, ___) \ V(StoreContextParent, 0, ORDN, ___, ___, ___) \ V(LoadContextVar, A_E, ORDN, num, ___, ___) \ V(LoadContextVar_Wide, A_E, WIDE, num, ___, ___) \ V(Unused04, 0, RESV, ___, ___, ___) \ V(Unused05, 0, RESV, ___, ___, ___) \ V(StoreContextVar, A_E, ORDN, num, ___, ___) \ V(StoreContextVar_Wide, A_E, WIDE, num, ___, ___) \ V(PushConstant, D, ORDN, lit, ___, ___) \ V(PushConstant_Wide, D, WIDE, lit, ___, ___) \ V(Unused06, 0, RESV, ___, ___, ___) \ V(Unused07, 0, RESV, ___, ___, ___) \ V(PushTrue, 0, ORDN, ___, ___, ___) \ V(PushFalse, 0, ORDN, ___, ___, ___) \ V(PushInt, X, ORDN, num, ___, ___) \ V(PushInt_Wide, X, WIDE, num, ___, ___) \ V(Unused08, 0, RESV, ___, ___, ___) \ V(Unused09, 0, RESV, ___, ___, ___) \ V(Unused10, 0, RESV, ___, ___, ___) \ V(Unused11, 0, RESV, ___, ___, ___) \ V(PushNull, 0, ORDN, ___, ___, ___) \ V(Drop1, 0, ORDN, ___, ___, ___) \ V(Push, X, ORDN, xeg, ___, ___) \ V(Push_Wide, X, WIDE, xeg, ___, ___) \ V(Unused12, 0, RESV, ___, ___, ___) \ V(Unused13, 0, RESV, ___, ___, ___) \ V(Unused14, 0, RESV, ___, ___, ___) \ V(Unused15, 0, RESV, ___, ___, ___) \ V(Unused16, 0, RESV, ___, ___, ___) \ V(Unused17, 0, RESV, ___, ___, ___) \ V(PopLocal, X, ORDN, xeg, ___, ___) \ V(PopLocal_Wide, X, WIDE, xeg, ___, ___) \ V(Unused18, 0, RESV, ___, ___, ___) \ V(Unused19, 0, RESV, ___, ___, ___) \ V(StoreLocal, X, ORDN, xeg, ___, ___) \ V(StoreLocal_Wide, X, WIDE, xeg, ___, ___) \ V(LoadFieldTOS, D, ORDN, lit, ___, ___) \ V(LoadFieldTOS_Wide, D, WIDE, lit, ___, ___) \ V(StoreFieldTOS, D, ORDN, lit, ___, ___) \ V(StoreFieldTOS_Wide, D, WIDE, lit, ___, ___) \ V(StoreIndexedTOS, 0, ORDN, ___, ___, ___) \ V(Unused20, 0, RESV, ___, ___, ___) \ V(PushStatic, D, ORDN, lit, ___, ___) \ V(PushStatic_Wide, D, WIDE, lit, ___, ___) \ V(StoreStaticTOS, D, ORDN, lit, ___, ___) \ V(StoreStaticTOS_Wide, D, WIDE, lit, ___, ___) \ V(Jump, T, ORDN, tgt, ___, ___) \ V(Jump_Wide, T, WIDE, tgt, ___, ___) \ V(JumpIfNoAsserts, T, ORDN, tgt, ___, ___) \ V(JumpIfNoAsserts_Wide, T, WIDE, tgt, ___, ___) \ V(JumpIfNotZeroTypeArgs, T, ORDN, tgt, ___, ___) \ V(JumpIfNotZeroTypeArgs_Wide, T, WIDE, tgt, ___, ___) \ V(JumpIfEqStrict, T, ORDN, tgt, ___, ___) \ V(JumpIfEqStrict_Wide, T, WIDE, tgt, ___, ___) \ V(JumpIfNeStrict, T, ORDN, tgt, ___, ___) \ V(JumpIfNeStrict_Wide, T, WIDE, tgt, ___, ___) \ V(JumpIfTrue, T, ORDN, tgt, ___, ___) \ V(JumpIfTrue_Wide, T, WIDE, tgt, ___, ___) \ V(JumpIfFalse, T, ORDN, tgt, ___, ___) \ V(JumpIfFalse_Wide, T, WIDE, tgt, ___, ___) \ V(JumpIfNull, T, ORDN, tgt, ___, ___) \ V(JumpIfNull_Wide, T, WIDE, tgt, ___, ___) \ V(JumpIfNotNull, T, ORDN, tgt, ___, ___) \ V(JumpIfNotNull_Wide, T, WIDE, tgt, ___, ___) \ V(DirectCall, D_F, ORDN, num, num, ___) \ V(DirectCall_Wide, D_F, WIDE, num, num, ___) \ V(Unused21, 0, RESV, ___, ___, ___) \ V(Unused22, 0, RESV, ___, ___, ___) \ V(InterfaceCall, D_F, ORDN, num, num, ___) \ V(InterfaceCall_Wide, D_F, WIDE, num, num, ___) \ V(Unused23, 0, RESV, ___, ___, ___) \ V(Unused24, 0, RESV, ___, ___, ___) \ V(Unused25, 0, RESV, ___, ___, ___) \ V(Unused26, 0, RESV, ___, ___, ___) \ V(Unused27, 0, RESV, ___, ___, ___) \ V(Unused28, 0, RESV, ___, ___, ___) \ V(UncheckedInterfaceCall, D_F, ORDN, num, num, ___) \ V(UncheckedInterfaceCall_Wide, D_F, WIDE, num, num, ___) \ V(DynamicCall, D_F, ORDN, num, num, ___) \ V(DynamicCall_Wide, D_F, WIDE, num, num, ___) \ V(NativeCall, D, ORDN, lit, ___, ___) \ V(NativeCall_Wide, D, WIDE, lit, ___, ___) \ V(ReturnTOS, 0, ORDN, ___, ___, ___) \ V(Unused29, 0, RESV, ___, ___, ___) \ V(AssertAssignable, A_E, ORDN, num, lit, ___) \ V(AssertAssignable_Wide, A_E, WIDE, num, lit, ___) \ V(Unused30, 0, RESV, ___, ___, ___) \ V(Unused31, 0, RESV, ___, ___, ___) \ V(AssertBoolean, A, ORDN, num, ___, ___) \ V(AssertSubtype, 0, ORDN, ___, ___, ___) \ V(LoadTypeArgumentsField, D, ORDN, lit, ___, ___) \ V(LoadTypeArgumentsField_Wide, D, WIDE, lit, ___, ___) \ V(InstantiateType, D, ORDN, lit, ___, ___) \ V(InstantiateType_Wide, D, WIDE, lit, ___, ___) \ V(InstantiateTypeArgumentsTOS, A_E, ORDN, num, lit, ___) \ V(InstantiateTypeArgumentsTOS_Wide, A_E, WIDE, num, lit, ___) \ V(Unused32, 0, RESV, ___, ___, ___) \ V(Unused33, 0, RESV, ___, ___, ___) \ V(Unused34, 0, RESV, ___, ___, ___) \ V(Unused35, 0, RESV, ___, ___, ___) \ V(Throw, A, ORDN, num, ___, ___) \ V(SetFrame, A, ORDN, num, ___, num) \ V(MoveSpecial, A_Y, ORDN, num, xeg, ___) \ V(MoveSpecial_Wide, A_Y, WIDE, num, xeg, ___) \ V(BooleanNegateTOS, 0, ORDN, ___, ___, ___) \ V(EqualsNull, 0, ORDN, ___, ___, ___) \ V(Unused36, 0, RESV, ___, ___, ___) \ V(Unused37, 0, RESV, ___, ___, ___) \ V(NegateInt, 0, ORDN, ___, ___, ___) \ V(AddInt, 0, ORDN, ___, ___, ___) \ V(SubInt, 0, ORDN, ___, ___, ___) \ V(MulInt, 0, ORDN, ___, ___, ___) \ V(TruncDivInt, 0, ORDN, ___, ___, ___) \ V(ModInt, 0, ORDN, ___, ___, ___) \ V(BitAndInt, 0, ORDN, ___, ___, ___) \ V(BitOrInt, 0, ORDN, ___, ___, ___) \ V(BitXorInt, 0, ORDN, ___, ___, ___) \ V(ShlInt, 0, ORDN, ___, ___, ___) \ V(ShrInt, 0, ORDN, ___, ___, ___) \ V(CompareIntEq, 0, ORDN, ___, ___, ___) \ V(CompareIntGt, 0, ORDN, ___, ___, ___) \ V(CompareIntLt, 0, ORDN, ___, ___, ___) \ V(CompareIntGe, 0, ORDN, ___, ___, ___) \ V(CompareIntLe, 0, ORDN, ___, ___, ___) \ V(NegateDouble, 0, ORDN, ___, ___, ___) \ V(AddDouble, 0, ORDN, ___, ___, ___) \ V(SubDouble, 0, ORDN, ___, ___, ___) \ V(MulDouble, 0, ORDN, ___, ___, ___) \ V(DivDouble, 0, ORDN, ___, ___, ___) \ V(CompareDoubleEq, 0, ORDN, ___, ___, ___) \ V(CompareDoubleGt, 0, ORDN, ___, ___, ___) \ V(CompareDoubleLt, 0, ORDN, ___, ___, ___) \ V(CompareDoubleGe, 0, ORDN, ___, ___, ___) \ V(CompareDoubleLe, 0, ORDN, ___, ___, ___) \ // These bytecodes are only generated within the VM. Reassigning their // opcodes is not a breaking change. #define INTERNAL_KERNEL_BYTECODES_LIST(V) \ V(VMInternal_ImplicitGetter, 0, ORDN, ___, ___, ___) \ V(VMInternal_ImplicitSetter, 0, ORDN, ___, ___, ___) \ V(VMInternal_ImplicitStaticGetter, 0, ORDN, ___, ___, ___) \ V(VMInternal_MethodExtractor, 0, ORDN, ___, ___, ___) \ V(VMInternal_InvokeClosure, 0, ORDN, ___, ___, ___) \ V(VMInternal_InvokeField, 0, ORDN, ___, ___, ___) \ V(VMInternal_ForwardDynamicInvocation, 0, ORDN, ___, ___, ___) \ V(VMInternal_NoSuchMethodDispatcher, 0, ORDN, ___, ___, ___) \ V(VMInternal_ImplicitStaticClosure, 0, ORDN, ___, ___, ___) \ V(VMInternal_ImplicitInstanceClosure, 0, ORDN, ___, ___, ___) \ #define KERNEL_BYTECODES_LIST(V) \ PUBLIC_KERNEL_BYTECODES_LIST(V) \ INTERNAL_KERNEL_BYTECODES_LIST(V) // clang-format on typedef uint8_t KBCInstr; class KernelBytecode { public: // Magic value of bytecode files. static const intptr_t kMagicValue = 0x44424332; // 'DBC2' // Minimum bytecode format version supported by VM. static const intptr_t kMinSupportedBytecodeFormatVersion = 7; // Maximum bytecode format version supported by VM. // The range of supported versions should include version produced by bytecode // generator (currentBytecodeFormatVersion in pkg/vm/lib/bytecode/dbc.dart). static const intptr_t kMaxSupportedBytecodeFormatVersion = 11; enum Opcode { #define DECLARE_BYTECODE(name, encoding, kind, op1, op2, op3) k##name, KERNEL_BYTECODES_LIST(DECLARE_BYTECODE) #undef DECLARE_BYTECODE }; static const char* NameOf(Opcode op) { const char* names[] = { #define NAME(name, encoding, kind, op1, op2, op3) #name, KERNEL_BYTECODES_LIST(NAME) #undef NAME }; return names[op]; } static const intptr_t kInstructionSize[]; enum SpecialIndex { kExceptionSpecialIndex, kStackTraceSpecialIndex, kSpecialIndexCount }; private: static const intptr_t kWideModifier = 1; // Should be used only on instructions with wide variants. DART_FORCE_INLINE static bool IsWide(const KBCInstr* instr) { return ((DecodeOpcode(instr) & kWideModifier) != 0); } public: DART_FORCE_INLINE static uint8_t DecodeA(const KBCInstr* bc) { return bc[1]; } DART_FORCE_INLINE static uint8_t DecodeB(const KBCInstr* bc) { return bc[2]; } DART_FORCE_INLINE static uint8_t DecodeC(const KBCInstr* bc) { return bc[3]; } DART_FORCE_INLINE static uint32_t DecodeD(const KBCInstr* bc) { if (IsWide(bc)) { return static_cast(bc[1]) | (static_cast(bc[2]) << 8) | (static_cast(bc[3]) << 16) | (static_cast(bc[4]) << 24); } else { return bc[1]; } } DART_FORCE_INLINE static int32_t DecodeX(const KBCInstr* bc) { if (IsWide(bc)) { return static_cast(static_cast(bc[1]) | (static_cast(bc[2]) << 8) | (static_cast(bc[3]) << 16) | (static_cast(bc[4]) << 24)); } else { return static_cast(bc[1]); } } DART_FORCE_INLINE static int32_t DecodeT(const KBCInstr* bc) { if (IsWide(bc)) { return static_cast((static_cast(bc[1]) << 8) | (static_cast(bc[2]) << 16) | (static_cast(bc[3]) << 24)) >> 8; } else { return static_cast(bc[1]); } } DART_FORCE_INLINE static uint32_t DecodeE(const KBCInstr* bc) { if (IsWide(bc)) { return static_cast(bc[2]) | (static_cast(bc[3]) << 8) | (static_cast(bc[4]) << 16) | (static_cast(bc[5]) << 24); } else { return bc[2]; } } DART_FORCE_INLINE static int32_t DecodeY(const KBCInstr* bc) { if (IsWide(bc)) { return static_cast(static_cast(bc[2]) | (static_cast(bc[3]) << 8) | (static_cast(bc[4]) << 16) | (static_cast(bc[5]) << 24)); } else { return static_cast(bc[2]); } } DART_FORCE_INLINE static uint8_t DecodeF(const KBCInstr* bc) { if (IsWide(bc)) { return bc[5]; } else { return bc[2]; } } DART_FORCE_INLINE static Opcode DecodeOpcode(const KBCInstr* bc) { return static_cast(bc[0]); } DART_FORCE_INLINE static const KBCInstr* Next(const KBCInstr* bc) { return bc + kInstructionSize[DecodeOpcode(bc)]; } DART_FORCE_INLINE static bool IsJumpOpcode(const KBCInstr* instr) { switch (DecodeOpcode(instr)) { case KernelBytecode::kJump: case KernelBytecode::kJump_Wide: case KernelBytecode::kJumpIfNoAsserts: case KernelBytecode::kJumpIfNoAsserts_Wide: case KernelBytecode::kJumpIfNotZeroTypeArgs: case KernelBytecode::kJumpIfNotZeroTypeArgs_Wide: case KernelBytecode::kJumpIfEqStrict: case KernelBytecode::kJumpIfEqStrict_Wide: case KernelBytecode::kJumpIfNeStrict: case KernelBytecode::kJumpIfNeStrict_Wide: case KernelBytecode::kJumpIfTrue: case KernelBytecode::kJumpIfTrue_Wide: case KernelBytecode::kJumpIfFalse: case KernelBytecode::kJumpIfFalse_Wide: case KernelBytecode::kJumpIfNull: case KernelBytecode::kJumpIfNull_Wide: case KernelBytecode::kJumpIfNotNull: case KernelBytecode::kJumpIfNotNull_Wide: return true; default: return false; } } DART_FORCE_INLINE static bool IsLoadConstantOpcode(const KBCInstr* instr) { switch (DecodeOpcode(instr)) { case KernelBytecode::kLoadConstant: case KernelBytecode::kLoadConstant_Wide: return true; default: return false; } } DART_FORCE_INLINE static bool IsCheckStackOpcode(const KBCInstr* instr) { return DecodeOpcode(instr) == KernelBytecode::kCheckStack; } DART_FORCE_INLINE static bool IsEntryOptionalOpcode(const KBCInstr* instr) { return DecodeOpcode(instr) == KernelBytecode::kEntryOptional; } DART_FORCE_INLINE static bool IsFrameOpcode(const KBCInstr* instr) { switch (DecodeOpcode(instr)) { case KernelBytecode::kFrame: case KernelBytecode::kFrame_Wide: return true; default: return false; } } DART_FORCE_INLINE static bool IsSetFrameOpcode(const KBCInstr* instr) { return DecodeOpcode(instr) == KernelBytecode::kSetFrame; } DART_FORCE_INLINE static bool IsNativeCallOpcode(const KBCInstr* instr) { switch (DecodeOpcode(instr)) { case KernelBytecode::kNativeCall: case KernelBytecode::kNativeCall_Wide: return true; default: return false; } } // The interpreter and this function must agree on the opcodes. DART_FORCE_INLINE static bool IsDebugBreakCheckedOpcode( const KBCInstr* instr) { switch (DecodeOpcode(instr)) { case KernelBytecode::kPopLocal: case KernelBytecode::kPopLocal_Wide: case KernelBytecode::kStoreLocal: case KernelBytecode::kStoreLocal_Wide: case KernelBytecode::kStoreStaticTOS: case KernelBytecode::kStoreStaticTOS_Wide: case KernelBytecode::kCheckStack: case KernelBytecode::kDirectCall: case KernelBytecode::kDirectCall_Wide: case KernelBytecode::kInterfaceCall: case KernelBytecode::kInterfaceCall_Wide: case KernelBytecode::kUncheckedInterfaceCall: case KernelBytecode::kUncheckedInterfaceCall_Wide: case KernelBytecode::kDynamicCall: case KernelBytecode::kDynamicCall_Wide: case KernelBytecode::kReturnTOS: case KernelBytecode::kThrow: case KernelBytecode::kJump: case KernelBytecode::kJump_Wide: return true; default: return false; } } static const uint8_t kNativeCallToGrowableListArgc = 2; // Returns a fake return address which points after the 2-argument // bytecode call, followed by ReturnTOS instructions. static const KBCInstr* GetNativeCallToGrowableListReturnTrampoline(); DART_FORCE_INLINE static uint8_t DecodeArgc(const KBCInstr* ret_addr) { // All call instructions have DF encoding, with argc being the last byte // regardless of whether the wide variant is used or not. return ret_addr[-1]; } // Converts bytecode PC into an offset. // For return addresses used in PcDescriptors, PC is also augmented by 1. // TODO(regis): Eliminate this correction. static intptr_t BytecodePcToOffset(uint32_t pc, bool is_return_address) { return pc + (is_return_address ? 1 : 0); } static uint32_t OffsetToBytecodePc(intptr_t offset, bool is_return_address) { return offset - (is_return_address ? 1 : 0); } static void GetVMInternalBytecodeInstructions(Opcode opcode, const KBCInstr** instructions, intptr_t* instructions_size); private: DISALLOW_ALLOCATION(); DISALLOW_IMPLICIT_CONSTRUCTORS(KernelBytecode); }; } // namespace dart #endif // RUNTIME_VM_CONSTANTS_KBC_H_