dart-sdk/runtime/vm/constants_arm64.h
Alexander Markov 10e9606861 [vm] More efficient 'is' tests for record types
'is' tests against record types are split into series of 'is' tests of
record fields. This is more efficient as record instances cannot be
added to subtype test caches (because type of a record instance
depends on types of its fields).

This change also adds canonicalization and constant evaluation of
LoadFieldInstr for record fields.

Performance on a trivial micro-benchmark (on x64):
JIT (RunTime): 4519104.5 -> 20031.5
AOT (RunTime): 4352583.0 -> 26281.6

TEST=ci
Issue: https://github.com/dart-lang/sdk/issues/49719
Change-Id: I2ed464cd3b31f365b17805f4e7debe1d6d1051fa
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/268080
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
2022-11-07 22:27:31 +00:00

1597 lines
51 KiB
C++

// Copyright (c) 2014, 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_ARM64_H_
#define RUNTIME_VM_CONSTANTS_ARM64_H_
#ifndef RUNTIME_VM_CONSTANTS_H_
#error Do not include constants_arm64.h directly; use constants.h instead.
#endif
#include "platform/assert.h"
#include "platform/globals.h"
#include "platform/utils.h"
#include "vm/constants_base.h"
namespace dart {
// LR register should not be used directly in handwritten assembly patterns,
// because it might contain return address. Instead use macross CLOBBERS_LR,
// SPILLS_RETURN_ADDRESS_FROM_LR_TO_REGISTER,
// RESTORES_RETURN_ADDRESS_FROM_REGISTER_TO_LR, SPILLS_LR_TO_FRAME,
// RESTORES_LR_FROM_FRAME, READS_RETURN_ADDRESS_FROM_LR,
// WRITES_RETURN_ADDRESS_TO_LR to get access to LR constant in a checked way.
//
// To prevent accidental use of LR constant we rename it to
// LR_DO_NOT_USE_DIRECTLY (while keeping the code in this file and other files
// which are permitted to access LR constant the same by defining LR as
// LR_DO_NOT_USE_DIRECTLY). You can also use LINK_REGISTER if you need
// to compare LR register code.
#define LR LR_DO_NOT_USE_DIRECTLY
enum Register {
R0 = 0,
R1 = 1,
R2 = 2,
R3 = 3,
R4 = 4,
R5 = 5,
R6 = 6,
R7 = 7,
R8 = 8,
R9 = 9,
R10 = 10,
R11 = 11,
R12 = 12,
R13 = 13,
R14 = 14,
R15 = 15, // SP in Dart code.
R16 = 16, // IP0 aka TMP
R17 = 17, // IP1 aka TMP2
R18 = 18, // reserved on iOS, shadow call stack on Fuchsia, TEB on Windows.
R19 = 19,
R20 = 20,
R21 = 21, // DISPATCH_TABLE_REG
R22 = 22, // NULL_REG
R23 = 23,
R24 = 24,
R25 = 25,
R26 = 26, // THR
R27 = 27, // PP
R28 = 28, // HEAP_BITS
R29 = 29, // FP
R30 = 30, // LR
R31 = 31, // ZR, CSP
kNumberOfCpuRegisters = 32,
kNoRegister = -1,
kNoRegister2 = -2,
// These registers both use the encoding R31, but to avoid mistakes we give
// them different values, and then translate before encoding.
CSP = 32,
ZR = 33,
// Aliases.
IP0 = R16,
IP1 = R17,
SP = R15,
FP = R29,
LR = R30, // Note: direct access to this constant is not allowed. See above.
};
enum VRegister {
// v0 Volatile; Parameter/scratch register, result register.
V0 = 0,
// v1-v7 Volatile; Parameter/scratch register.
V1 = 1,
V2 = 2,
V3 = 3,
V4 = 4,
V5 = 5,
V6 = 6,
V7 = 7,
// v8-v15 Non-volatile; Scratch registers
// Only the bottom 64 bits are non-volatile! [ARM IHI 0055B, 5.1.2]
V8 = 8,
V9 = 9,
V10 = 10,
V11 = 11,
V12 = 12,
V13 = 13,
V14 = 14,
V15 = 15,
// v16-v31 Volatile; Scratch registers.
V16 = 16,
V17 = 17,
V18 = 18,
V19 = 19,
V20 = 20,
V21 = 21,
V22 = 22,
V23 = 23,
V24 = 24,
V25 = 25,
V26 = 26,
V27 = 27,
V28 = 28,
V29 = 29,
V30 = 30,
V31 = 31,
kNumberOfVRegisters = 32,
kNoVRegister = -1,
};
// Register alias for floating point scratch register.
const VRegister VTMP = V31;
// Architecture independent aliases.
typedef VRegister FpuRegister;
const FpuRegister FpuTMP = VTMP;
const int kFpuRegisterSize = 16;
typedef simd128_value_t fpu_register_t;
const int kNumberOfFpuRegisters = kNumberOfVRegisters;
const FpuRegister kNoFpuRegister = kNoVRegister;
extern const char* const cpu_reg_names[kNumberOfCpuRegisters];
extern const char* const cpu_reg_abi_names[kNumberOfCpuRegisters];
extern const char* const fpu_reg_names[kNumberOfFpuRegisters];
// Register aliases.
const Register TMP = R16; // Used as scratch register by assembler.
const Register TMP2 = R17;
const Register PP = R27; // Caches object pool pointer in generated code.
const Register DISPATCH_TABLE_REG = R21; // Dispatch table register.
const Register CODE_REG = R24;
// Set when calling Dart functions in JIT mode, used by LazyCompileStub.
const Register FUNCTION_REG = R0;
const Register FPREG = FP; // Frame pointer register.
const Register SPREG = R15; // Stack pointer register.
const Register IC_DATA_REG = R5; // ICData/MegamorphicCache register.
const Register ARGS_DESC_REG = R4; // Arguments descriptor register.
const Register THR = R26; // Caches current thread in generated code.
const Register CALLEE_SAVED_TEMP = R19;
const Register CALLEE_SAVED_TEMP2 = R20;
const Register HEAP_BITS = R28; // write_barrier_mask << 32 | heap_base >> 32
const Register NULL_REG = R22; // Caches NullObject() value.
// 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 = R25;
// Common ABI for shared slow path stubs.
struct SharedSlowPathStubABI {
static const Register kResultReg = R0;
};
// ABI for instantiation stubs.
struct InstantiationABI {
static const Register kUninstantiatedTypeArgumentsReg = R3;
static const Register kInstantiatorTypeArgumentsReg = R2;
static const Register kFunctionTypeArgumentsReg = R1;
static const Register kResultTypeArgumentsReg = R0;
static const Register kResultTypeReg = R0;
static const Register kScratchReg = R8;
};
// Registers in addition to those listed in TypeTestABI used inside the
// implementation of type testing stubs that are _not_ preserved.
struct TTSInternalRegs {
static const Register kInstanceTypeArgumentsReg = R7;
static const Register kScratchReg = R9;
static const Register kSubTypeArgumentReg = R5;
static const Register kSuperTypeArgumentReg = R6;
// Must be pushed/popped whenever generic type arguments are being checked as
// they overlap with registers in TypeTestABI.
static const intptr_t kSavedTypeArgumentRegisters = 0;
static const intptr_t kInternalRegisters =
((1 << kInstanceTypeArgumentsReg) | (1 << kScratchReg) |
(1 << kSubTypeArgumentReg) | (1 << kSuperTypeArgumentReg)) &
~kSavedTypeArgumentRegisters;
};
// Registers in addition to those listed in TypeTestABI used inside the
// implementation of subtype test cache stubs that are _not_ preserved.
struct STCInternalRegs {
static const Register kInstanceCidOrSignatureReg = R6;
static const Register kInstanceInstantiatorTypeArgumentsReg = R5;
static const Register kInstanceParentFunctionTypeArgumentsReg = R9;
static const Register kInstanceDelayedFunctionTypeArgumentsReg = R10;
static const intptr_t kInternalRegisters =
(1 << kInstanceCidOrSignatureReg) |
(1 << kInstanceInstantiatorTypeArgumentsReg) |
(1 << kInstanceParentFunctionTypeArgumentsReg) |
(1 << kInstanceDelayedFunctionTypeArgumentsReg);
};
// Calling convention when calling TypeTestingStub and SubtypeTestCacheStub.
struct TypeTestABI {
static const Register kInstanceReg = R0;
static const Register kDstTypeReg = R8;
static const Register kInstantiatorTypeArgumentsReg = R2;
static const Register kFunctionTypeArgumentsReg = R1;
static const Register kSubtypeTestCacheReg = R3;
static const Register kScratchReg = R4;
// For calls to InstanceOfStub.
static const Register kInstanceOfResultReg = kInstanceReg;
// For calls to SubtypeNTestCacheStub. Must not overlap with any other
// registers above, for it is also used internally as kNullReg in those stubs.
static const Register kSubtypeTestCacheResultReg = R7;
// Registers that need saving across SubtypeTestCacheStub calls.
static const intptr_t kSubtypeTestCacheStubCallerSavedRegisters =
1 << kSubtypeTestCacheReg;
static const intptr_t kPreservedAbiRegisters =
(1 << kInstanceReg) | (1 << kDstTypeReg) |
(1 << kInstantiatorTypeArgumentsReg) | (1 << kFunctionTypeArgumentsReg);
static const intptr_t kNonPreservedAbiRegisters =
TTSInternalRegs::kInternalRegisters |
STCInternalRegs::kInternalRegisters | (1 << kSubtypeTestCacheReg) |
(1 << kScratchReg) | (1 << kSubtypeTestCacheResultReg) | (1 << CODE_REG);
static const intptr_t kAbiRegisters =
kPreservedAbiRegisters | kNonPreservedAbiRegisters;
};
// Calling convention when calling AssertSubtypeStub.
struct AssertSubtypeABI {
static const Register kSubTypeReg = R0;
static const Register kSuperTypeReg = R8;
static const Register kInstantiatorTypeArgumentsReg = R2;
static const Register kFunctionTypeArgumentsReg = R1;
static const Register kDstNameReg = R3;
static const intptr_t kAbiRegisters =
(1 << kSubTypeReg) | (1 << kSuperTypeReg) |
(1 << kInstantiatorTypeArgumentsReg) | (1 << kFunctionTypeArgumentsReg) |
(1 << kDstNameReg);
// No result register, as AssertSubtype is only run for side effect
// (throws if the subtype check fails).
};
// ABI for InitStaticFieldStub.
struct InitStaticFieldABI {
static const Register kFieldReg = R2;
static const Register kResultReg = R0;
};
// Registers used inside the implementation of InitLateStaticFieldStub.
struct InitLateStaticFieldInternalRegs {
static const Register kAddressReg = R3;
static const Register kScratchReg = R4;
};
// ABI for InitInstanceFieldStub.
struct InitInstanceFieldABI {
static const Register kInstanceReg = R1;
static const Register kFieldReg = R2;
static const Register kResultReg = R0;
};
// Registers used inside the implementation of InitLateInstanceFieldStub.
struct InitLateInstanceFieldInternalRegs {
static const Register kAddressReg = R3;
static const Register kScratchReg = R4;
};
// ABI for LateInitializationError stubs.
struct LateInitializationErrorABI {
static const Register kFieldReg = R9;
};
// ABI for ThrowStub.
struct ThrowABI {
static const Register kExceptionReg = R0;
};
// ABI for ReThrowStub.
struct ReThrowABI {
static const Register kExceptionReg = R0;
static const Register kStackTraceReg = R1;
};
// ABI for AssertBooleanStub.
struct AssertBooleanABI {
static const Register kObjectReg = R0;
};
// ABI for RangeErrorStub.
struct RangeErrorABI {
static const Register kLengthReg = R0;
static const Register kIndexReg = R1;
};
// ABI for AllocateObjectStub.
struct AllocateObjectABI {
static const Register kResultReg = R0;
static const Register kTypeArgumentsReg = R1;
static const Register kTagsReg = R2;
};
// ABI for AllocateClosureStub.
struct AllocateClosureABI {
static const Register kResultReg = AllocateObjectABI::kResultReg;
static const Register kFunctionReg = R1;
static const Register kContextReg = R2;
static const Register kScratchReg = R4;
};
// ABI for AllocateMintShared*Stub.
struct AllocateMintABI {
static const Register kResultReg = AllocateObjectABI::kResultReg;
static const Register kTempReg = R1;
};
// ABI for Allocate{Mint,Double,Float32x4,Float64x2}Stub.
struct AllocateBoxABI {
static const Register kResultReg = AllocateObjectABI::kResultReg;
static const Register kTempReg = R1;
};
// ABI for AllocateArrayStub.
struct AllocateArrayABI {
static const Register kResultReg = AllocateObjectABI::kResultReg;
static const Register kLengthReg = R2;
static const Register kTypeArgumentsReg = R1;
};
// ABI for AllocateRecordStub.
struct AllocateRecordABI {
static const Register kResultReg = AllocateObjectABI::kResultReg;
static const Register kNumFieldsReg = R2;
static const Register kFieldNamesReg = R1;
static const Register kTemp1Reg = R3;
static const Register kTemp2Reg = R4;
};
// ABI for AllocateSmallRecordStub (AllocateRecord2, AllocateRecord2Named,
// AllocateRecord3, AllocateRecord3Named).
struct AllocateSmallRecordABI {
static const Register kResultReg = AllocateObjectABI::kResultReg;
static const Register kFieldNamesReg = R1;
static const Register kValue0Reg = R2;
static const Register kValue1Reg = R3;
static const Register kValue2Reg = R4;
static const Register kTempReg = R5;
};
// ABI for AllocateTypedDataArrayStub.
struct AllocateTypedDataArrayABI {
static const Register kResultReg = AllocateObjectABI::kResultReg;
static const Register kLengthReg = R4;
};
// ABI for BoxDoubleStub.
struct BoxDoubleStubABI {
static const FpuRegister kValueReg = V0;
static const Register kTempReg = R1;
static const Register kResultReg = R0;
};
// ABI for DoubleToIntegerStub.
struct DoubleToIntegerStubABI {
static const FpuRegister kInputReg = V0;
static const Register kRecognizedKindReg = R0;
static const Register kResultReg = R0;
};
// ABI for SuspendStub (AwaitStub, YieldAsyncStarStub,
// SuspendSyncStarAtStartStub, SuspendSyncStarAtYieldStub).
struct SuspendStubABI {
static const Register kArgumentReg = R0;
static const Register kTempReg = R1;
static const Register kFrameSizeReg = R2;
static const Register kSuspendStateReg = R3;
static const Register kFunctionDataReg = R4;
static const Register kSrcFrameReg = R5;
static const Register kDstFrameReg = R6;
};
// ABI for InitSuspendableFunctionStub (InitAsyncStub, InitAsyncStarStub,
// InitSyncStarStub).
struct InitSuspendableFunctionStubABI {
static const Register kTypeArgsReg = R0;
};
// ABI for ResumeStub
struct ResumeStubABI {
static const Register kSuspendStateReg = R2;
static const Register kTempReg = R0;
// Registers for the frame copying (the 1st part).
static const Register kFrameSizeReg = R1;
static const Register kSrcFrameReg = R3;
static const Register kDstFrameReg = R4;
// Registers for control transfer.
// (the 2nd part, can reuse registers from the 1st part)
static const Register kResumePcReg = R1;
// Can also reuse kSuspendStateReg but should not conflict with CODE_REG/PP.
static const Register kExceptionReg = R3;
static const Register kStackTraceReg = R4;
};
// ABI for ReturnStub (ReturnAsyncStub, ReturnAsyncNotFutureStub,
// ReturnAsyncStarStub).
struct ReturnStubABI {
static const Register kSuspendStateReg = R2;
};
// ABI for AsyncExceptionHandlerStub.
struct AsyncExceptionHandlerStubABI {
static const Register kSuspendStateReg = R2;
};
// ABI for CloneSuspendStateStub.
struct CloneSuspendStateStubABI {
static const Register kSourceReg = R0;
static const Register kDestinationReg = R1;
static const Register kTempReg = R2;
static const Register kFrameSizeReg = R3;
static const Register kSrcFrameReg = R4;
static const Register kDstFrameReg = R5;
};
// ABI for DispatchTableNullErrorStub and consequently for all dispatch
// table calls (though normal functions will not expect or use this
// register). This ABI is added to distinguish memory corruption errors from
// null errors.
struct DispatchTableNullErrorABI {
static const Register kClassIdReg = R0;
};
// TODO(regis): Add ABIs for type testing stubs and is-type test stubs instead
// of reusing the constants of the instantiation stubs ABI.
// Masks, sizes, etc.
const int kXRegSizeInBits = 64;
const int kWRegSizeInBits = 32;
const int64_t kXRegMask = 0xffffffffffffffffL;
const int64_t kWRegMask = 0x00000000ffffffffL;
// List of registers used in load/store multiple.
typedef uint32_t RegList;
const RegList kAllCpuRegistersList = 0xFFFFFFFF;
const RegList kAllFpuRegistersList = 0xFFFFFFFF;
// See "Procedure Call Standard for the ARM 64-bit Architecture", document
// number "ARM IHI 0055B", May 22 2013.
#define R(reg) (static_cast<RegList>(1) << (reg))
// C++ ABI call registers.
const RegList kAbiArgumentCpuRegs =
R(R0) | R(R1) | R(R2) | R(R3) | R(R4) | R(R5) | R(R6) | R(R7);
const RegList kAbiVolatileCpuRegs = kAbiArgumentCpuRegs | R(R8) | R(R9) |
R(R10) | R(R11) | R(R12) | R(R13) | R(R14) |
R(R15) | R(R16) | R(R17) | R(LR);
#if defined(DART_TARGET_OS_FUCHSIA)
// We rely on R18 not being touched by Dart generated assembly or stubs at all.
// We rely on that any calls into C++ also preserve R18.
const RegList kAbiPreservedCpuRegs = R(R18) | R(R19) | R(R20) | R(R21) |
R(R22) | R(R23) | R(R24) | R(R25) |
R(R26) | R(R27) | R(R28);
const Register kAbiFirstPreservedCpuReg = R18;
const Register kAbiLastPreservedCpuReg = R28;
const int kAbiPreservedCpuRegCount = 11;
#else
const RegList kAbiPreservedCpuRegs = R(R19) | R(R20) | R(R21) | R(R22) |
R(R23) | R(R24) | R(R25) | R(R26) |
R(R27) | R(R28);
const Register kAbiFirstPreservedCpuReg = R19;
const Register kAbiLastPreservedCpuReg = R28;
const int kAbiPreservedCpuRegCount = 10;
#endif
const VRegister kAbiFirstPreservedFpuReg = V8;
const VRegister kAbiLastPreservedFpuReg = V15;
const int kAbiPreservedFpuRegCount = 8;
const RegList kReservedCpuRegisters = R(SPREG) | // Dart SP
R(FPREG) | R(TMP) | R(TMP2) | R(PP) |
R(THR) | R(LR) | R(HEAP_BITS) |
R(NULL_REG) | R(R31) | // C++ SP
R(R18) | R(DISPATCH_TABLE_REG);
constexpr intptr_t kNumberOfReservedCpuRegisters =
Utils::CountOneBits32(kReservedCpuRegisters);
// CPU registers available to Dart allocator.
const RegList kDartAvailableCpuRegs =
kAllCpuRegistersList & ~kReservedCpuRegisters;
constexpr int kNumberOfDartAvailableCpuRegs =
kNumberOfCpuRegisters - kNumberOfReservedCpuRegisters;
// No reason to prefer certain registers on ARM64.
constexpr int kRegisterAllocationBias = 0;
// Registers available to Dart that are not preserved by runtime calls.
const RegList kDartVolatileCpuRegs =
kDartAvailableCpuRegs & ~kAbiPreservedCpuRegs;
const Register kDartFirstVolatileCpuReg = R0;
const Register kDartLastVolatileCpuReg = R14;
const int kDartVolatileCpuRegCount = 15;
const int kDartVolatileFpuRegCount = 24;
const RegList kAbiVolatileFpuRegs =
R(V0) | R(V1) | R(V2) | R(V3) | R(V4) | R(V5) | R(V6) | R(V7) | R(V16) |
R(V17) | R(V18) | R(V19) | R(V20) | R(V21) | R(V22) | R(V23) | R(V24) |
R(V25) | R(V26) | R(V27) | R(V28) | R(V29) | R(V30) | R(V31);
constexpr int kStoreBufferWrapperSize = 32;
class CallingConventions {
public:
static const intptr_t kArgumentRegisters = kAbiArgumentCpuRegs;
static const Register ArgumentRegisters[];
static const intptr_t kNumArgRegs = 8;
// The native ABI uses R8 to pass the pointer to the memory preallocated for
// struct return values. Arm64 is the only ABI in which this pointer is _not_
// in ArgumentRegisters[0] or on the stack.
static const Register kPointerToReturnStructRegisterCall = R8;
static const Register kPointerToReturnStructRegisterReturn = R8;
static const FpuRegister FpuArgumentRegisters[];
static const intptr_t kFpuArgumentRegisters =
R(V0) | R(V1) | R(V2) | R(V3) | R(V4) | R(V5) | R(V6) | R(V7);
static const intptr_t kNumFpuArgRegs = 8;
static const bool kArgumentIntRegXorFpuReg = false;
static constexpr intptr_t kCalleeSaveCpuRegisters = kAbiPreservedCpuRegs;
// Whether larger than wordsize arguments are aligned to even registers.
static constexpr AlignmentStrategy kArgumentRegisterAlignment =
kAlignedToWordSize;
// How stack arguments are aligned.
#if defined(DART_TARGET_OS_MACOS_IOS) || defined(DART_TARGET_OS_MACOS)
// > Function arguments may consume slots on the stack that are not multiples
// > of 8 bytes.
// https://developer.apple.com/documentation/xcode/writing_arm64_code_for_apple_platforms
static constexpr AlignmentStrategy kArgumentStackAlignment =
kAlignedToValueSize;
#else
static constexpr AlignmentStrategy kArgumentStackAlignment =
kAlignedToWordSize;
#endif
// How fields in compounds are aligned.
static constexpr AlignmentStrategy kFieldAlignment = kAlignedToValueSize;
// Whether 1 or 2 byte-sized arguments or return values are passed extended
// to 4 bytes.
#if defined(DART_TARGET_OS_MACOS_IOS) || defined(DART_TARGET_OS_MACOS)
static constexpr ExtensionStrategy kReturnRegisterExtension = kExtendedTo4;
static constexpr ExtensionStrategy kArgumentRegisterExtension = kExtendedTo4;
#else
static constexpr ExtensionStrategy kReturnRegisterExtension = kNotExtended;
static constexpr ExtensionStrategy kArgumentRegisterExtension = kNotExtended;
#endif
static constexpr ExtensionStrategy kArgumentStackExtension = kNotExtended;
static constexpr Register kReturnReg = R0;
static constexpr Register kSecondReturnReg = kNoRegister;
static constexpr FpuRegister kReturnFpuReg = V0;
static constexpr Register kFfiAnyNonAbiRegister = R19;
static constexpr Register kFirstNonArgumentRegister = R9;
static constexpr Register kSecondNonArgumentRegister = R10;
static constexpr Register kStackPointerRegister = SPREG;
COMPILE_ASSERT(
((R(kFirstNonArgumentRegister) | R(kSecondNonArgumentRegister)) &
(kArgumentRegisters | R(kPointerToReturnStructRegisterCall))) == 0);
};
#undef R
static inline Register ConcreteRegister(Register r) {
return ((r == ZR) || (r == CSP)) ? R31 : 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)
NV = 15, // special condition (refer to section C1.2.3)
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,
OVERFLOW = VS,
NO_OVERFLOW = VC,
kInvalidCondition = 16
};
static inline Condition InvertCondition(Condition c) {
COMPILE_ASSERT((EQ ^ NE) == 1);
COMPILE_ASSERT((CS ^ CC) == 1);
COMPILE_ASSERT((MI ^ PL) == 1);
COMPILE_ASSERT((VS ^ VC) == 1);
COMPILE_ASSERT((HI ^ LS) == 1);
COMPILE_ASSERT((GE ^ LT) == 1);
COMPILE_ASSERT((GT ^ LE) == 1);
COMPILE_ASSERT((AL ^ NV) == 1);
ASSERT(c != AL);
ASSERT(c != kInvalidCondition);
return static_cast<Condition>(c ^ 1);
}
enum Bits {
B0 = (1 << 0),
B1 = (1 << 1),
B2 = (1 << 2),
B3 = (1 << 3),
B4 = (1 << 4),
B5 = (1 << 5),
B6 = (1 << 6),
B7 = (1 << 7),
B8 = (1 << 8),
B9 = (1 << 9),
B10 = (1 << 10),
B11 = (1 << 11),
B12 = (1 << 12),
B13 = (1 << 13),
B14 = (1 << 14),
B15 = (1 << 15),
B16 = (1 << 16),
B17 = (1 << 17),
B18 = (1 << 18),
B19 = (1 << 19),
B20 = (1 << 20),
B21 = (1 << 21),
B22 = (1 << 22),
B23 = (1 << 23),
B24 = (1 << 24),
B25 = (1 << 25),
B26 = (1 << 26),
B27 = (1 << 27),
B28 = (1 << 28),
B29 = (1 << 29),
B30 = (1 << 30),
B31 = (1 << 31),
};
// Opcodes from C3
// C3.1.
enum MainOp {
DPImmediateMask = 0x1c000000,
DPImmediateFixed = B28,
CompareBranchMask = 0x1c000000,
CompareBranchFixed = B28 | B26,
LoadStoreMask = B27 | B25,
LoadStoreFixed = B27,
DPRegisterMask = 0x0e000000,
DPRegisterFixed = B27 | B25,
DPSimd1Mask = 0x1e000000,
DPSimd1Fixed = B27 | B26 | B25,
DPSimd2Mask = 0x1e000000,
DPSimd2Fixed = B28 | DPSimd1Fixed,
FPMask = 0x5e000000,
FPFixed = B28 | B27 | B26 | B25,
};
// C3.2.1
enum CompareAndBranchOp {
CompareAndBranchMask = 0x7e000000,
CompareAndBranchFixed = CompareBranchFixed | B29,
CBZ = CompareAndBranchFixed,
CBNZ = CompareAndBranchFixed | B24,
};
// C.3.2.2
enum ConditionalBranchOp {
ConditionalBranchMask = 0xfe000000,
ConditionalBranchFixed = CompareBranchFixed | B30,
BCOND = ConditionalBranchFixed,
};
// C3.2.3
enum ExceptionGenOp {
ExceptionGenMask = 0xff000000,
ExceptionGenFixed = CompareBranchFixed | B31 | B30,
SVC = ExceptionGenFixed | B0,
BRK = ExceptionGenFixed | B21,
HLT = ExceptionGenFixed | B22,
};
// C3.2.4
enum SystemOp {
SystemMask = 0xffc00000,
SystemFixed = CompareBranchFixed | B31 | B30 | B24,
HINT = SystemFixed | B17 | B16 | B13 | B4 | B3 | B2 | B1 | B0,
CLREX = SystemFixed | B17 | B16 | B13 | B12 | B11 | B10 | B9 | B8 | B6 | B4 |
B3 | B2 | B1 | B0,
};
// C3.2.5
enum TestAndBranchOp {
TestAndBranchMask = 0x7e000000,
TestAndBranchFixed = CompareBranchFixed | B29 | B25,
TBZ = TestAndBranchFixed,
TBNZ = TestAndBranchFixed | B24,
};
// C3.2.6
enum UnconditionalBranchOp {
UnconditionalBranchMask = 0x7c000000,
UnconditionalBranchFixed = CompareBranchFixed,
B = UnconditionalBranchFixed,
BL = UnconditionalBranchFixed | B31,
};
// C3.2.7
enum UnconditionalBranchRegOp {
UnconditionalBranchRegMask = 0xfe000000,
UnconditionalBranchRegFixed = CompareBranchFixed | B31 | B30 | B25,
BR = UnconditionalBranchRegFixed | B20 | B19 | B18 | B17 | B16,
BLR = BR | B21,
RET = BR | B22,
};
// C3.3.5
enum LoadRegLiteralOp {
LoadRegLiteralMask = 0x3b000000,
LoadRegLiteralFixed = LoadStoreFixed | B28,
LDRpc = LoadRegLiteralFixed,
};
// C3.3.6
enum LoadStoreExclusiveOp {
LoadStoreExclusiveMask = 0x3f000000,
LoadStoreExclusiveFixed = B27,
LDXR = LoadStoreExclusiveFixed | B22,
STXR = LoadStoreExclusiveFixed,
LDAR = LoadStoreExclusiveFixed | B23 | B22 | B15,
STLR = LoadStoreExclusiveFixed | B23 | B15,
};
// C3.3.7-10
enum LoadStoreRegOp {
LoadStoreRegMask = 0x3a000000,
LoadStoreRegFixed = LoadStoreFixed | B29 | B28,
STR = LoadStoreRegFixed,
LDR = LoadStoreRegFixed | B22,
LDRS = LoadStoreRegFixed | B23,
FSTR = STR | B26,
FLDR = LDR | B26,
FSTRQ = STR | B26 | B23,
FLDRQ = LDR | B26 | B23,
};
// C3.3.14-16
enum LoadStoreRegPairOp {
LoadStoreRegPairMask = 0x3a000000,
LoadStoreRegPairFixed = LoadStoreFixed | B29,
STP = LoadStoreRegPairFixed,
LDP = LoadStoreRegPairFixed | B22,
FSTP = STP | B26,
FLDP = LDP | B26,
};
// C3.4.1
enum AddSubImmOp {
AddSubImmMask = 0x1f000000,
AddSubImmFixed = DPImmediateFixed | B24,
ADDI = AddSubImmFixed,
SUBI = AddSubImmFixed | B30,
};
// C3.4.2
enum BitfieldOp {
BitfieldMask = 0x1f800000,
BitfieldFixed = 0x13000000,
SBFM = BitfieldFixed,
BFM = BitfieldFixed | B29,
UBFM = BitfieldFixed | B30,
Bitfield64 = B31 | B22,
};
// C3.4.4
enum LogicalImmOp {
LogicalImmMask = 0x1f800000,
LogicalImmFixed = DPImmediateFixed | B25,
ANDI = LogicalImmFixed,
ORRI = LogicalImmFixed | B29,
EORI = LogicalImmFixed | B30,
ANDIS = LogicalImmFixed | B30 | B29,
};
// C3.4.5
enum MoveWideOp {
MoveWideMask = 0x1f800000,
MoveWideFixed = DPImmediateFixed | B25 | B23,
MOVN = MoveWideFixed,
MOVZ = MoveWideFixed | B30,
MOVK = MoveWideFixed | B30 | B29,
};
// C3.4.6
enum PCRelOp {
PCRelMask = 0x1f000000,
PCRelFixed = DPImmediateFixed,
ADR = PCRelFixed,
ADRP = PCRelFixed | B31,
};
// C3.5.1
enum AddSubShiftExtOp {
AddSubShiftExtMask = 0x1f000000,
AddSubShiftExtFixed = DPRegisterFixed | B24,
ADD = AddSubShiftExtFixed,
SUB = AddSubShiftExtFixed | B30,
};
// C3.5.3
enum AddSubWithCarryOp {
AddSubWithCarryMask = 0x1fe00000,
AddSubWithCarryFixed = DPRegisterFixed | B28,
ADC = AddSubWithCarryFixed,
SBC = AddSubWithCarryFixed | B30,
};
// C3.5.6
enum ConditionalSelectOp {
ConditionalSelectMask = 0x1fe00000,
ConditionalSelectFixed = DPRegisterFixed | B28 | B23,
CSEL = ConditionalSelectFixed,
CSINC = ConditionalSelectFixed | B10,
CSINV = ConditionalSelectFixed | B30,
CSNEG = ConditionalSelectFixed | B10 | B30,
};
// C3.5.7
enum MiscDP1SourceOp {
MiscDP1SourceMask = 0x5fe00000,
MiscDP1SourceFixed = DPRegisterFixed | B30 | B28 | B23 | B22,
CLZ = MiscDP1SourceFixed | B12,
RBIT = MiscDP1SourceFixed, // opc = '00'
};
// C3.5.8
enum MiscDP2SourceOp {
MiscDP2SourceMask = 0x5fe00000,
MiscDP2SourceFixed = DPRegisterFixed | B28 | B23 | B22,
UDIV = MiscDP2SourceFixed | B11,
SDIV = MiscDP2SourceFixed | B11 | B10,
LSLV = MiscDP2SourceFixed | B13,
LSRV = MiscDP2SourceFixed | B13 | B10,
ASRV = MiscDP2SourceFixed | B13 | B11,
};
// C3.5.9
enum MiscDP3SourceOp {
MiscDP3SourceMask = 0x1f000000,
MiscDP3SourceFixed = DPRegisterFixed | B28 | B24,
MADDW = MiscDP3SourceFixed,
MADD = MiscDP3SourceFixed | B31,
MSUBW = MiscDP3SourceFixed | B15,
MSUB = MiscDP3SourceFixed | B31 | B15,
SMULH = MiscDP3SourceFixed | B31 | B22,
UMULH = MiscDP3SourceFixed | B31 | B23 | B22,
SMADDL = MiscDP3SourceFixed | B31 | B21,
UMADDL = MiscDP3SourceFixed | B31 | B23 | B21,
SMSUBL = MiscDP3SourceFixed | B31 | B21 | B15,
UMSUBL = MiscDP3SourceFixed | B31 | B23 | B21 | B15,
};
// C3.5.10
enum LogicalShiftOp {
LogicalShiftMask = 0x1f000000,
LogicalShiftFixed = DPRegisterFixed,
AND = LogicalShiftFixed,
BIC = LogicalShiftFixed | B21,
ORR = LogicalShiftFixed | B29,
ORN = LogicalShiftFixed | B29 | B21,
EOR = LogicalShiftFixed | B30,
EON = LogicalShiftFixed | B30 | B21,
ANDS = LogicalShiftFixed | B30 | B29,
BICS = LogicalShiftFixed | B30 | B29 | B21,
};
// C.3.6.5
enum SIMDCopyOp {
SIMDCopyMask = 0x9fe08400,
SIMDCopyFixed = DPSimd1Fixed | B10,
VDUPI = SIMDCopyFixed | B30 | B11,
VINSI = SIMDCopyFixed | B30 | B12 | B11,
VMOVW = SIMDCopyFixed | B13 | B12 | B11,
VMOVX = SIMDCopyFixed | B30 | B13 | B12 | B11,
VDUP = SIMDCopyFixed | B30,
VINS = SIMDCopyFixed | B30 | B29,
};
// C.3.6.16
enum SIMDThreeSameOp {
SIMDThreeSameMask = 0x9f200400,
SIMDThreeSameFixed = DPSimd1Fixed | B21 | B10,
VAND = SIMDThreeSameFixed | B30 | B12 | B11,
VORR = SIMDThreeSameFixed | B30 | B23 | B12 | B11,
VEOR = SIMDThreeSameFixed | B30 | B29 | B12 | B11,
VADDW = SIMDThreeSameFixed | B30 | B23 | B15,
VADDX = SIMDThreeSameFixed | B30 | B23 | B22 | B15,
VSUBW = SIMDThreeSameFixed | B30 | B29 | B23 | B15,
VSUBX = SIMDThreeSameFixed | B30 | B29 | B23 | B22 | B15,
VADDS = SIMDThreeSameFixed | B30 | B15 | B14 | B12,
VADDD = SIMDThreeSameFixed | B30 | B22 | B15 | B14 | B12,
VSUBS = SIMDThreeSameFixed | B30 | B23 | B15 | B14 | B12,
VSUBD = SIMDThreeSameFixed | B30 | B23 | B22 | B15 | B14 | B12,
VMULS = SIMDThreeSameFixed | B30 | B29 | B15 | B14 | B12 | B11,
VMULD = SIMDThreeSameFixed | B30 | B29 | B22 | B15 | B14 | B12 | B11,
VDIVS = SIMDThreeSameFixed | B30 | B29 | B15 | B14 | B13 | B12 | B11,
VDIVD = SIMDThreeSameFixed | B30 | B29 | B22 | B15 | B14 | B13 | B12 | B11,
VCEQS = SIMDThreeSameFixed | B30 | B15 | B14 | B13,
VCEQD = SIMDThreeSameFixed | B30 | B22 | B15 | B14 | B13,
VCGES = SIMDThreeSameFixed | B30 | B29 | B15 | B14 | B13,
VCGED = SIMDThreeSameFixed | B30 | B29 | B22 | B15 | B14 | B13,
VCGTS = SIMDThreeSameFixed | B30 | B29 | B23 | B15 | B14 | B13,
VCGTD = SIMDThreeSameFixed | B30 | B29 | B23 | B22 | B15 | B14 | B13,
VMAXS = SIMDThreeSameFixed | B30 | B15 | B14 | B13 | B12,
VMAXD = SIMDThreeSameFixed | B30 | B22 | B15 | B14 | B13 | B12,
VMINS = SIMDThreeSameFixed | B30 | B23 | B15 | B14 | B13 | B12,
VMIND = SIMDThreeSameFixed | B30 | B23 | B22 | B15 | B14 | B13 | B12,
VRECPSS = SIMDThreeSameFixed | B30 | B15 | B14 | B13 | B12 | B11,
VRSQRTSS = SIMDThreeSameFixed | B30 | B23 | B15 | B14 | B13 | B12 | B11,
};
// C.3.6.17
enum SIMDTwoRegOp {
SIMDTwoRegMask = 0x9f3e0c00,
SIMDTwoRegFixed = DPSimd1Fixed | B21 | B11,
VNOT = SIMDTwoRegFixed | B30 | B29 | B14 | B12,
VABSS = SIMDTwoRegFixed | B30 | B23 | B15 | B14 | B13 | B12,
VNEGS = SIMDTwoRegFixed | B30 | B29 | B23 | B15 | B14 | B13 | B12,
VABSD = SIMDTwoRegFixed | B30 | B23 | B22 | B15 | B14 | B13 | B12,
VNEGD = SIMDTwoRegFixed | B30 | B29 | B23 | B22 | B15 | B14 | B13 | B12,
VSQRTS = SIMDTwoRegFixed | B30 | B29 | B23 | B16 | B15 | B14 | B13 | B12,
VSQRTD =
SIMDTwoRegFixed | B30 | B29 | B23 | B22 | B16 | B15 | B14 | B13 | B12,
VRECPES = SIMDTwoRegFixed | B30 | B23 | B16 | B15 | B14 | B12,
VRSQRTES = SIMDTwoRegFixed | B30 | B29 | B23 | B16 | B15 | B14 | B12,
};
// C.3.6.22
enum FPCompareOp {
FPCompareMask = 0xffa0fc07,
FPCompareFixed = FPFixed | B21 | B13,
FCMPD = FPCompareFixed | B22,
FCMPZD = FPCompareFixed | B22 | B3,
};
// C3.6.25
enum FPOneSourceOp {
FPOneSourceMask = 0x5f207c00,
FPOneSourceFixed = FPFixed | B21 | B14,
FMOVDD = FPOneSourceFixed | B22,
FABSD = FPOneSourceFixed | B22 | B15,
FNEGD = FPOneSourceFixed | B22 | B16,
FSQRTD = FPOneSourceFixed | B22 | B16 | B15,
FCVTDS = FPOneSourceFixed | B15 | B17,
FCVTSD = FPOneSourceFixed | B22 | B17,
};
// C3.6.26
enum FPTwoSourceOp {
FPTwoSourceMask = 0xff200c00,
FPTwoSourceFixed = FPFixed | B21 | B11,
FMULD = FPTwoSourceFixed | B22,
FDIVD = FPTwoSourceFixed | B22 | B12,
FADDD = FPTwoSourceFixed | B22 | B13,
FSUBD = FPTwoSourceFixed | B22 | B13 | B12,
};
// C3.6.28
enum FPImmOp {
FPImmMask = 0x5f201c00,
FPImmFixed = FPFixed | B21 | B12,
FMOVSI = FPImmFixed,
FMOVDI = FPImmFixed | B22,
};
// C3.6.30
enum FPIntCvtOp {
FPIntCvtMask = 0x5f00fc00,
FPIntCvtFixed = FPFixed | B21,
FMOVRS = FPIntCvtFixed | B18 | B17,
FMOVSR = FPIntCvtFixed | B18 | B17 | B16,
FMOVRD = FPIntCvtFixed | B22 | B18 | B17,
FMOVDR = FPIntCvtFixed | B22 | B18 | B17 | B16,
FCVTZS_D = FPIntCvtFixed | B22 | B20 | B19,
FCVTMS_D = FPIntCvtFixed | B22 | B20,
FCVTPS_D = FPIntCvtFixed | B22 | B19,
SCVTFD = FPIntCvtFixed | B22 | B17,
};
#define APPLY_OP_LIST(_V) \
_V(DPImmediate) \
_V(CompareBranch) \
_V(LoadStore) \
_V(DPRegister) \
_V(DPSimd1) \
_V(DPSimd2) \
_V(FP) \
_V(CompareAndBranch) \
_V(ConditionalBranch) \
_V(ExceptionGen) \
_V(System) \
_V(TestAndBranch) \
_V(UnconditionalBranch) \
_V(UnconditionalBranchReg) \
_V(LoadStoreReg) \
_V(LoadStoreRegPair) \
_V(LoadRegLiteral) \
_V(LoadStoreExclusive) \
_V(AddSubImm) \
_V(Bitfield) \
_V(LogicalImm) \
_V(MoveWide) \
_V(PCRel) \
_V(AddSubShiftExt) \
_V(AddSubWithCarry) \
_V(ConditionalSelect) \
_V(MiscDP1Source) \
_V(MiscDP2Source) \
_V(MiscDP3Source) \
_V(LogicalShift) \
_V(SIMDCopy) \
_V(SIMDThreeSame) \
_V(SIMDTwoReg) \
_V(FPCompare) \
_V(FPOneSource) \
_V(FPTwoSource) \
_V(FPImm) \
_V(FPIntCvt)
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,
};
enum Extend {
kNoExtend = -1,
UXTB = 0, // Zero extend byte.
UXTH = 1, // Zero extend halfword (16 bits).
UXTW = 2, // Zero extend word (32 bits).
UXTX = 3, // Zero extend doubleword (64 bits).
SXTB = 4, // Sign extend byte.
SXTH = 5, // Sign extend halfword (16 bits).
SXTW = 6, // Sign extend word (32 bits).
SXTX = 7, // Sign extend doubleword (64 bits).
kMaxExtend = 8,
};
enum R31Type {
R31IsSP,
R31IsZR,
};
// 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 {
// S-bit (modify condition register)
kSShift = 29,
kSBits = 1,
// sf field.
kSFShift = 31,
kSFBits = 1,
// size field,
kSzShift = 30,
kSzBits = 2,
// Registers.
kRdShift = 0,
kRdBits = 5,
kRnShift = 5,
kRnBits = 5,
kRaShift = 10,
kRaBits = 5,
kRmShift = 16,
kRmBits = 5,
kRtShift = 0,
kRtBits = 5,
kRt2Shift = 10,
kRt2Bits = 5,
kRsShift = 16,
kRsBits = 5,
// V Registers.
kVdShift = 0,
kVdBits = 5,
kVnShift = 5,
kVnBits = 5,
kVmShift = 16,
kVmBits = 5,
kVtShift = 0,
kVtBits = 5,
kVt2Shift = 10,
kVt2Bits = 5,
// Immediates.
kImm3Shift = 10,
kImm3Bits = 3,
kImm4Shift = 11,
kImm4Bits = 4,
kImm5Shift = 16,
kImm5Bits = 5,
kImm6Shift = 10,
kImm6Bits = 6,
kImm7Shift = 15,
kImm7Bits = 7,
kImm7Mask = 0x7f << kImm7Shift,
kImm8Shift = 13,
kImm8Bits = 8,
kImm9Shift = 12,
kImm9Bits = 9,
kImm12Shift = 10,
kImm12Bits = 12,
kImm12Mask = 0xfff << kImm12Shift,
kImm12ShiftShift = 22,
kImm12ShiftBits = 2,
kImm14Shift = 5,
kImm14Bits = 14,
kImm14Mask = 0x3fff << kImm14Shift,
kImm16Shift = 5,
kImm16Bits = 16,
kImm16Mask = 0xffff << kImm16Shift,
kImm19Shift = 5,
kImm19Bits = 19,
kImm19Mask = 0x7ffff << kImm19Shift,
kImm26Shift = 0,
kImm26Bits = 26,
kImm26Mask = 0x03ffffff << kImm26Shift,
kCondShift = 0,
kCondBits = 4,
kCondMask = 0xf << kCondShift,
kSelCondShift = 12,
kSelCondBits = 4,
// Bitfield immediates.
kNShift = 22,
kNBits = 1,
kImmRShift = 16,
kImmRBits = 6,
kImmSShift = 10,
kImmSBits = 6,
kHWShift = 21,
kHWBits = 2,
// Shift and Extend.
kAddShiftExtendShift = 21,
kAddShiftExtendBits = 1,
kShiftTypeShift = 22,
kShiftTypeBits = 2,
kExtendTypeShift = 13,
kExtendTypeBits = 3,
// Hint Fields.
kHintCRmShift = 8,
kHintCRmBits = 4,
kHintOp2Shift = 5,
kHintOp2Bits = 3,
};
// Helper functions for decoding logical immediates.
static inline uint64_t RotateRight(uint64_t value,
uint8_t rotate,
uint8_t width) {
ASSERT(width <= 64);
uint8_t right = rotate & 63;
uint8_t left = (width - rotate) & 63;
return ((value & ((1ULL << right) - 1ULL)) << left) | (value >> right);
}
static inline uint64_t RepeatBitsAcrossReg(uint8_t reg_size,
uint64_t value,
uint8_t width) {
ASSERT((width == 2) || (width == 4) || (width == 8) || (width == 16) ||
(width == 32));
ASSERT((reg_size == kWRegSizeInBits) || (reg_size == kXRegSizeInBits));
uint64_t result = value & ((1ULL << width) - 1ULL);
for (unsigned i = width; i < reg_size; i *= 2) {
result |= (result << i);
}
return result;
}
enum ScaleFactor {
TIMES_1 = 0,
TIMES_2 = 1,
TIMES_4 = 2,
TIMES_8 = 3,
TIMES_16 = 4,
// We can't include vm/compiler/runtime_api.h, so just be explicit instead
// of using (dart::)kWordSizeLog2.
#if defined(TARGET_ARCH_IS_64_BIT)
// Used for Smi-boxed indices.
TIMES_HALF_WORD_SIZE = kInt64SizeLog2 - 1,
// Used for unboxed indices.
TIMES_WORD_SIZE = kInt64SizeLog2,
#else
#error "Unexpected word size"
#endif
#if !defined(DART_COMPRESSED_POINTERS)
TIMES_COMPRESSED_WORD_SIZE = TIMES_WORD_SIZE,
#else
TIMES_COMPRESSED_WORD_SIZE = TIMES_HALF_WORD_SIZE,
#endif
// Used for Smi-boxed indices.
TIMES_COMPRESSED_HALF_WORD_SIZE = TIMES_COMPRESSED_WORD_SIZE - 1,
};
// 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 };
enum class WideSize { k32Bits, k64Bits };
static const int32_t kNopInstruction = HINT; // hint #0 === nop.
// Reserved brk and hlt instruction codes.
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 = // brk(0xdeb0).
BRK | (kBreakPointCode << kImm16Shift);
// 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. brk(0).
static const int32_t kSimulatorBreakpointInstruction =
HLT | (kSimulatorBreakCode << kImm16Shift);
// Runtime call redirection instruction used by the simulator.
static const int32_t kSimulatorRedirectInstruction =
HLT | (kSimulatorRedirectCode << kImm16Shift);
// 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);
}
// 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;
}
inline void SetMoveWideBits(MoveWideOp op,
Register rd,
uint16_t imm,
int hw,
WideSize sz) {
ASSERT((hw >= 0) && (hw <= 3));
const int32_t size = (sz == WideSize::k64Bits) ? B31 : 0;
SetInstructionBits(op | size | (static_cast<int32_t>(rd) << kRdShift) |
(static_cast<int32_t>(hw) << kHWShift) |
(static_cast<int32_t>(imm) << kImm16Shift));
}
inline void SetUnconditionalBranchRegBits(UnconditionalBranchRegOp op,
Register rn) {
SetInstructionBits(op | (static_cast<int32_t>(rn) << kRnShift));
}
inline void SetImm12Bits(int32_t orig, int32_t imm12) {
ASSERT((imm12 & 0xfffff000) == 0);
SetInstructionBits((orig & ~kImm12Mask) | (imm12 << kImm12Shift));
}
inline int NField() const { return Bit(22); }
inline int SField() const { return Bit(kSShift); }
inline int SFField() const { return Bit(kSFShift); }
inline int SzField() const { return Bits(kSzShift, kSzBits); }
inline Register RdField() const {
return static_cast<Register>(Bits(kRdShift, kRdBits));
}
inline Register RnField() const {
return static_cast<Register>(Bits(kRnShift, kRnBits));
}
inline Register RaField() const {
return static_cast<Register>(Bits(kRaShift, kRaBits));
}
inline Register RmField() const {
return static_cast<Register>(Bits(kRmShift, kRmBits));
}
inline Register RtField() const {
return static_cast<Register>(Bits(kRtShift, kRtBits));
}
inline Register Rt2Field() const {
return static_cast<Register>(Bits(kRt2Shift, kRt2Bits));
}
inline Register RsField() const {
return static_cast<Register>(Bits(kRsShift, kRsBits));
}
inline VRegister VdField() const {
return static_cast<VRegister>(Bits(kVdShift, kVdBits));
}
inline VRegister VnField() const {
return static_cast<VRegister>(Bits(kVnShift, kVnBits));
}
inline VRegister VmField() const {
return static_cast<VRegister>(Bits(kVmShift, kVmBits));
}
inline VRegister VtField() const {
return static_cast<VRegister>(Bits(kVtShift, kVtBits));
}
inline VRegister Vt2Field() const {
return static_cast<VRegister>(Bits(kVt2Shift, kVt2Bits));
}
// Immediates
inline int Imm3Field() const { return Bits(kImm3Shift, kImm3Bits); }
inline int Imm6Field() const { return Bits(kImm6Shift, kImm6Bits); }
inline int Imm7Field() const { return Bits(kImm7Shift, kImm7Bits); }
// Sign-extended Imm7Field()
inline int64_t SImm7Field() const {
return (static_cast<int32_t>(Imm7Field()) << 25) >> 25;
}
inline int Imm8Field() const { return Bits(kImm8Shift, kImm8Bits); }
inline int Imm9Field() const { return Bits(kImm9Shift, kImm9Bits); }
// Sign-extended Imm9Field()
inline int64_t SImm9Field() const {
return (static_cast<int32_t>(Imm9Field()) << 23) >> 23;
}
inline int Imm12Field() const { return Bits(kImm12Shift, kImm12Bits); }
inline int Imm12ShiftField() const {
return Bits(kImm12ShiftShift, kImm12ShiftBits);
}
inline int Imm16Field() const { return Bits(kImm16Shift, kImm16Bits); }
inline int HWField() const { return Bits(kHWShift, kHWBits); }
inline int ImmRField() const { return Bits(kImmRShift, kImmRBits); }
inline int ImmSField() const { return Bits(kImmSShift, kImmSBits); }
inline int Imm14Field() const { return Bits(kImm14Shift, kImm14Bits); }
inline int64_t SImm14Field() const {
return (static_cast<int32_t>(Imm14Field()) << 18) >> 18;
}
inline int Imm19Field() const { return Bits(kImm19Shift, kImm19Bits); }
inline int64_t SImm19Field() const {
return (static_cast<int32_t>(Imm19Field()) << 13) >> 13;
}
inline int Imm26Field() const { return Bits(kImm26Shift, kImm26Bits); }
inline int64_t SImm26Field() const {
return (static_cast<int32_t>(Imm26Field()) << 6) >> 6;
}
inline Condition ConditionField() const {
return static_cast<Condition>(Bits(kCondShift, kCondBits));
}
inline Condition SelectConditionField() const {
return static_cast<Condition>(Bits(kSelCondShift, kSelCondBits));
}
// Shift and Extend.
inline bool IsShift() const {
return IsLogicalShiftOp() || (Bit(kAddShiftExtendShift) == 0);
}
inline bool IsExtend() const {
return !IsLogicalShiftOp() && (Bit(kAddShiftExtendShift) == 1);
}
inline Shift ShiftTypeField() const {
return static_cast<Shift>(Bits(kShiftTypeShift, kShiftTypeBits));
}
inline Extend ExtendTypeField() const {
return static_cast<Extend>(Bits(kExtendTypeShift, kExtendTypeBits));
}
inline int ShiftAmountField() const { return Imm6Field(); }
inline int ExtShiftAmountField() const { return Imm3Field(); }
// Instruction identification.
#define IS_OP(op) \
inline bool Is##op##Op() const { \
return ((InstructionBits() & op##Mask) == (op##Fixed & op##Mask)); \
}
APPLY_OP_LIST(IS_OP)
#undef IS_OP
inline bool HasS() const { return (SField() == 1); }
// Indicate whether Rd can be the CSP or ZR. This does not check that the
// instruction actually has an Rd field.
R31Type RdMode() const {
// The following instructions use CSP as Rd:
// Add/sub (immediate) when not setting the flags.
// Add/sub (extended) when not setting the flags.
// Logical (immediate) when not setting the flags.
// Otherwise, R31 is the ZR.
if (IsAddSubImmOp() || (IsAddSubShiftExtOp() && IsExtend())) {
if (HasS()) {
return R31IsZR;
} else {
return R31IsSP;
}
}
if (IsLogicalImmOp()) {
const int op = Bits(29, 2);
const bool set_flags = op == 3;
if (set_flags) {
return R31IsZR;
} else {
return R31IsSP;
}
}
return R31IsZR;
}
// Indicate whether Rn can be CSP or ZR. This does not check that the
// instruction actually has an Rn field.
R31Type RnMode() const {
// The following instructions use CSP as Rn:
// All loads and stores.
// Add/sub (immediate).
// Add/sub (extended).
// Otherwise, r31 is ZR.
if (IsLoadStoreOp() || IsAddSubImmOp() ||
(IsAddSubShiftExtOp() && IsExtend())) {
return R31IsSP;
}
return R31IsZR;
}
// Logical immediates can't encode zero, so a return value of zero is used to
// indicate a failure case. Specifically, where the constraints on imm_s are
// not met.
uint64_t ImmLogical() {
const uint8_t reg_size = SFField() == 1 ? kXRegSizeInBits : kWRegSizeInBits;
const int64_t n = NField();
const int64_t imm_s = ImmSField();
const int64_t imm_r = ImmRField();
// An integer is constructed from the n, imm_s and imm_r bits according to
// the following table:
//
// N imms immr size S R
// 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr)
// 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr)
// 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr)
// 0 110sss xxxrrr 8 UInt(sss) UInt(rrr)
// 0 1110ss xxxxrr 4 UInt(ss) UInt(rr)
// 0 11110s xxxxxr 2 UInt(s) UInt(r)
// (s bits must not be all set)
//
// A pattern is constructed of size bits, where the least significant S+1
// bits are set. The pattern is rotated right by R, and repeated across a
// 32 or 64-bit value, depending on destination register width.
if (n == 1) {
if (imm_s == 0x3F) {
return 0;
}
uint64_t bits = (1ULL << (imm_s + 1)) - 1;
return RotateRight(bits, imm_r, 64);
} else {
if ((imm_s >> 1) == 0x1F) {
return 0;
}
for (int width = 0x20; width >= 0x2; width >>= 1) {
if ((imm_s & width) == 0) {
int mask = width - 1;
if ((imm_s & mask) == mask) {
return 0;
}
uint64_t bits = (1ULL << ((imm_s & mask) + 1)) - 1;
return RepeatBitsAcrossReg(
reg_size, RotateRight(bits, imm_r & mask, width), width);
}
}
}
UNREACHABLE();
return 0;
}
static int64_t VFPExpandImm(uint8_t imm8) {
const int64_t sign = static_cast<int64_t>((imm8 & 0x80) >> 7) << 63;
const int64_t hi_exp = static_cast<int64_t>(!((imm8 & 0x40) >> 6)) << 62;
const int64_t mid_exp = (((imm8 & 0x40) >> 6) == 0) ? 0 : (0xffLL << 54);
const int64_t low_exp = static_cast<int64_t>((imm8 & 0x30) >> 4) << 52;
const int64_t frac = static_cast<int64_t>(imm8 & 0x0f) << 48;
return sign | hi_exp | mid_exp | low_exp | frac;
}
// 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(uword pc) { return reinterpret_cast<Instr*>(pc); }
private:
DISALLOW_ALLOCATION();
DISALLOW_IMPLICIT_CONSTRUCTORS(Instr);
};
const uint64_t kBreakInstructionFiller = 0xD4200000D4200000L; // brk #0; brk #0
struct LinkRegister {};
constexpr bool operator==(Register r, LinkRegister) {
return r == LR;
}
constexpr bool operator!=(Register r, LinkRegister lr) {
return !(r == lr);
}
inline Register ConcreteRegister(LinkRegister) {
return LR;
}
#undef LR
#define LINK_REGISTER (LinkRegister())
} // namespace dart
#endif // RUNTIME_VM_CONSTANTS_ARM64_H_