52cfd29cbb
The RecordCoverage instruction has an A/E encoding. The A argument is the type of coverage being recorded, whereas the E argument is the logical index into the coverage array for updating whether that source position has been hit. Also adds new metadata to the bytecode component for the coverage arrays associated with bytecode containing RecordCoverage instructions and a new runtime entry for lazily allocate the coverage array for an interpreted function when needed. The type of coverage is encoded in the RecordCoverage instruction, despite being redundant with the information in the coverage array, so that checking whether that type of coverage is currently enabled at runtime doesn't require either accessing the coverage array (which may be lazily allocated), forcing allocation of the coverage array just to discover that type of coverage is currently disabled, or reading the serialized bytecode component to avoid that forced allocation. ------ Other changes: Source reporting now treats unexecuted interpreted functions when not forcing compilation as if they were uncompiled native functions, so that the source report from running the same code gives the same result whether using the interpreter or the native compiler. Bytecode closures are no longer skipped in source reports. Previously any closure without a context scope was skipped, but bytecode closures don't have those. TEST=vm/cc/SourceReport_Coverage Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try,vm-aot-dyn-linux-debug-x64-try,vm-aot-dyn-linux-product-x64-try Change-Id: I7557e5dd4c98331c7ca2f5c867dd5f6d03e9d756 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501520 Reviewed-by: Alexander Markov <alexmarkov@google.com>
530 lines
26 KiB
C++
530 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, ___, ___) \
|
|
V(RecordCoverage, A_E, ORDN, num, num, ___) \
|
|
V(RecordCoverage_Wide, A_E, WIDE, num, num, ___) \
|
|
|
|
// 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 = 3;
|
|
|
|
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_
|