| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifndef jit_arm64_Architecture_arm64_h |
| #define jit_arm64_Architecture_arm64_h |
| |
| #include "mozilla/Assertions.h" |
| #include "mozilla/MathAlgorithms.h" |
| |
| #include "js/Utility.h" |
| |
| namespace js { |
| namespace jit { |
| |
| // AArch64 has 32 64-bit integer registers, x0 though x31. |
| // x31 is special and functions as both the stack pointer and a zero register. |
| // The bottom 32 bits of each of the X registers is accessible as w0 through w31. |
| // The program counter is no longer accessible as a register. |
| // SIMD and scalar floating-point registers share a register bank. |
| // 32 bit float registers are s0 through s31. |
| // 64 bit double registers are d0 through d31. |
| // 128 bit SIMD registers are v0 through v31. |
| // e.g., s0 is the bottom 32 bits of d0, which is the bottom 64 bits of v0. |
| |
| // AArch64 Calling Convention: |
| // x0 - x7: arguments and return value |
| // x8: indirect result (struct) location |
| // x9 - x15: temporary registers |
| // x16 - x17: intra-call-use registers (PLT, linker) |
| // x18: platform specific use (TLS) |
| // x19 - x28: callee-saved registers |
| // x29: frame pointer |
| // x30: link register |
| |
| // AArch64 Calling Convention for Floats: |
| // d0 - d7: arguments and return value |
| // d8 - d15: callee-saved registers |
| // Bits 64:128 are not saved for v8-v15. |
| // d16 - d31: temporary registers |
| |
| // AArch64 does not have soft float. |
| |
| class Registers { |
| public: |
| enum RegisterID { |
| w0 = 0, x0 = 0, |
| w1 = 1, x1 = 1, |
| w2 = 2, x2 = 2, |
| w3 = 3, x3 = 3, |
| w4 = 4, x4 = 4, |
| w5 = 5, x5 = 5, |
| w6 = 6, x6 = 6, |
| w7 = 7, x7 = 7, |
| w8 = 8, x8 = 8, |
| w9 = 9, x9 = 9, |
| w10 = 10, x10 = 10, |
| w11 = 11, x11 = 11, |
| w12 = 12, x12 = 12, |
| w13 = 13, x13 = 13, |
| w14 = 14, x14 = 14, |
| w15 = 15, x15 = 15, |
| w16 = 16, x16 = 16, ip0 = 16, // MacroAssembler scratch register 1. |
| w17 = 17, x17 = 17, ip1 = 17, // MacroAssembler scratch register 2. |
| w18 = 18, x18 = 18, tls = 18, // Platform-specific use (TLS). |
| w19 = 19, x19 = 19, |
| w20 = 20, x20 = 20, |
| w21 = 21, x21 = 21, |
| w22 = 22, x22 = 22, |
| w23 = 23, x23 = 23, |
| w24 = 24, x24 = 24, |
| w25 = 25, x25 = 25, |
| w26 = 26, x26 = 26, |
| w27 = 27, x27 = 27, |
| w28 = 28, x28 = 28, |
| w29 = 29, x29 = 29, fp = 29, |
| w30 = 30, x30 = 30, lr = 30, |
| w31 = 31, x31 = 31, wzr = 31, xzr = 31, sp = 31, // Special: both stack pointer and a zero register. |
| invalid_reg |
| }; |
| typedef uint8_t Code; |
| typedef uint32_t Encoding; |
| typedef uint32_t SetType; |
| |
| union RegisterContent { |
| uintptr_t r; |
| }; |
| |
| static uint32_t SetSize(SetType x) { |
| static_assert(sizeof(SetType) == 4, "SetType must be 32 bits"); |
| return mozilla::CountPopulation32(x); |
| } |
| static uint32_t FirstBit(SetType x) { |
| return mozilla::CountTrailingZeroes32(x); |
| } |
| static uint32_t LastBit(SetType x) { |
| return 31 - mozilla::CountLeadingZeroes32(x); |
| } |
| |
| static const char* GetName(Code code) { |
| static const char* const Names[] = |
| { "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", |
| "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", |
| "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", |
| "lr", "sp", "invalid" }; |
| return Names[code]; |
| } |
| static const char* GetName(uint32_t i) { |
| MOZ_ASSERT(i < Total); |
| return GetName(Code(i)); |
| } |
| |
| static Code FromName(const char* name); |
| |
| // If SP is used as the base register for a memory load or store, then the value |
| // of the stack pointer prior to adding any offset must be quadword (16 byte) aligned, |
| // or else a stack aligment exception will be generated. |
| static const Code StackPointer = sp; |
| |
| static const Code Invalid = invalid_reg; |
| |
| static const uint32_t Total = 32; |
| static const uint32_t TotalPhys = 32; |
| static const uint32_t Allocatable = 27; // No named special-function registers. |
| |
| static const SetType AllMask = 0xFFFFFFFF; |
| |
| static const SetType ArgRegMask = |
| (1 << Registers::x0) | (1 << Registers::x1) | |
| (1 << Registers::x2) | (1 << Registers::x3) | |
| (1 << Registers::x4) | (1 << Registers::x5) | |
| (1 << Registers::x6) | (1 << Registers::x7) | |
| (1 << Registers::x8); |
| |
| static const SetType VolatileMask = |
| (1 << Registers::x0) | (1 << Registers::x1) | |
| (1 << Registers::x2) | (1 << Registers::x3) | |
| (1 << Registers::x4) | (1 << Registers::x5) | |
| (1 << Registers::x6) | (1 << Registers::x7) | |
| (1 << Registers::x8) | (1 << Registers::x9) | |
| (1 << Registers::x10) | (1 << Registers::x11) | |
| (1 << Registers::x11) | (1 << Registers::x12) | |
| (1 << Registers::x13) | (1 << Registers::x14) | |
| (1 << Registers::x14) | (1 << Registers::x15) | |
| (1 << Registers::x16) | (1 << Registers::x17) | |
| (1 << Registers::x18); |
| |
| static const SetType NonVolatileMask = |
| (1 << Registers::x19) | (1 << Registers::x20) | |
| (1 << Registers::x21) | (1 << Registers::x22) | |
| (1 << Registers::x23) | (1 << Registers::x24) | |
| (1 << Registers::x25) | (1 << Registers::x26) | |
| (1 << Registers::x27) | (1 << Registers::x28) | |
| (1 << Registers::x29) | (1 << Registers::x30); |
| |
| static const SetType SingleByteRegs = VolatileMask | NonVolatileMask; |
| |
| static const SetType NonAllocatableMask = |
| (1 << Registers::x28) | // PseudoStackPointer. |
| (1 << Registers::ip0) | // First scratch register. |
| (1 << Registers::ip1) | // Second scratch register. |
| (1 << Registers::tls) | |
| (1 << Registers::lr) | |
| (1 << Registers::sp); |
| |
| // Registers that can be allocated without being saved, generally. |
| static const SetType TempMask = VolatileMask & ~NonAllocatableMask; |
| |
| static const SetType WrapperMask = VolatileMask; |
| |
| // Registers returned from a JS -> JS call. |
| static const SetType JSCallMask = (1 << Registers::x2); |
| |
| // Registers returned from a JS -> C call. |
| static const SetType CallMask = (1 << Registers::x0); |
| |
| static const SetType AllocatableMask = AllMask & ~NonAllocatableMask; |
| }; |
| |
| // Smallest integer type that can hold a register bitmask. |
| typedef uint32_t PackedRegisterMask; |
| |
| template <typename T> |
| class TypedRegisterSet; |
| |
| class FloatRegisters |
| { |
| public: |
| enum FPRegisterID { |
| s0 = 0, d0 = 0, v0 = 0, |
| s1 = 1, d1 = 1, v1 = 1, |
| s2 = 2, d2 = 2, v2 = 2, |
| s3 = 3, d3 = 3, v3 = 3, |
| s4 = 4, d4 = 4, v4 = 4, |
| s5 = 5, d5 = 5, v5 = 5, |
| s6 = 6, d6 = 6, v6 = 6, |
| s7 = 7, d7 = 7, v7 = 7, |
| s8 = 8, d8 = 8, v8 = 8, |
| s9 = 9, d9 = 9, v9 = 9, |
| s10 = 10, d10 = 10, v10 = 10, |
| s11 = 11, d11 = 11, v11 = 11, |
| s12 = 12, d12 = 12, v12 = 12, |
| s13 = 13, d13 = 13, v13 = 13, |
| s14 = 14, d14 = 14, v14 = 14, |
| s15 = 15, d15 = 15, v15 = 15, |
| s16 = 16, d16 = 16, v16 = 16, |
| s17 = 17, d17 = 17, v17 = 17, |
| s18 = 18, d18 = 18, v18 = 18, |
| s19 = 19, d19 = 19, v19 = 19, |
| s20 = 20, d20 = 20, v20 = 20, |
| s21 = 21, d21 = 21, v21 = 21, |
| s22 = 22, d22 = 22, v22 = 22, |
| s23 = 23, d23 = 23, v23 = 23, |
| s24 = 24, d24 = 24, v24 = 24, |
| s25 = 25, d25 = 25, v25 = 25, |
| s26 = 26, d26 = 26, v26 = 26, |
| s27 = 27, d27 = 27, v27 = 27, |
| s28 = 28, d28 = 28, v28 = 28, |
| s29 = 29, d29 = 29, v29 = 29, |
| s30 = 30, d30 = 30, v30 = 30, |
| s31 = 31, d31 = 31, v31 = 31, // Scratch register. |
| invalid_fpreg |
| }; |
| typedef uint8_t Code; |
| typedef FPRegisterID Encoding; |
| typedef uint64_t SetType; |
| |
| static const char* GetName(Code code) { |
| static const char* const Names[] = |
| { "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", |
| "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17", "d18", "d19", |
| "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", |
| "d30", "d31", "invalid" }; |
| return Names[code]; |
| } |
| |
| static const char* GetName(uint32_t i) { |
| MOZ_ASSERT(i < TotalPhys); |
| return GetName(Code(i)); |
| } |
| |
| static Code FromName(const char* name); |
| |
| static const Code Invalid = invalid_fpreg; |
| |
| static const uint32_t Total = 64; |
| static const uint32_t TotalPhys = 32; |
| static const SetType AllMask = 0xFFFFFFFFFFFFFFFFULL; |
| static const SetType AllPhysMask = 0xFFFFFFFFULL; |
| static const SetType SpreadCoefficient = 0x100000001ULL; |
| |
| static const uint32_t Allocatable = 31; // Without d31, the scratch register. |
| |
| // d31 is the ScratchFloatReg. |
| static const SetType NonVolatileMask = |
| SetType((1 << FloatRegisters::d8) | (1 << FloatRegisters::d9) | |
| (1 << FloatRegisters::d10) | (1 << FloatRegisters::d11) | |
| (1 << FloatRegisters::d12) | (1 << FloatRegisters::d13) | |
| (1 << FloatRegisters::d14) | (1 << FloatRegisters::d15) | |
| (1 << FloatRegisters::d16) | (1 << FloatRegisters::d17) | |
| (1 << FloatRegisters::d18) | (1 << FloatRegisters::d19) | |
| (1 << FloatRegisters::d20) | (1 << FloatRegisters::d21) | |
| (1 << FloatRegisters::d22) | (1 << FloatRegisters::d23) | |
| (1 << FloatRegisters::d24) | (1 << FloatRegisters::d25) | |
| (1 << FloatRegisters::d26) | (1 << FloatRegisters::d27) | |
| (1 << FloatRegisters::d28) | (1 << FloatRegisters::d29) | |
| (1 << FloatRegisters::d30)) * SpreadCoefficient; |
| |
| static const SetType VolatileMask = AllMask & ~NonVolatileMask; |
| static const SetType AllDoubleMask = AllMask; |
| |
| static const SetType WrapperMask = VolatileMask; |
| |
| // d31 is the ScratchFloatReg. |
| static const SetType NonAllocatableMask = (SetType(1) << FloatRegisters::d31) * SpreadCoefficient; |
| |
| // Registers that can be allocated without being saved, generally. |
| static const SetType TempMask = VolatileMask & ~NonAllocatableMask; |
| |
| static const SetType AllocatableMask = AllMask & ~NonAllocatableMask; |
| union RegisterContent { |
| float s; |
| double d; |
| }; |
| enum Kind { |
| Double, |
| Single |
| }; |
| }; |
| |
| // In bytes: slots needed for potential memory->memory move spills. |
| // +8 for cycles |
| // +8 for gpr spills |
| // +8 for double spills |
| static const uint32_t ION_FRAME_SLACK_SIZE = 24; |
| |
| static const uint32_t ShadowStackSpace = 0; |
| |
| static const uint32_t ABIStackAlignment = 16; |
| static const uint32_t CodeAlignment = 16; |
| static const bool StackKeptAligned = false; |
| |
| // Although sp is only usable if 16-byte alignment is kept, |
| // the Pseudo-StackPointer enables use of 8-byte alignment. |
| static const uint32_t StackAlignment = 8; |
| static const uint32_t NativeFrameSize = 8; |
| |
| struct FloatRegister |
| { |
| typedef FloatRegisters Codes; |
| typedef Codes::Code Code; |
| typedef Codes::Encoding Encoding; |
| typedef Codes::SetType SetType; |
| |
| union RegisterContent { |
| float s; |
| double d; |
| }; |
| |
| constexpr FloatRegister(uint32_t code, FloatRegisters::Kind k) |
| : code_(FloatRegisters::Code(code & 31)), |
| k_(k) |
| { } |
| |
| explicit constexpr FloatRegister(uint32_t code) |
| : code_(FloatRegisters::Code(code & 31)), |
| k_(FloatRegisters::Kind(code >> 5)) |
| { } |
| |
| constexpr FloatRegister() |
| : code_(FloatRegisters::Code(-1)), |
| k_(FloatRegisters::Double) |
| { } |
| |
| static uint32_t SetSize(SetType x) { |
| static_assert(sizeof(SetType) == 8, "SetType must be 64 bits"); |
| x |= x >> FloatRegisters::TotalPhys; |
| x &= FloatRegisters::AllPhysMask; |
| return mozilla::CountPopulation32(x); |
| } |
| |
| static FloatRegister FromCode(uint32_t i) { |
| MOZ_ASSERT(i < FloatRegisters::Total); |
| FloatRegister r(i); |
| return r; |
| } |
| Code code() const { |
| MOZ_ASSERT((uint32_t)code_ < FloatRegisters::Total); |
| return Code(code_ | (k_ << 5)); |
| } |
| Encoding encoding() const { |
| return Encoding(code_); |
| } |
| |
| const char* name() const { |
| return FloatRegisters::GetName(code()); |
| } |
| bool volatile_() const { |
| return !!((SetType(1) << code()) & FloatRegisters::VolatileMask); |
| } |
| bool operator!=(FloatRegister other) const { |
| return other.code_ != code_ || other.k_ != k_; |
| } |
| bool operator==(FloatRegister other) const { |
| return other.code_ == code_ && other.k_ == k_; |
| } |
| bool aliases(FloatRegister other) const { |
| return other.code_ == code_; |
| } |
| uint32_t numAliased() const { |
| return 2; |
| } |
| static FloatRegisters::Kind otherkind(FloatRegisters::Kind k) { |
| if (k == FloatRegisters::Double) |
| return FloatRegisters::Single; |
| return FloatRegisters::Double; |
| } |
| void aliased(uint32_t aliasIdx, FloatRegister* ret) { |
| if (aliasIdx == 0) |
| *ret = *this; |
| else |
| *ret = FloatRegister(code_, otherkind(k_)); |
| } |
| // This function mostly exists for the ARM backend. It is to ensure that two |
| // floating point registers' types are equivalent. e.g. S0 is not equivalent |
| // to D16, since S0 holds a float32, and D16 holds a Double. |
| // Since all floating point registers on x86 and x64 are equivalent, it is |
| // reasonable for this function to do the same. |
| bool equiv(FloatRegister other) const { |
| return k_ == other.k_; |
| } |
| MOZ_CONSTEXPR uint32_t size() const { |
| return k_ == FloatRegisters::Double ? sizeof(double) : sizeof(float); |
| } |
| uint32_t numAlignedAliased() { |
| return numAliased(); |
| } |
| void alignedAliased(uint32_t aliasIdx, FloatRegister* ret) { |
| MOZ_ASSERT(aliasIdx == 0); |
| aliased(aliasIdx, ret); |
| } |
| SetType alignedOrDominatedAliasedSet() const { |
| return Codes::SpreadCoefficient << code_; |
| } |
| |
| bool isSingle() const { |
| return k_ == FloatRegisters::Single; |
| } |
| bool isDouble() const { |
| return k_ == FloatRegisters::Double; |
| } |
| bool isSimd128() const { |
| return false; |
| } |
| |
| static uint32_t FirstBit(SetType x) { |
| JS_STATIC_ASSERT(sizeof(SetType) == 8); |
| return mozilla::CountTrailingZeroes64(x); |
| } |
| static uint32_t LastBit(SetType x) { |
| JS_STATIC_ASSERT(sizeof(SetType) == 8); |
| return 63 - mozilla::CountLeadingZeroes64(x); |
| } |
| |
| static TypedRegisterSet<FloatRegister> ReduceSetForPush(const TypedRegisterSet<FloatRegister>& s); |
| static uint32_t GetSizeInBytes(const TypedRegisterSet<FloatRegister>& s); |
| static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister>& s); |
| uint32_t getRegisterDumpOffsetInBytes(); |
| |
| public: |
| Code code_ : 8; |
| FloatRegisters::Kind k_ : 1; |
| }; |
| |
| // ARM/D32 has double registers that cannot be treated as float32. |
| // Luckily, ARMv8 doesn't have the same misfortune. |
| inline bool |
| hasUnaliasedDouble() |
| { |
| return false; |
| } |
| |
| // ARM prior to ARMv8 also has doubles that alias multiple floats. |
| // Again, ARMv8 is in the clear. |
| inline bool |
| hasMultiAlias() |
| { |
| return false; |
| } |
| |
| static const size_t AsmJSCheckedImmediateRange = 0; |
| static const size_t AsmJSImmediateRange = 0; |
| |
| } // namespace jit |
| } // namespace js |
| |
| #endif // jit_arm64_Architecture_arm64_h |