blob: 12b0a1242a0fb33a017273be22f4264e556e7272 [file] [log] [blame]
/* -*- 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_arm_Architecture_arm_h
#define jit_arm_Architecture_arm_h
#include "mozilla/MathAlgorithms.h"
#include <limits.h>
#include <stdint.h>
#include "js/Utility.h"
// GCC versions 4.6 and above define __ARM_PCS_VFP to denote a hard-float
// ABI target. The iOS toolchain doesn't define anything specific here,
// but iOS always supports VFP.
#if defined(__ARM_PCS_VFP) || defined(XP_IOS)
#define JS_CODEGEN_ARM_HARDFP
#endif
namespace js {
namespace jit {
// In bytes: slots needed for potential memory->memory move spills.
// +8 for cycles
// +4 for gpr spills
// +8 for double spills
static const uint32_t ION_FRAME_SLACK_SIZE = 20;
// These offsets are specific to nunboxing, and capture offsets into the
// components of a js::Value.
static const int32_t NUNBOX32_TYPE_OFFSET = 4;
static const int32_t NUNBOX32_PAYLOAD_OFFSET = 0;
static const uint32_t ShadowStackSpace = 0;
////
// These offsets are related to bailouts.
////
// Size of each bailout table entry. On arm, this is presently a single call
// (which is wrong!). The call clobbers lr.
// For now, I've dealt with this by ensuring that we never allocate to lr. It
// should probably be 8 bytes, a mov of an immediate into r12 (not allocated
// presently, or ever) followed by a branch to the apropriate code.
static const uint32_t BAILOUT_TABLE_ENTRY_SIZE = 4;
class Registers
{
public:
enum RegisterID {
r0 = 0,
r1,
r2,
r3,
S0 = r3,
r4,
r5,
r6,
r7,
r8,
S1 = r8,
r9,
r10,
r11,
r12,
ip = r12,
r13,
sp = r13,
r14,
lr = r14,
r15,
pc = r15,
invalid_reg
};
typedef uint8_t Code;
typedef RegisterID Encoding;
// Content spilled during bailouts.
union RegisterContent {
uintptr_t r;
};
static const char* GetName(Code code) {
MOZ_ASSERT(code < Total);
static const char * const Names[] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "sp", "r14", "pc"};
return Names[code];
}
static const char* GetName(Encoding i) {
return GetName(Code(i));
}
static Code FromName(const char* name);
static const Encoding StackPointer = sp;
static const Encoding Invalid = invalid_reg;
static const uint32_t Total = 16;
static const uint32_t Allocatable = 13;
typedef uint32_t SetType;
static const SetType AllMask = (1 << Total) - 1;
static const SetType ArgRegMask = (1 << r0) | (1 << r1) | (1 << r2) | (1 << r3);
static const SetType VolatileMask =
(1 << r0) |
(1 << r1) |
(1 << Registers::r2) |
(1 << Registers::r3)
#if defined(XP_IOS)
// per https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARMv6FunctionCallingConventions.html#//apple_ref/doc/uid/TP40009021-SW4
| (1 << Registers::r9)
#endif
;
static const SetType NonVolatileMask =
(1 << Registers::r4) |
(1 << Registers::r5) |
(1 << Registers::r6) |
(1 << Registers::r7) |
(1 << Registers::r8) |
#if !defined(XP_IOS)
(1 << Registers::r9) |
#endif
(1 << Registers::r10) |
(1 << Registers::r11) |
(1 << Registers::r12) |
(1 << Registers::r14);
static const SetType WrapperMask =
VolatileMask | // = arguments
(1 << Registers::r4) | // = outReg
(1 << Registers::r5); // = argBase
static const SetType SingleByteRegs =
VolatileMask | NonVolatileMask;
static const SetType NonAllocatableMask =
(1 << Registers::sp) |
(1 << Registers::r12) | // r12 = ip = scratch
(1 << Registers::lr) |
(1 << Registers::pc);
// Registers that can be allocated without being saved, generally.
static const SetType TempMask = VolatileMask & ~NonAllocatableMask;
// Registers returned from a JS -> JS call.
static const SetType JSCallMask =
(1 << Registers::r2) |
(1 << Registers::r3);
// Registers returned from a JS -> C call.
static const SetType CallMask =
(1 << Registers::r0) |
(1 << Registers::r1); // Used for double-size returns.
static const SetType AllocatableMask = AllMask & ~NonAllocatableMask;
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);
}
};
// Smallest integer type that can hold a register bitmask.
typedef uint16_t PackedRegisterMask;
typedef uint16_t PackedRegisterMask;
class FloatRegisters
{
public:
enum FPRegisterID {
s0,
s1,
s2,
s3,
s4,
s5,
s6,
s7,
s8,
s9,
s10,
s11,
s12,
s13,
s14,
s15,
s16,
s17,
s18,
s19,
s20,
s21,
s22,
s23,
s24,
s25,
s26,
s27,
s28,
s29,
s30,
s31,
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_freg
};
typedef uint32_t Code;
typedef FPRegisterID Encoding;
// Content spilled during bailouts.
union RegisterContent {
double d;
};
static const char* GetDoubleName(Encoding 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"};
return Names[code];
}
static const char* GetSingleName(Encoding code) {
static const char * const Names[] = { "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
"s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15",
"s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23",
"s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31"};
return Names[code];
}
static const char* GetName(uint32_t i) {
MOZ_ASSERT(i < Total);
return GetName(Encoding(i));
}
static Code FromName(const char* name);
static const Encoding Invalid = invalid_freg;
static const uint32_t Total = 48;
static const uint32_t TotalDouble = 16;
static const uint32_t TotalSingle = 32;
static const uint32_t Allocatable = 45;
// There are only 32 places that we can put values.
static const uint32_t TotalPhys = 32;
static uint32_t ActualTotalPhys();
typedef uint64_t SetType;
static const SetType AllDoubleMask = ((1ull << 16) - 1) << 32;
static const SetType AllMask = ((1ull << 48) - 1);
// d15 is the ScratchFloatReg.
static const SetType NonVolatileDoubleMask =
((1ULL << d8) |
(1ULL << d9) |
(1ULL << d10) |
(1ULL << d11) |
(1ULL << d12) |
(1ULL << d13) |
(1ULL << d14));
// s30 and s31 alias d15.
static const SetType NonVolatileMask =
(NonVolatileDoubleMask |
((1 << s16) |
(1 << s17) |
(1 << s18) |
(1 << s19) |
(1 << s20) |
(1 << s21) |
(1 << s22) |
(1 << s23) |
(1 << s24) |
(1 << s25) |
(1 << s26) |
(1 << s27) |
(1 << s28) |
(1 << s29) |
(1 << s30)));
static const SetType VolatileMask = AllMask & ~NonVolatileMask;
static const SetType VolatileDoubleMask = AllDoubleMask & ~NonVolatileDoubleMask;
static const SetType WrapperMask = VolatileMask;
// d15 is the ARM scratch float register.
// s30 and s31 alias d15.
static const SetType NonAllocatableMask = ((1ULL << d15)) |
(1ULL << s30) |
(1ULL << s31);
// Registers that can be allocated without being saved, generally.
static const SetType TempMask = VolatileMask & ~NonAllocatableMask;
static const SetType AllocatableMask = AllMask & ~NonAllocatableMask;
};
template <typename T>
class TypedRegisterSet;
class VFPRegister
{
public:
// What type of data is being stored in this register? UInt / Int are
// specifically for vcvt, where we need to know how the data is supposed to
// be converted.
enum RegType {
Single = 0x0,
Double = 0x1,
UInt = 0x2,
Int = 0x3
};
typedef FloatRegisters Codes;
typedef Codes::Code Code;
typedef Codes::Encoding Encoding;
protected:
RegType kind : 2;
// ARM doesn't have more than 32 registers. Don't take more bits than we'll
// need. Presently, we don't have plans to address the upper and lower
// halves of the double registers seprately, so 5 bits should suffice. If we
// do decide to address them seprately (vmov, I'm looking at you), we will
// likely specify it as a separate field.
public:
uint32_t code_ : 5;
protected:
bool _isInvalid : 1;
bool _isMissing : 1;
public:
MOZ_CONSTEXPR VFPRegister(uint32_t r, RegType k)
: kind(k), code_ (Code(r)), _isInvalid(false), _isMissing(false)
{ }
MOZ_CONSTEXPR VFPRegister()
: kind(Double), code_(Code(0)), _isInvalid(true), _isMissing(false)
{ }
MOZ_CONSTEXPR VFPRegister(RegType k, uint32_t id, bool invalid, bool missing) :
kind(k), code_(Code(id)), _isInvalid(invalid), _isMissing(missing) {
}
explicit MOZ_CONSTEXPR VFPRegister(Code id)
: kind(Double), code_(id), _isInvalid(false), _isMissing(false)
{ }
bool operator==(const VFPRegister& other) const {
MOZ_ASSERT(!isInvalid());
MOZ_ASSERT(!other.isInvalid());
return kind == other.kind && code_ == other.code_;
}
bool isSingle() const { return kind == Single; }
bool isDouble() const { return kind == Double; }
bool isSimd128() const { return false; }
bool isFloat() const { return (kind == Double) || (kind == Single); }
bool isInt() const { return (kind == UInt) || (kind == Int); }
bool isSInt() const { return kind == Int; }
bool isUInt() const { return kind == UInt; }
bool equiv(const VFPRegister& other) const { return other.kind == kind; }
size_t size() const { return (kind == Double) ? 8 : 4; }
bool isInvalid() const;
bool isMissing() const;
VFPRegister doubleOverlay(unsigned int which = 0) const;
VFPRegister singleOverlay(unsigned int which = 0) const;
VFPRegister sintOverlay(unsigned int which = 0) const;
VFPRegister uintOverlay(unsigned int which = 0) const;
VFPRegister asSingle() const { return singleOverlay(); }
VFPRegister asDouble() const { return doubleOverlay(); }
VFPRegister asSimd128() const { MOZ_CRASH("NYI"); }
struct VFPRegIndexSplit;
VFPRegIndexSplit encode();
// For serializing values.
struct VFPRegIndexSplit {
const uint32_t block : 4;
const uint32_t bit : 1;
private:
friend VFPRegIndexSplit js::jit::VFPRegister::encode();
VFPRegIndexSplit(uint32_t block_, uint32_t bit_)
: block(block_), bit(bit_)
{
MOZ_ASSERT(block == block_);
MOZ_ASSERT(bit == bit_);
}
};
Code code() const {
MOZ_ASSERT(!_isInvalid && !_isMissing);
// This should only be used in areas where we only have doubles and
// singles.
MOZ_ASSERT(isFloat());
return Code(code_ | (kind << 5));
}
Encoding encoding() const {
MOZ_ASSERT(!_isInvalid && !_isMissing);
return Encoding(code_);
}
uint32_t id() const {
return code_;
}
static VFPRegister FromCode(uint32_t i) {
uint32_t code = i & 31;
uint32_t kind = i >> 5;
return VFPRegister(code, RegType(kind));
}
bool volatile_() const {
if (isDouble())
return !!((1 << (code_ >> 1)) & FloatRegisters::VolatileMask);
return !!((1 << code_) & FloatRegisters::VolatileMask);
}
const char* name() const {
if (isDouble())
return FloatRegisters::GetDoubleName(Encoding(code_));
return FloatRegisters::GetSingleName(Encoding(code_));
}
bool operator != (const VFPRegister& other) const {
return other.kind != kind || code_ != other.code_;
}
bool aliases(const VFPRegister& other) {
if (kind == other.kind)
return code_ == other.code_;
return doubleOverlay() == other.doubleOverlay();
}
static const int NumAliasedDoubles = 16;
uint32_t numAliased() const {
if (isDouble()) {
if (code_ < NumAliasedDoubles)
return 3;
return 1;
}
return 2;
}
// N.B. FloatRegister is an explicit outparam here because msvc-2010
// miscompiled it on win64 when the value was simply returned
void aliased(uint32_t aliasIdx, VFPRegister* ret) {
if (aliasIdx == 0) {
*ret = *this;
return;
}
if (isDouble()) {
MOZ_ASSERT(code_ < NumAliasedDoubles);
MOZ_ASSERT(aliasIdx <= 2);
*ret = singleOverlay(aliasIdx - 1);
return;
}
MOZ_ASSERT(aliasIdx == 1);
*ret = doubleOverlay(aliasIdx - 1);
}
uint32_t numAlignedAliased() const {
if (isDouble()) {
if (code_ < NumAliasedDoubles)
return 2;
return 1;
}
// s1 has 0 other aligned aliases, 1 total.
// s0 has 1 other aligned aliase, 2 total.
return 2 - (code_ & 1);
}
// | d0 |
// | s0 | s1 |
// If we've stored s0 and s1 in memory, we also want to say that d0 is
// stored there, but it is only stored at the location where it is aligned
// e.g. at s0, not s1.
void alignedAliased(uint32_t aliasIdx, VFPRegister* ret) {
if (aliasIdx == 0) {
*ret = *this;
return;
}
MOZ_ASSERT(aliasIdx == 1);
if (isDouble()) {
MOZ_ASSERT(code_ < NumAliasedDoubles);
*ret = singleOverlay(aliasIdx - 1);
return;
}
MOZ_ASSERT((code_ & 1) == 0);
*ret = doubleOverlay(aliasIdx - 1);
return;
}
typedef FloatRegisters::SetType SetType;
// This function is used to ensure that Register set can take all Single
// registers, even if we are taking a mix of either double or single
// registers.
//
// s0.alignedOrDominatedAliasedSet() == s0 | d0.
// s1.alignedOrDominatedAliasedSet() == s1.
// d0.alignedOrDominatedAliasedSet() == s0 | s1 | d0.
//
// This way the Allocator register set does not have to do any arithmetics
// to know if a register is available or not, as we have the following
// relations:
//
// d0.alignedOrDominatedAliasedSet() ==
// s0.alignedOrDominatedAliasedSet() | s1.alignedOrDominatedAliasedSet()
//
// s0.alignedOrDominatedAliasedSet() & s1.alignedOrDominatedAliasedSet() == 0
//
SetType alignedOrDominatedAliasedSet() const {
if (isSingle()) {
if (code_ % 2 != 0)
return SetType(1) << code_;
return (SetType(1) << code_) | (SetType(1) << (32 + code_ / 2));
}
MOZ_ASSERT(isDouble());
return (SetType(0b11) << (code_ * 2)) | (SetType(1) << (32 + code_));
}
static uint32_t SetSize(SetType x) {
static_assert(sizeof(SetType) == 8, "SetType must be 64 bits");
return mozilla::CountPopulation32(x);
}
static Code FromName(const char* name) {
return FloatRegisters::FromName(name);
}
static TypedRegisterSet<VFPRegister> ReduceSetForPush(const TypedRegisterSet<VFPRegister>& s);
static uint32_t GetPushSizeInBytes(const TypedRegisterSet<VFPRegister>& s);
uint32_t getRegisterDumpOffsetInBytes();
static uint32_t FirstBit(SetType x) {
return mozilla::CountTrailingZeroes64(x);
}
static uint32_t LastBit(SetType x) {
return 63 - mozilla::CountLeadingZeroes64(x);
}
};
// The only floating point register set that we work with are the VFP Registers.
typedef VFPRegister FloatRegister;
uint32_t GetARMFlags();
bool HasMOVWT();
bool HasLDSTREXBHD(); // {LD,ST}REX{B,H,D}
bool HasDMBDSBISB(); // DMB, DSB, and ISB
bool HasVFPv3();
bool HasVFP();
bool Has32DP();
bool HasIDIV();
extern volatile uint32_t armHwCapFlags;
// Not part of the HWCAP flag, but we need to know these and these bits are not
// used. Define these here so that their use can be inlined by the simulator.
// A bit to flag when the flags are uninitialized, so they can be atomically set.
#define HWCAP_UNINITIALIZED (1 << 25)
// A bit to flag when alignment faults are enabled and signal.
#define HWCAP_ALIGNMENT_FAULT (1 << 26)
// A bit to flag the use of the hardfp ABI.
#define HWCAP_USE_HARDFP_ABI (1 << 27)
// A bit to flag the use of the ARMv7 arch, otherwise ARMv6.
#define HWCAP_ARMv7 (1 << 28)
// Returns true when cpu alignment faults are enabled and signaled, and thus we
// should ensure loads and stores are aligned.
inline bool HasAlignmentFault()
{
MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED);
return armHwCapFlags & HWCAP_ALIGNMENT_FAULT;
}
// Arm/D32 has double registers that can NOT be treated as float32 and this
// requires some dances in lowering.
inline bool
hasUnaliasedDouble()
{
return Has32DP();
}
// On ARM, Dn aliases both S2n and S2n+1, so if you need to convert a float32 to
// a double as a temporary, you need a temporary double register.
inline bool
hasMultiAlias()
{
return true;
}
bool ParseARMHwCapFlags(const char* armHwCap);
void InitARMFlags();
uint32_t GetARMFlags();
// If the simulator is used then the ABI choice is dynamic. Otherwise the ABI is
// static and useHardFpABI is inlined so that unused branches can be optimized
// away.
#ifdef JS_SIMULATOR_ARM
bool UseHardFpABI();
#else
static inline bool UseHardFpABI()
{
#if defined(JS_CODEGEN_ARM_HARDFP)
return true;
#else
return false;
#endif
}
#endif
// In order to handle SoftFp ABI calls, we need to be able to express that we
// have ABIArg which are represented by pair of general purpose registers.
#define JS_CODEGEN_REGISTER_PAIR 1
// See the comments above AsmJSMappedSize in AsmJSValidate.h for more info.
// TODO: Implement this for ARM. Note that it requires Codegen to respect the
// offset field of AsmJSHeapAccess.
static const size_t AsmJSCheckedImmediateRange = 0;
static const size_t AsmJSImmediateRange = 0;
} // namespace jit
} // namespace js
#endif /* jit_arm_Architecture_arm_h */