mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
![Samir Jindel](/assets/img/avatar_default.png)
The original revision is in patchset 1. Three bugs are fixed: 1. Fix SIMARM_X64 build: no need to generate trampolines for AOT or simulated JIT. 2. Hot-reload: Fix hot-reload: don't invalidate Code for force-optimized functions. 3. Windows: Provide shadow space to runtime routines. Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-mac-debug-simarm_x64-try,vm-kernel-precomp-mac-release-simarm_x64-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-kernel-win-release-ia32-try,vm-kernel-win-release-x64-try Change-Id: I326009cfacb51a84e9de4ddf9ff2d6d415460f91 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/113829 Commit-Queue: Samir Jindel <sjindel@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
821 lines
25 KiB
C++
821 lines
25 KiB
C++
// Copyright (c) 2013, 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_ARM_H_
|
|
#define RUNTIME_VM_CONSTANTS_ARM_H_
|
|
|
|
#ifndef RUNTIME_VM_CONSTANTS_H_
|
|
#error Do not include constants_arm.h directly; use constants.h instead.
|
|
#endif
|
|
|
|
#include "platform/assert.h"
|
|
#include "platform/globals.h"
|
|
|
|
namespace arch_arm {
|
|
|
|
// We support both VFPv3-D16 and VFPv3-D32 profiles, but currently only one at
|
|
// a time.
|
|
#if defined(__ARM_ARCH_7A__)
|
|
#define VFPv3_D32
|
|
#elif defined(TARGET_ARCH_ARM) && !defined(HOST_ARCH_ARM)
|
|
// If we're running in the simulator, use all 32.
|
|
#define VFPv3_D32
|
|
#else
|
|
#define VFPv3_D16
|
|
#endif
|
|
#if defined(VFPv3_D16) == defined(VFPv3_D32)
|
|
#error "Exactly one of VFPv3_D16 or VFPv3_D32 can be defined at a time."
|
|
#endif
|
|
|
|
// The Linux/Android ABI and the iOS ABI differ in their choice of frame
|
|
// pointer, their treatment of R9, and the interprocedural stack alignment.
|
|
|
|
// EABI (Linux, Android)
|
|
// See "Procedure Call Standard for the ARM Architecture".
|
|
// R0-R1: Argument / result / volatile
|
|
// R2-R3: Argument / volatile
|
|
// R4-R10: Preserved
|
|
// R11: Frame pointer
|
|
// R12: Volatile
|
|
// R13: Stack pointer
|
|
// R14: Link register
|
|
// R15: Program counter
|
|
// Stack alignment: 4 bytes always, 8 bytes at public interfaces
|
|
|
|
// Linux (Debian armhf) and Android also differ in whether floating point
|
|
// arguments are passed in floating point registers. Linux uses hardfp and
|
|
// Android uses softfp. See TargetCPUFeatures::hardfp_supported().
|
|
|
|
// iOS ABI
|
|
// See "iOS ABI Function Call Guide"
|
|
// R0-R1: Argument / result / volatile
|
|
// R2-R3: Argument / volatile
|
|
// R4-R6: Preserved
|
|
// R7: Frame pointer
|
|
// R8-R11: Preserved
|
|
// R12: Volatile
|
|
// R13: Stack pointer
|
|
// R14: Link register
|
|
// R15: Program counter
|
|
// Stack alignment: 4 bytes always, 4 bytes at public interfaces
|
|
|
|
// iOS passes floating point arguments in integer registers (softfp)
|
|
|
|
enum Register {
|
|
R0 = 0,
|
|
R1 = 1,
|
|
R2 = 2,
|
|
R3 = 3,
|
|
R4 = 4,
|
|
R5 = 5, // PP
|
|
R6 = 6,
|
|
R7 = 7, // iOS FP
|
|
R8 = 8,
|
|
R9 = 9,
|
|
R10 = 10, // THR
|
|
R11 = 11, // Linux FP
|
|
R12 = 12, // IP aka TMP
|
|
R13 = 13, // SP
|
|
R14 = 14, // LR
|
|
R15 = 15, // PC
|
|
kNumberOfCpuRegisters = 16,
|
|
kNoRegister = -1, // Signals an illegal register.
|
|
|
|
// Aliases.
|
|
#if defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS)
|
|
FP = R7,
|
|
NOTFP = R11,
|
|
#else
|
|
FP = R11,
|
|
NOTFP = R7,
|
|
#endif
|
|
IP = R12,
|
|
SP = R13,
|
|
LR = R14,
|
|
PC = R15,
|
|
};
|
|
|
|
// Values for single-precision floating point registers.
|
|
enum SRegister {
|
|
kNoSRegister = -1,
|
|
S0 = 0,
|
|
S1 = 1,
|
|
S2 = 2,
|
|
S3 = 3,
|
|
S4 = 4,
|
|
S5 = 5,
|
|
S6 = 6,
|
|
S7 = 7,
|
|
S8 = 8,
|
|
S9 = 9,
|
|
S10 = 10,
|
|
S11 = 11,
|
|
S12 = 12,
|
|
S13 = 13,
|
|
S14 = 14,
|
|
S15 = 15,
|
|
S16 = 16,
|
|
S17 = 17,
|
|
S18 = 18,
|
|
S19 = 19,
|
|
S20 = 20,
|
|
S21 = 21,
|
|
S22 = 22,
|
|
S23 = 23,
|
|
S24 = 24,
|
|
S25 = 25,
|
|
S26 = 26,
|
|
S27 = 27,
|
|
S28 = 28,
|
|
S29 = 29,
|
|
S30 = 30,
|
|
S31 = 31,
|
|
kNumberOfSRegisters = 32,
|
|
};
|
|
|
|
// Values for double-precision floating point registers.
|
|
enum DRegister {
|
|
kNoDRegister = -1,
|
|
D0 = 0,
|
|
D1 = 1,
|
|
D2 = 2,
|
|
D3 = 3,
|
|
D4 = 4,
|
|
D5 = 5,
|
|
D6 = 6,
|
|
D7 = 7,
|
|
D8 = 8,
|
|
D9 = 9,
|
|
D10 = 10,
|
|
D11 = 11,
|
|
D12 = 12,
|
|
D13 = 13,
|
|
D14 = 14,
|
|
D15 = 15,
|
|
#if defined(VFPv3_D16)
|
|
kNumberOfDRegisters = 16,
|
|
// Leaving these defined, but marking them as kNoDRegister to avoid polluting
|
|
// other parts of the code with #ifdef's. Instead, query kNumberOfDRegisters
|
|
// to see which registers are valid.
|
|
D16 = kNoDRegister,
|
|
D17 = kNoDRegister,
|
|
D18 = kNoDRegister,
|
|
D19 = kNoDRegister,
|
|
D20 = kNoDRegister,
|
|
D21 = kNoDRegister,
|
|
D22 = kNoDRegister,
|
|
D23 = kNoDRegister,
|
|
D24 = kNoDRegister,
|
|
D25 = kNoDRegister,
|
|
D26 = kNoDRegister,
|
|
D27 = kNoDRegister,
|
|
D28 = kNoDRegister,
|
|
D29 = kNoDRegister,
|
|
D30 = kNoDRegister,
|
|
D31 = kNoDRegister,
|
|
#else
|
|
D16 = 16,
|
|
D17 = 17,
|
|
D18 = 18,
|
|
D19 = 19,
|
|
D20 = 20,
|
|
D21 = 21,
|
|
D22 = 22,
|
|
D23 = 23,
|
|
D24 = 24,
|
|
D25 = 25,
|
|
D26 = 26,
|
|
D27 = 27,
|
|
D28 = 28,
|
|
D29 = 29,
|
|
D30 = 30,
|
|
D31 = 31,
|
|
kNumberOfDRegisters = 32,
|
|
#endif
|
|
kNumberOfOverlappingDRegisters = 16,
|
|
};
|
|
|
|
enum QRegister {
|
|
kNoQRegister = -1,
|
|
Q0 = 0,
|
|
Q1 = 1,
|
|
Q2 = 2,
|
|
Q3 = 3,
|
|
Q4 = 4,
|
|
Q5 = 5,
|
|
Q6 = 6,
|
|
Q7 = 7,
|
|
#if defined(VFPv3_D16)
|
|
kNumberOfQRegisters = 8,
|
|
Q8 = kNoQRegister,
|
|
Q9 = kNoQRegister,
|
|
Q10 = kNoQRegister,
|
|
Q11 = kNoQRegister,
|
|
Q12 = kNoQRegister,
|
|
Q13 = kNoQRegister,
|
|
Q14 = kNoQRegister,
|
|
Q15 = kNoQRegister,
|
|
#else
|
|
Q8 = 8,
|
|
Q9 = 9,
|
|
Q10 = 10,
|
|
Q11 = 11,
|
|
Q12 = 12,
|
|
Q13 = 13,
|
|
Q14 = 14,
|
|
Q15 = 15,
|
|
kNumberOfQRegisters = 16,
|
|
#endif
|
|
};
|
|
|
|
static inline DRegister EvenDRegisterOf(QRegister q) {
|
|
return static_cast<DRegister>(q * 2);
|
|
}
|
|
|
|
static inline DRegister OddDRegisterOf(QRegister q) {
|
|
return static_cast<DRegister>((q * 2) + 1);
|
|
}
|
|
|
|
static inline SRegister EvenSRegisterOf(DRegister d) {
|
|
#if defined(VFPv3_D32)
|
|
// When we have 32 D registers, the S registers only overlap the first 16.
|
|
// That is, there are only 32 S registers.
|
|
ASSERT(d < D16);
|
|
#endif
|
|
return static_cast<SRegister>(d * 2);
|
|
}
|
|
|
|
static inline SRegister OddSRegisterOf(DRegister d) {
|
|
#if defined(VFPv3_D32)
|
|
ASSERT(d < D16);
|
|
#endif
|
|
return static_cast<SRegister>((d * 2) + 1);
|
|
}
|
|
|
|
// Register aliases for floating point scratch registers.
|
|
const QRegister QTMP = Q7; // Overlaps with DTMP, STMP.
|
|
const DRegister DTMP = EvenDRegisterOf(QTMP); // Overlaps with STMP.
|
|
const SRegister STMP DART_USED = EvenSRegisterOf(DTMP);
|
|
|
|
// Architecture independent aliases.
|
|
typedef QRegister FpuRegister;
|
|
|
|
const FpuRegister FpuTMP = QTMP;
|
|
const int kNumberOfFpuRegisters = kNumberOfQRegisters;
|
|
const FpuRegister kNoFpuRegister = kNoQRegister;
|
|
|
|
extern const char* cpu_reg_names[kNumberOfCpuRegisters];
|
|
extern const char* fpu_reg_names[kNumberOfFpuRegisters];
|
|
|
|
// Register aliases.
|
|
const Register TMP = IP; // Used as scratch register by assembler.
|
|
const Register TMP2 = kNoRegister; // There is no second assembler temporary.
|
|
const Register PP = R5; // Caches object pool pointer in generated code.
|
|
const Register SPREG = SP; // Stack pointer register.
|
|
const Register FPREG = FP; // Frame pointer register.
|
|
const Register LRREG = LR; // Link register.
|
|
const Register ARGS_DESC_REG = R4;
|
|
const Register CODE_REG = R6;
|
|
const Register THR = R10; // Caches current thread in generated code.
|
|
const Register CALLEE_SAVED_TEMP = R8;
|
|
|
|
// R15 encodes APSR in the vmrs instruction.
|
|
const Register APSR = R15;
|
|
|
|
// ABI for catch-clause entry point.
|
|
const Register kExceptionObjectReg = R0;
|
|
const Register kStackTraceObjectReg = R1;
|
|
|
|
// ABI for write barrier stub.
|
|
const Register kWriteBarrierObjectReg = R1;
|
|
const Register kWriteBarrierValueReg = R0;
|
|
const Register kWriteBarrierSlotReg = R9;
|
|
|
|
// List of registers used in load/store multiple.
|
|
typedef uint16_t RegList;
|
|
const RegList kAllCpuRegistersList = 0xFFFF;
|
|
|
|
// C++ ABI call registers.
|
|
const RegList kAbiArgumentCpuRegs =
|
|
(1 << R0) | (1 << R1) | (1 << R2) | (1 << R3);
|
|
#if defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS)
|
|
const RegList kAbiPreservedCpuRegs =
|
|
(1 << R4) | (1 << R5) | (1 << R6) | (1 << R8) | (1 << R10) | (1 << R11);
|
|
const int kAbiPreservedCpuRegCount = 6;
|
|
#else
|
|
const RegList kAbiPreservedCpuRegs = (1 << R4) | (1 << R5) | (1 << R6) |
|
|
(1 << R7) | (1 << R8) | (1 << R9) |
|
|
(1 << R10);
|
|
const int kAbiPreservedCpuRegCount = 7;
|
|
#endif
|
|
const QRegister kAbiFirstPreservedFpuReg = Q4;
|
|
const QRegister kAbiLastPreservedFpuReg = Q7;
|
|
const int kAbiPreservedFpuRegCount = 4;
|
|
|
|
const RegList kReservedCpuRegisters = (1 << SPREG) | (1 << FPREG) | (1 << TMP) |
|
|
(1 << PP) | (1 << THR) | (1 << LR) |
|
|
(1 << PC);
|
|
constexpr intptr_t kNumberOfReservedCpuRegisters = 7;
|
|
// CPU registers available to Dart allocator.
|
|
constexpr RegList kDartAvailableCpuRegs =
|
|
kAllCpuRegistersList & ~kReservedCpuRegisters;
|
|
constexpr int kNumberOfDartAvailableCpuRegs =
|
|
kNumberOfCpuRegisters - kNumberOfReservedCpuRegisters;
|
|
const intptr_t kStoreBufferWrapperSize = 24;
|
|
// Registers available to Dart that are not preserved by runtime calls.
|
|
const RegList kDartVolatileCpuRegs =
|
|
kDartAvailableCpuRegs & ~kAbiPreservedCpuRegs;
|
|
#if defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS)
|
|
const int kDartVolatileCpuRegCount = 6;
|
|
#else
|
|
const int kDartVolatileCpuRegCount = 5;
|
|
#endif
|
|
const QRegister kDartFirstVolatileFpuReg = Q0;
|
|
const QRegister kDartLastVolatileFpuReg = Q3;
|
|
const int kDartVolatileFpuRegCount = 4;
|
|
|
|
#define R(REG) (1 << REG)
|
|
|
|
class CallingConventions {
|
|
public:
|
|
static const intptr_t kArgumentRegisters = kAbiArgumentCpuRegs;
|
|
static const Register ArgumentRegisters[];
|
|
static const intptr_t kNumArgRegs = 4;
|
|
|
|
static const FpuRegister FpuArgumentRegisters[];
|
|
static const intptr_t kFpuArgumentRegisters = 0;
|
|
static const intptr_t kNumFpuArgRegs = 0;
|
|
|
|
static constexpr bool kArgumentIntRegXorFpuReg = false;
|
|
|
|
static constexpr intptr_t kCalleeSaveCpuRegisters = kAbiPreservedCpuRegs;
|
|
|
|
// Whether floating-point values should be passed as integers ("softfp" vs
|
|
// "hardfp"). Android and iOS always use the "softfp" calling convention, even
|
|
// when hardfp support is present.
|
|
#if defined(TARGET_OS_MACOS_IOS) || defined(TARGET_OS_ANDROID)
|
|
static constexpr bool kAbiSoftFP = true;
|
|
#else
|
|
static constexpr bool kAbiSoftFP = false;
|
|
#endif
|
|
|
|
// Whether 64-bit arguments must be aligned to an even register or 8-byte
|
|
// stack address. True for ARM 32-bit, see "Procedure Call Standard for the
|
|
// ARM Architecture".
|
|
static constexpr bool kAlignArguments = true;
|
|
|
|
static constexpr Register kReturnReg = R0;
|
|
static constexpr Register kSecondReturnReg = R1;
|
|
static constexpr FpuRegister kReturnFpuReg = kNoFpuRegister;
|
|
|
|
// We choose these to avoid overlap between themselves and reserved registers.
|
|
static constexpr Register kFirstNonArgumentRegister = R8;
|
|
static constexpr Register kSecondNonArgumentRegister = R9;
|
|
static constexpr Register kFirstCalleeSavedCpuReg = NOTFP;
|
|
static constexpr Register kSecondCalleeSavedCpuReg = R4;
|
|
static constexpr Register kStackPointerRegister = SPREG;
|
|
};
|
|
|
|
#undef R
|
|
|
|
// Values for the condition field as defined in section A3.2.
|
|
enum Condition {
|
|
kNoCondition = -1,
|
|
EQ = 0, // equal
|
|
NE = 1, // not equal
|
|
CS = 2, // carry set/unsigned higher or same
|
|
CC = 3, // carry clear/unsigned lower
|
|
MI = 4, // minus/negative
|
|
PL = 5, // plus/positive or zero
|
|
VS = 6, // overflow
|
|
VC = 7, // no overflow
|
|
HI = 8, // unsigned higher
|
|
LS = 9, // unsigned lower or same
|
|
GE = 10, // signed greater than or equal
|
|
LT = 11, // signed less than
|
|
GT = 12, // signed greater than
|
|
LE = 13, // signed less than or equal
|
|
AL = 14, // always (unconditional)
|
|
kSpecialCondition = 15, // special condition (refer to section A3.2.1)
|
|
kNumberOfConditions = 16,
|
|
|
|
// Platform-independent variants declared for all platforms
|
|
EQUAL = EQ,
|
|
ZERO = EQUAL,
|
|
NOT_EQUAL = NE,
|
|
NOT_ZERO = NOT_EQUAL,
|
|
LESS = LT,
|
|
LESS_EQUAL = LE,
|
|
GREATER_EQUAL = GE,
|
|
GREATER = GT,
|
|
UNSIGNED_LESS = CC,
|
|
UNSIGNED_LESS_EQUAL = LS,
|
|
UNSIGNED_GREATER = HI,
|
|
UNSIGNED_GREATER_EQUAL = CS,
|
|
|
|
kInvalidCondition = 16
|
|
};
|
|
|
|
// Opcodes for Data-processing instructions (instructions with a type 0 and 1)
|
|
// as defined in section A3.4
|
|
enum Opcode {
|
|
kNoOperand = -1,
|
|
AND = 0, // Logical AND
|
|
EOR = 1, // Logical Exclusive OR
|
|
SUB = 2, // Subtract
|
|
RSB = 3, // Reverse Subtract
|
|
ADD = 4, // Add
|
|
ADC = 5, // Add with Carry
|
|
SBC = 6, // Subtract with Carry
|
|
RSC = 7, // Reverse Subtract with Carry
|
|
TST = 8, // Test
|
|
TEQ = 9, // Test Equivalence
|
|
CMP = 10, // Compare
|
|
CMN = 11, // Compare Negated
|
|
ORR = 12, // Logical (inclusive) OR
|
|
MOV = 13, // Move
|
|
BIC = 14, // Bit Clear
|
|
MVN = 15, // Move Not
|
|
kMaxOperand = 16
|
|
};
|
|
|
|
// Shifter types for Data-processing operands as defined in section A5.1.2.
|
|
enum Shift {
|
|
kNoShift = -1,
|
|
LSL = 0, // Logical shift left
|
|
LSR = 1, // Logical shift right
|
|
ASR = 2, // Arithmetic shift right
|
|
ROR = 3, // Rotate right
|
|
kMaxShift = 4
|
|
};
|
|
|
|
// Constants used for the decoding or encoding of the individual fields of
|
|
// instructions. Based on the "Figure 3-1 ARM instruction set summary".
|
|
enum InstructionFields {
|
|
kConditionShift = 28,
|
|
kConditionBits = 4,
|
|
kTypeShift = 25,
|
|
kTypeBits = 3,
|
|
kLinkShift = 24,
|
|
kLinkBits = 1,
|
|
kUShift = 23,
|
|
kUBits = 1,
|
|
kOpcodeShift = 21,
|
|
kOpcodeBits = 4,
|
|
kSShift = 20,
|
|
kSBits = 1,
|
|
kRnShift = 16,
|
|
kRnBits = 4,
|
|
kRdShift = 12,
|
|
kRdBits = 4,
|
|
kRsShift = 8,
|
|
kRsBits = 4,
|
|
kRmShift = 0,
|
|
kRmBits = 4,
|
|
|
|
// Immediate instruction fields encoding.
|
|
kRotateShift = 8,
|
|
kRotateBits = 4,
|
|
kImmed8Shift = 0,
|
|
kImmed8Bits = 8,
|
|
|
|
// Shift instruction register fields encodings.
|
|
kShiftImmShift = 7,
|
|
kShiftRegisterShift = 8,
|
|
kShiftImmBits = 5,
|
|
kShiftShift = 5,
|
|
kShiftBits = 2,
|
|
|
|
// Load/store instruction offset field encoding.
|
|
kOffset12Shift = 0,
|
|
kOffset12Bits = 12,
|
|
kOffset12Mask = 0x00000fff,
|
|
|
|
// Mul instruction register field encodings.
|
|
kMulRdShift = 16,
|
|
kMulRdBits = 4,
|
|
kMulRnShift = 12,
|
|
kMulRnBits = 4,
|
|
|
|
// Div instruction register field encodings.
|
|
kDivRdShift = 16,
|
|
kDivRdBits = 4,
|
|
kDivRmShift = 8,
|
|
kDivRmBits = 4,
|
|
kDivRnShift = 0,
|
|
kDivRnBits = 4,
|
|
|
|
// ldrex/strex register field encodings.
|
|
kLdExRnShift = 16,
|
|
kLdExRtShift = 12,
|
|
kStrExRnShift = 16,
|
|
kStrExRdShift = 12,
|
|
kStrExRtShift = 0,
|
|
|
|
// MRC instruction offset field encoding.
|
|
kCRmShift = 0,
|
|
kCRmBits = 4,
|
|
kOpc2Shift = 5,
|
|
kOpc2Bits = 3,
|
|
kCoprocShift = 8,
|
|
kCoprocBits = 4,
|
|
kCRnShift = 16,
|
|
kCRnBits = 4,
|
|
kOpc1Shift = 21,
|
|
kOpc1Bits = 3,
|
|
|
|
kBranchOffsetMask = 0x00ffffff
|
|
};
|
|
|
|
// The class Instr enables access to individual fields defined in the ARM
|
|
// architecture instruction set encoding as described in figure A3-1.
|
|
//
|
|
// Example: Test whether the instruction at ptr sets the condition code bits.
|
|
//
|
|
// bool InstructionSetsConditionCodes(byte* ptr) {
|
|
// Instr* instr = Instr::At(ptr);
|
|
// int type = instr->TypeField();
|
|
// return ((type == 0) || (type == 1)) && instr->HasS();
|
|
// }
|
|
//
|
|
class Instr {
|
|
public:
|
|
enum { kInstrSize = 4, kInstrSizeLog2 = 2, kPCReadOffset = 8 };
|
|
|
|
static const int32_t kNopInstruction = // nop
|
|
((AL << kConditionShift) | (0x32 << 20) | (0xf << 12));
|
|
|
|
static const int32_t kBreakPointCode = 0xdeb0; // For breakpoint.
|
|
static const int32_t kSimulatorBreakCode = 0xdeb2; // For breakpoint in sim.
|
|
static const int32_t kSimulatorRedirectCode = 0xca11; // For redirection.
|
|
|
|
// Breakpoint instruction filling assembler code buffers in debug mode.
|
|
static const int32_t kBreakPointInstruction = // bkpt(0xdeb0)
|
|
((AL << kConditionShift) | (0x12 << 20) | (0xdeb << 8) | (0x7 << 4));
|
|
|
|
// Breakpoint instruction used by the simulator.
|
|
// Should be distinct from kBreakPointInstruction and from a typical user
|
|
// breakpoint inserted in generated code for debugging, e.g. bkpt(0).
|
|
static const int32_t kSimulatorBreakpointInstruction =
|
|
// svc #kBreakpointSvcCode
|
|
((AL << kConditionShift) | (0xf << 24) | kSimulatorBreakCode);
|
|
|
|
// Runtime call redirection instruction used by the simulator.
|
|
static const int32_t kSimulatorRedirectInstruction =
|
|
((AL << kConditionShift) | (0xf << 24) | kSimulatorRedirectCode);
|
|
|
|
// Get the raw instruction bits.
|
|
inline int32_t InstructionBits() const {
|
|
return *reinterpret_cast<const int32_t*>(this);
|
|
}
|
|
|
|
// Set the raw instruction bits to value.
|
|
inline void SetInstructionBits(int32_t value) {
|
|
*reinterpret_cast<int32_t*>(this) = value;
|
|
}
|
|
|
|
// Read one particular bit out of the instruction bits.
|
|
inline int Bit(int nr) const { return (InstructionBits() >> nr) & 1; }
|
|
|
|
// Read a bit field out of the instruction bits.
|
|
inline int Bits(int shift, int count) const {
|
|
return (InstructionBits() >> shift) & ((1 << count) - 1);
|
|
}
|
|
|
|
// Accessors for the different named fields used in the ARM encoding.
|
|
// The naming of these accessor corresponds to figure A3-1.
|
|
// Generally applicable fields
|
|
inline Condition ConditionField() const {
|
|
return static_cast<Condition>(Bits(kConditionShift, kConditionBits));
|
|
}
|
|
inline int TypeField() const { return Bits(kTypeShift, kTypeBits); }
|
|
|
|
inline Register RnField() const {
|
|
return static_cast<Register>(Bits(kRnShift, kRnBits));
|
|
}
|
|
inline Register RdField() const {
|
|
return static_cast<Register>(Bits(kRdShift, kRdBits));
|
|
}
|
|
|
|
// Fields used in Data processing instructions
|
|
inline Opcode OpcodeField() const {
|
|
return static_cast<Opcode>(Bits(kOpcodeShift, kOpcodeBits));
|
|
}
|
|
inline int SField() const { return Bits(kSShift, kSBits); }
|
|
// with register
|
|
inline Register RmField() const {
|
|
return static_cast<Register>(Bits(kRmShift, kRmBits));
|
|
}
|
|
inline Shift ShiftField() const {
|
|
return static_cast<Shift>(Bits(kShiftShift, kShiftBits));
|
|
}
|
|
inline int RegShiftField() const { return Bit(4); }
|
|
inline Register RsField() const {
|
|
return static_cast<Register>(Bits(kRsShift, kRsBits));
|
|
}
|
|
inline int ShiftAmountField() const {
|
|
return Bits(kShiftImmShift, kShiftImmBits);
|
|
}
|
|
// with immediate
|
|
inline int RotateField() const { return Bits(kRotateShift, kRotateBits); }
|
|
inline int Immed8Field() const { return Bits(kImmed8Shift, kImmed8Bits); }
|
|
|
|
// Fields used in Load/Store instructions
|
|
inline int PUField() const { return Bits(23, 2); }
|
|
inline int BField() const { return Bit(22); }
|
|
inline int WField() const { return Bit(21); }
|
|
inline int LField() const { return Bit(20); }
|
|
// with register uses same fields as Data processing instructions above
|
|
// with immediate
|
|
inline int Offset12Field() const {
|
|
return Bits(kOffset12Shift, kOffset12Bits);
|
|
}
|
|
// multiple
|
|
inline int RlistField() const { return Bits(0, 16); }
|
|
// extra loads and stores
|
|
inline int SignField() const { return Bit(6); }
|
|
inline int HField() const { return Bit(5); }
|
|
inline int ImmedHField() const { return Bits(8, 4); }
|
|
inline int ImmedLField() const { return Bits(0, 4); }
|
|
|
|
// Fields used in Branch instructions
|
|
inline int LinkField() const { return Bits(kLinkShift, kLinkBits); }
|
|
inline int SImmed24Field() const { return ((InstructionBits() << 8) >> 8); }
|
|
|
|
// Fields used in Supervisor Call instructions
|
|
inline uint32_t SvcField() const { return Bits(0, 24); }
|
|
|
|
// Field used in Breakpoint instruction
|
|
inline uint16_t BkptField() const {
|
|
return ((Bits(8, 12) << 4) | Bits(0, 4));
|
|
}
|
|
|
|
// Field used in 16-bit immediate move instructions
|
|
inline uint16_t MovwField() const {
|
|
return ((Bits(16, 4) << 12) | Bits(0, 12));
|
|
}
|
|
|
|
// Field used in VFP float immediate move instruction
|
|
inline float ImmFloatField() const {
|
|
uint32_t imm32 = (Bit(19) << 31) | (((1 << 5) - Bit(18)) << 25) |
|
|
(Bits(16, 2) << 23) | (Bits(0, 4) << 19);
|
|
return ::dart::bit_cast<float, uint32_t>(imm32);
|
|
}
|
|
|
|
// Field used in VFP double immediate move instruction
|
|
inline double ImmDoubleField() const {
|
|
uint64_t imm64 = (Bit(19) * (1LL << 63)) | (((1LL << 8) - Bit(18)) << 54) |
|
|
(Bits(16, 2) * (1LL << 52)) | (Bits(0, 4) * (1LL << 48));
|
|
return ::dart::bit_cast<double, uint64_t>(imm64);
|
|
}
|
|
|
|
inline Register DivRdField() const {
|
|
return static_cast<Register>(Bits(kDivRdShift, kDivRdBits));
|
|
}
|
|
inline Register DivRmField() const {
|
|
return static_cast<Register>(Bits(kDivRmShift, kDivRmBits));
|
|
}
|
|
inline Register DivRnField() const {
|
|
return static_cast<Register>(Bits(kDivRnShift, kDivRnBits));
|
|
}
|
|
|
|
// Test for data processing instructions of type 0 or 1.
|
|
// See "ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition",
|
|
// section A5.1 "ARM instruction set encoding".
|
|
inline bool IsDataProcessing() const {
|
|
ASSERT(ConditionField() != kSpecialCondition);
|
|
ASSERT(Bits(26, 2) == 0); // Type 0 or 1.
|
|
return ((Bits(20, 5) & 0x19) != 0x10) &&
|
|
((Bit(25) == 1) || // Data processing immediate.
|
|
(Bit(4) == 0) || // Data processing register.
|
|
(Bit(7) == 0)); // Data processing register-shifted register.
|
|
}
|
|
|
|
// Tests for special encodings of type 0 instructions (extra loads and stores,
|
|
// as well as multiplications, synchronization primitives, and miscellaneous).
|
|
// Can only be called for a type 0 or 1 instruction.
|
|
inline bool IsMiscellaneous() const {
|
|
ASSERT(Bits(26, 2) == 0); // Type 0 or 1.
|
|
return ((Bit(25) == 0) && ((Bits(20, 5) & 0x19) == 0x10) && (Bit(7) == 0));
|
|
}
|
|
inline bool IsMultiplyOrSyncPrimitive() const {
|
|
ASSERT(Bits(26, 2) == 0); // Type 0 or 1.
|
|
return ((Bit(25) == 0) && (Bits(4, 4) == 9));
|
|
}
|
|
|
|
// Test for Supervisor Call instruction.
|
|
inline bool IsSvc() const {
|
|
return ((InstructionBits() & 0x0f000000) == 0x0f000000);
|
|
}
|
|
|
|
// Test for Breakpoint instruction.
|
|
inline bool IsBkpt() const {
|
|
return ((InstructionBits() & 0x0ff000f0) == 0x01200070);
|
|
}
|
|
|
|
// VFP register fields.
|
|
inline SRegister SnField() const {
|
|
return static_cast<SRegister>((Bits(kRnShift, kRnBits) << 1) + Bit(7));
|
|
}
|
|
inline SRegister SdField() const {
|
|
return static_cast<SRegister>((Bits(kRdShift, kRdBits) << 1) + Bit(22));
|
|
}
|
|
inline SRegister SmField() const {
|
|
return static_cast<SRegister>((Bits(kRmShift, kRmBits) << 1) + Bit(5));
|
|
}
|
|
inline DRegister DnField() const {
|
|
return static_cast<DRegister>(Bits(kRnShift, kRnBits) + (Bit(7) << 4));
|
|
}
|
|
inline DRegister DdField() const {
|
|
return static_cast<DRegister>(Bits(kRdShift, kRdBits) + (Bit(22) << 4));
|
|
}
|
|
inline DRegister DmField() const {
|
|
return static_cast<DRegister>(Bits(kRmShift, kRmBits) + (Bit(5) << 4));
|
|
}
|
|
inline QRegister QnField() const {
|
|
const intptr_t bits = Bits(kRnShift, kRnBits) + (Bit(7) << 4);
|
|
return static_cast<QRegister>(bits >> 1);
|
|
}
|
|
inline QRegister QdField() const {
|
|
const intptr_t bits = Bits(kRdShift, kRdBits) + (Bit(22) << 4);
|
|
return static_cast<QRegister>(bits >> 1);
|
|
}
|
|
inline QRegister QmField() const {
|
|
const intptr_t bits = Bits(kRmShift, kRmBits) + (Bit(5) << 4);
|
|
return static_cast<QRegister>(bits >> 1);
|
|
}
|
|
|
|
inline bool IsDivision() const {
|
|
ASSERT(ConditionField() != kSpecialCondition);
|
|
ASSERT(TypeField() == 3);
|
|
return ((Bit(4) == 1) && (Bits(5, 3) == 0) && (Bit(20) == 1) &&
|
|
(Bits(22, 3) == 4));
|
|
}
|
|
|
|
// Test for VFP data processing or single transfer instructions of type 7.
|
|
inline bool IsVFPDataProcessingOrSingleTransfer() const {
|
|
ASSERT(ConditionField() != kSpecialCondition);
|
|
ASSERT(TypeField() == 7);
|
|
return ((Bit(24) == 0) && (Bits(9, 3) == 5));
|
|
// Bit(4) == 0: Data Processing
|
|
// Bit(4) == 1: 8, 16, or 32-bit Transfer between ARM Core and VFP
|
|
}
|
|
|
|
// Test for VFP 64-bit transfer instructions of type 6.
|
|
inline bool IsVFPDoubleTransfer() const {
|
|
ASSERT(ConditionField() != kSpecialCondition);
|
|
ASSERT(TypeField() == 6);
|
|
return ((Bits(21, 4) == 2) && (Bits(9, 3) == 5) &&
|
|
((Bits(4, 4) & 0xd) == 1));
|
|
}
|
|
|
|
// Test for VFP load and store instructions of type 6.
|
|
inline bool IsVFPLoadStore() const {
|
|
ASSERT(ConditionField() != kSpecialCondition);
|
|
ASSERT(TypeField() == 6);
|
|
return ((Bits(20, 5) & 0x12) == 0x10) && (Bits(9, 3) == 5);
|
|
}
|
|
|
|
// Test for VFP multiple load and store instructions of type 6.
|
|
inline bool IsVFPMultipleLoadStore() const {
|
|
ASSERT(ConditionField() != kSpecialCondition);
|
|
ASSERT(TypeField() == 6);
|
|
int32_t puw = (PUField() << 1) | Bit(21); // don't care about D bit
|
|
return (Bits(9, 3) == 5) && ((puw == 2) || (puw == 3) || (puw == 5));
|
|
}
|
|
|
|
inline bool IsSIMDDataProcessing() const {
|
|
ASSERT(ConditionField() == kSpecialCondition);
|
|
return (Bits(25, 3) == 1);
|
|
}
|
|
|
|
inline bool IsSIMDLoadStore() const {
|
|
ASSERT(ConditionField() == kSpecialCondition);
|
|
return (Bits(24, 4) == 4) && (Bit(20) == 0);
|
|
}
|
|
|
|
// Special accessors that test for existence of a value.
|
|
inline bool HasS() const { return SField() == 1; }
|
|
inline bool HasB() const { return BField() == 1; }
|
|
inline bool HasW() const { return WField() == 1; }
|
|
inline bool HasL() const { return LField() == 1; }
|
|
inline bool HasSign() const { return SignField() == 1; }
|
|
inline bool HasH() const { return HField() == 1; }
|
|
inline bool HasLink() const { return LinkField() == 1; }
|
|
|
|
// Instructions are read out of a code stream. The only way to get a
|
|
// reference to an instruction is to convert a pointer. There is no way
|
|
// to allocate or create instances of class Instr.
|
|
// Use the At(pc) function to create references to Instr.
|
|
static Instr* At(::dart::uword pc) { return reinterpret_cast<Instr*>(pc); }
|
|
|
|
private:
|
|
DISALLOW_ALLOCATION();
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(Instr);
|
|
};
|
|
|
|
} // namespace arch_arm
|
|
|
|
#endif // RUNTIME_VM_CONSTANTS_ARM_H_
|