summaryrefslogtreecommitdiffstats
path: root/js/src/jit/arm64/Architecture-arm64.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/arm64/Architecture-arm64.h')
-rw-r--r--js/src/jit/arm64/Architecture-arm64.h462
1 files changed, 462 insertions, 0 deletions
diff --git a/js/src/jit/arm64/Architecture-arm64.h b/js/src/jit/arm64/Architecture-arm64.h
new file mode 100644
index 000000000..e74340f13
--- /dev/null
+++ b/js/src/jit/arm64/Architecture-arm64.h
@@ -0,0 +1,462 @@
+/* -*- 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 AllSingleMask = 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;
+
+// TODO:
+// This constant needs to be updated to account for whatever near/far branching
+// strategy is used by ARM64.
+static const uint32_t JumpImmediateRange = UINT32_MAX;
+
+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_;
+ }
+ 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;
+}
+
+} // namespace jit
+} // namespace js
+
+#endif // jit_arm64_Architecture_arm64_h