51d1c8923a
This is a reland of commit 88496ba1c3
Fixes on top of the original change:
* Closure layout is changed to avoid gap between fixed fields and
variable-length elements on compressed pointers architecture.
This gap was causing crashes in the GC when scanning closure
objects.
* pkg/vm_snapshot_analysis/test/instruction_sizes_test is fixed
on arm64 by decreasing threshold for detecting size changes.
Original change's description:
> [vm,dart2bytecode,modular_aot] Variable-length closure objects
>
> Extend closure objects with variable number of elements to capture.
> This is needed to support capturing multiple independent contexts
> after capturing is computed in the front-end.
>
> The following fixed Closure fields are moved into variable-length
> elements:
> - delayed type arguments;
> - instantiator type arguments;
> - function type arguments;
> - context.
>
> Number of elements and presence/indices of various type arguments
> are encoded into the new length_and_flags field in the Closure.
>
> Most closure objects don't need any of the type arguments so this
> change will reduce average Closure object size.
TEST=ci
Issue: https://github.com/dart-lang/sdk/issues/61572
Issue: https://github.com/dart-lang/sdk/issues/61635
Change-Id: I8685e632e2d0832766ecdc470f3cf9a6b880de48
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/494243
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Tess Strickland <sstrickl@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
528 lines
26 KiB
C++
528 lines
26 KiB
C++
// 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.
|
|
|
|
#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
|
|
// Bytecode instructions are 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.
|
|
//
|
|
// - 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(Trap, 0, ORDN, ___, ___, ___) \
|
|
V(Unused00, 0, RESV, ___, ___, ___) \
|
|
V(Entry, D, ORDN, num, ___, ___) \
|
|
V(Entry_Wide, D, WIDE, num, ___, ___) \
|
|
V(EntryOptional, A_B_C, ORDN, num, num, num) \
|
|
V(EntrySuspendable, A_B_C, ORDN, num, num, num) \
|
|
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(Nop, 0, ORDN, ___, ___, ___) \
|
|
V(JumpIfUnchecked, T, ORDN, tgt, ___, ___) \
|
|
V(JumpIfUnchecked_Wide, T, WIDE, tgt, ___, ___) \
|
|
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(LoadClosureElement, D, ORDN, num, ___, ___) \
|
|
V(LoadClosureElement_Wide, D, WIDE, num, ___, ___) \
|
|
V(StoreClosureElement, D, ORDN, num, ___, ___) \
|
|
V(StoreClosureElement_Wide, D, WIDE, num, ___, ___) \
|
|
V(AllocateContext, A_E, ORDN, num, num, ___) \
|
|
V(AllocateContext_Wide, A_E, WIDE, num, num, ___) \
|
|
V(CloneContext, A_E, ORDN, num, num, ___) \
|
|
V(CloneContext_Wide, A_E, WIDE, num, num, ___) \
|
|
V(LoadContextParent, 0, ORDN, ___, ___, ___) \
|
|
V(StoreContextParent, 0, ORDN, ___, ___, ___) \
|
|
V(LoadContextVar, A_E, ORDN, num, num, ___) \
|
|
V(LoadContextVar_Wide, A_E, WIDE, num, num, ___) \
|
|
V(Unused04, 0, RESV, ___, ___, ___) \
|
|
V(Unused05, 0, RESV, ___, ___, ___) \
|
|
V(StoreContextVar, A_E, ORDN, num, num, ___) \
|
|
V(StoreContextVar_Wide, A_E, WIDE, num, 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(LoadStatic, D, ORDN, lit, ___, ___) \
|
|
V(LoadStatic_Wide, D, WIDE, lit, ___, ___) \
|
|
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(JumpIfInitialized, T, ORDN, tgt, ___, ___) \
|
|
V(JumpIfInitialized_Wide, T, WIDE, tgt, ___, ___) \
|
|
V(PushUninitializedSentinel, 0, ORDN, ___, ___, ___) \
|
|
V(Unused21, 0, RESV, ___, ___, ___) \
|
|
V(InitLateField, D, ORDN, lit, ___, ___) \
|
|
V(InitLateField_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(Suspend, T, ORDN, tgt, ___, ___) \
|
|
V(Suspend_Wide, T, WIDE, tgt, ___, ___) \
|
|
V(DirectCall, D_F, ORDN, num, num, ___) \
|
|
V(DirectCall_Wide, D_F, WIDE, num, num, ___) \
|
|
V(UncheckedDirectCall, D_F, ORDN, num, num, ___) \
|
|
V(UncheckedDirectCall_Wide, D_F, WIDE, num, num, ___) \
|
|
V(InterfaceCall, D_F, ORDN, num, num, ___) \
|
|
V(InterfaceCall_Wide, D_F, WIDE, num, num, ___) \
|
|
V(ExternalCall, D, ORDN, lit, ___, ___) \
|
|
V(ExternalCall_Wide, D, WIDE, lit, ___, ___) \
|
|
V(InstantiatedInterfaceCall, D_F, ORDN, num, num, ___) \
|
|
V(InstantiatedInterfaceCall_Wide, D_F, WIDE, num, num, ___) \
|
|
V(UncheckedClosureCall, D_F, ORDN, num, num, ___) \
|
|
V(UncheckedClosureCall_Wide, D_F, WIDE, num, num, ___) \
|
|
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(ReturnTOS, 0, ORDN, ___, ___, ___) \
|
|
V(Unused25, 0, RESV, ___, ___, ___) \
|
|
V(AssertAssignable, A_E, ORDN, num, lit, ___) \
|
|
V(AssertAssignable_Wide, A_E, WIDE, num, lit, ___) \
|
|
V(AssertSubtype, 0, ORDN, ___, ___, ___) \
|
|
V(Unused30, 0, RESV, ___, ___, ___) \
|
|
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(NullCheck, D, ORDN, lit, ___, ___) \
|
|
V(NullCheck_Wide, D, WIDE, lit, ___, ___) \
|
|
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, ___, ___, ___) \
|
|
V(AllocateRecord, D, ORDN, lit, ___, ___) \
|
|
V(AllocateRecord_Wide, D, WIDE, lit, ___, ___) \
|
|
V(LoadRecordField, D, ORDN, num, ___, ___) \
|
|
V(LoadRecordField_Wide, D, WIDE, num, ___, ___) \
|
|
V(FfiCall, D, ORDN, lit, ___, ___) \
|
|
V(FfiCall_Wide, D, WIDE, lit, ___, ___) \
|
|
|
|
// These bytecodes are only generated within the VM. Reassigning their
|
|
// opcodes is not a breaking change.
|
|
#define INTERNAL_KERNEL_BYTECODES_WITH_CUSTOM_CODE(V) \
|
|
/* ImplicitConstructorClosure and ImplicitInstanceClosure instructions */ \
|
|
/* use D_F encoding as they may call target constructor or method and */ \
|
|
/* should be compatible with other ***Call instructions */ \
|
|
/* in order to support DecodeArgc when returning from a call. */ \
|
|
V(VMInternal_ImplicitConstructorClosure, D_F, ORDN, num, num, ___) \
|
|
V(VMInternal_ImplicitConstructorClosure_Wide, D_F, ORDN, num, num, ___) \
|
|
V(VMInternal_ImplicitInstanceClosure, D_F, ORDN, num, num, ___) \
|
|
V(VMInternal_ImplicitInstanceClosure_Wide, D_F, ORDN, num, num, ___) \
|
|
|
|
#define INTERNAL_KERNEL_BYTECODES_WITH_DEFAULT_CODE(V) \
|
|
V(VMInternal_ImplicitGetter, 0, ORDN, ___, ___, ___) \
|
|
V(VMInternal_ImplicitSetter, 0, ORDN, ___, ___, ___) \
|
|
V(VMInternal_ImplicitStaticGetter, 0, ORDN, ___, ___, ___) \
|
|
V(VMInternal_ImplicitSharedStaticGetter, 0, ORDN, ___, ___, ___) \
|
|
V(VMInternal_ImplicitStaticSetter, 0, ORDN, ___, ___, ___) \
|
|
V(VMInternal_ImplicitSharedStaticSetter, 0, ORDN, ___, ___, ___) \
|
|
V(VMInternal_MethodExtractorWithITA, 0, ORDN, ___, ___, ___) \
|
|
V(VMInternal_MethodExtractorWithoutITA, 0, ORDN, ___, ___, ___) \
|
|
V(VMInternal_InvokeClosure, 0, ORDN, ___, ___, ___) \
|
|
V(VMInternal_InvokeField, 0, ORDN, ___, ___, ___) \
|
|
V(VMInternal_ForwardDynamicInvocation, 0, ORDN, ___, ___, ___) \
|
|
V(VMInternal_ImplicitStaticClosure, 0, ORDN, ___, ___, ___) \
|
|
V(VMInternal_NoSuchMethodDispatcher, 0, ORDN, ___, ___, ___) \
|
|
|
|
// One breakpoint opcode for each instruction size.
|
|
#define INTERNAL_KERNEL_BREAKPOINT_BYTECODES(V) \
|
|
V(VMInternal_Breakpoint_0, 0, ORDN, ___, ___, ___) \
|
|
V(VMInternal_Breakpoint_A_B_C, A_B_C, ORDN, num, num, num) \
|
|
V(VMInternal_Breakpoint_D, D, ORDN, num, ___, ___) \
|
|
V(VMInternal_Breakpoint_D_Wide, D, WIDE, num, ___, ___) \
|
|
V(VMInternal_Breakpoint_A_E, A_E, ORDN, num, num, ___) \
|
|
V(VMInternal_Breakpoint_A_E_Wide, A_E, WIDE, num, num, ___) \
|
|
|
|
#define INTERNAL_KERNEL_BYTECODES_LIST_WITH_NO_BREAKPOINTS(V) \
|
|
INTERNAL_KERNEL_BYTECODES_WITH_CUSTOM_CODE(V) \
|
|
INTERNAL_KERNEL_BYTECODES_WITH_DEFAULT_CODE(V)
|
|
|
|
#define INTERNAL_KERNEL_BYTECODES_LIST(V) \
|
|
INTERNAL_KERNEL_BYTECODES_LIST_WITH_NO_BREAKPOINTS(V) \
|
|
INTERNAL_KERNEL_BREAKPOINT_BYTECODES(V)
|
|
|
|
#define KERNEL_BYTECODES_LIST_WITH_NO_BREAKPOINTS(V) \
|
|
PUBLIC_KERNEL_BYTECODES_LIST(V) \
|
|
INTERNAL_KERNEL_BYTECODES_LIST_WITH_NO_BREAKPOINTS(V)
|
|
|
|
#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 = 0x44424333; // 'DBC3'
|
|
// Bytecode format version supported by the VM
|
|
// (should match pkg/dart2bytecode/lib/dbc.dart).
|
|
static const intptr_t kBytecodeFormatVersion = 2;
|
|
|
|
enum Opcode {
|
|
#define DECLARE_BYTECODE(name, encoding, kind, op1, op2, op3) k##name,
|
|
KERNEL_BYTECODES_LIST(DECLARE_BYTECODE)
|
|
#undef DECLARE_BYTECODE
|
|
kNumOpcodes,
|
|
};
|
|
|
|
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 kMaxInstructionSize = 6;
|
|
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 IsWide(DecodeOpcode(instr));
|
|
}
|
|
|
|
// Should be used only on instructions with wide variants.
|
|
DART_FORCE_INLINE static constexpr bool IsWide(Opcode opcode) {
|
|
return ((opcode & 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<uint32_t>(bc[1]) |
|
|
(static_cast<uint32_t>(bc[2]) << 8) |
|
|
(static_cast<uint32_t>(bc[3]) << 16) |
|
|
(static_cast<uint32_t>(bc[4]) << 24);
|
|
} else {
|
|
return bc[1];
|
|
}
|
|
}
|
|
|
|
DART_FORCE_INLINE static int32_t DecodeX(const KBCInstr* bc) {
|
|
if (IsWide(bc)) {
|
|
return static_cast<int32_t>(static_cast<uint32_t>(bc[1]) |
|
|
(static_cast<uint32_t>(bc[2]) << 8) |
|
|
(static_cast<uint32_t>(bc[3]) << 16) |
|
|
(static_cast<uint32_t>(bc[4]) << 24));
|
|
} else {
|
|
return static_cast<int8_t>(bc[1]);
|
|
}
|
|
}
|
|
|
|
DART_FORCE_INLINE static int32_t DecodeT(const KBCInstr* bc) {
|
|
if (IsWide(bc)) {
|
|
return static_cast<int32_t>((static_cast<uint32_t>(bc[1]) << 8) |
|
|
(static_cast<uint32_t>(bc[2]) << 16) |
|
|
(static_cast<uint32_t>(bc[3]) << 24)) >>
|
|
8;
|
|
} else {
|
|
return static_cast<int8_t>(bc[1]);
|
|
}
|
|
}
|
|
|
|
DART_FORCE_INLINE static uint32_t DecodeE(const KBCInstr* bc) {
|
|
if (IsWide(bc)) {
|
|
return static_cast<uint32_t>(bc[2]) |
|
|
(static_cast<uint32_t>(bc[3]) << 8) |
|
|
(static_cast<uint32_t>(bc[4]) << 16) |
|
|
(static_cast<uint32_t>(bc[5]) << 24);
|
|
} else {
|
|
return bc[2];
|
|
}
|
|
}
|
|
|
|
DART_FORCE_INLINE static int32_t DecodeY(const KBCInstr* bc) {
|
|
if (IsWide(bc)) {
|
|
return static_cast<int32_t>(static_cast<uint32_t>(bc[2]) |
|
|
(static_cast<uint32_t>(bc[3]) << 8) |
|
|
(static_cast<uint32_t>(bc[4]) << 16) |
|
|
(static_cast<uint32_t>(bc[5]) << 24));
|
|
} else {
|
|
return static_cast<int8_t>(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<Opcode>(bc[0]);
|
|
}
|
|
|
|
DART_FORCE_INLINE static const KBCInstr* Next(const KBCInstr* bc) {
|
|
return bc + kInstructionSize[DecodeOpcode(bc)];
|
|
}
|
|
|
|
DART_FORCE_INLINE static uword Next(uword pc) {
|
|
return pc + kInstructionSize[DecodeOpcode(
|
|
reinterpret_cast<const KBCInstr*>(pc))];
|
|
}
|
|
|
|
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 IsCheckFunctionTypeArgs(const KBCInstr* instr) {
|
|
switch (DecodeOpcode(instr)) {
|
|
case KernelBytecode::kCheckFunctionTypeArgs:
|
|
case KernelBytecode::kCheckFunctionTypeArgs_Wide:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
DART_FORCE_INLINE static bool IsEntryOpcode(const KBCInstr* instr) {
|
|
switch (DecodeOpcode(instr)) {
|
|
case KernelBytecode::kEntry:
|
|
case KernelBytecode::kEntry_Wide:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
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 IsDirectCallOpcode(const KBCInstr* instr) {
|
|
switch (DecodeOpcode(instr)) {
|
|
case KernelBytecode::kDirectCall:
|
|
case KernelBytecode::kDirectCall_Wide:
|
|
case KernelBytecode::kUncheckedDirectCall:
|
|
case KernelBytecode::kUncheckedDirectCall_Wide:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
DART_FORCE_INLINE static bool IsReturnOpcode(const KBCInstr* instr) {
|
|
return DecodeOpcode(instr) == KernelBytecode::kReturnTOS;
|
|
}
|
|
|
|
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);
|
|
|
|
static Opcode BreakpointOpcode(Opcode opcode) {
|
|
Opcode replacement;
|
|
switch (kInstructionSize[opcode]) {
|
|
case 1:
|
|
replacement = kVMInternal_Breakpoint_0;
|
|
break;
|
|
case 2:
|
|
replacement = kVMInternal_Breakpoint_D;
|
|
break;
|
|
case 3:
|
|
replacement = kVMInternal_Breakpoint_A_E;
|
|
break;
|
|
case 4:
|
|
replacement = kVMInternal_Breakpoint_A_B_C;
|
|
break;
|
|
case 5:
|
|
replacement = kVMInternal_Breakpoint_D_Wide;
|
|
break;
|
|
case 6:
|
|
replacement = kVMInternal_Breakpoint_A_E_Wide;
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
return kTrap;
|
|
}
|
|
ASSERT_EQUAL(kInstructionSize[replacement], kInstructionSize[opcode]);
|
|
return replacement;
|
|
}
|
|
|
|
static Opcode BreakpointOpcode(const KBCInstr* instr) {
|
|
return BreakpointOpcode(DecodeOpcode(instr));
|
|
}
|
|
|
|
private:
|
|
friend class Interpreter; // for IsWide(Opcode) in static_asserts.
|
|
|
|
DISALLOW_ALLOCATION();
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(KernelBytecode);
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_CONSTANTS_KBC_H_
|