tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Architecture-x86-shared.h (14953B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef jit_x86_shared_Architecture_x86_h
      8 #define jit_x86_shared_Architecture_x86_h
      9 
     10 #if !defined(JS_CODEGEN_X86) && !defined(JS_CODEGEN_X64)
     11 #  error "Unsupported architecture!"
     12 #endif
     13 
     14 #include "mozilla/MathAlgorithms.h"
     15 
     16 #include <algorithm>
     17 #include <string.h>
     18 
     19 #include "jit/shared/Architecture-shared.h"
     20 
     21 #include "jit/x86-shared/Constants-x86-shared.h"
     22 
     23 namespace js {
     24 namespace jit {
     25 
     26 #if defined(JS_CODEGEN_X86)
     27 // These offsets are specific to nunboxing, and capture offsets into the
     28 // components of a js::Value.
     29 static const int32_t NUNBOX32_TYPE_OFFSET = 4;
     30 static const int32_t NUNBOX32_PAYLOAD_OFFSET = 0;
     31 #endif
     32 
     33 #if defined(JS_CODEGEN_X64) && defined(_WIN64)
     34 static constexpr uint32_t ShadowStackSpace = 32;
     35 #else
     36 static constexpr uint32_t ShadowStackSpace = 0;
     37 #endif
     38 
     39 static const uint32_t JumpImmediateRange = INT32_MAX;
     40 
     41 class Registers {
     42 public:
     43  using Code = uint8_t;
     44  using Encoding = X86Encoding::RegisterID;
     45 
     46  // Content spilled during bailouts.
     47  union RegisterContent {
     48    uintptr_t r;
     49  };
     50 
     51 #if defined(JS_CODEGEN_X86)
     52  using SetType = uint8_t;
     53 
     54  static const char* GetName(Code code) {
     55    return X86Encoding::GPRegName(Encoding(code));
     56  }
     57 
     58  static const uint32_t Total = 8;
     59  static const uint32_t TotalPhys = 8;
     60  static const uint32_t Allocatable = 7;
     61 
     62 #elif defined(JS_CODEGEN_X64)
     63  using SetType = uint16_t;
     64 
     65  static const char* GetName(Code code) {
     66    static const char* const Names[] = {
     67        "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
     68        "r8",  "r9",  "r10", "r11", "r12", "r13", "r14", "r15"};
     69    return Names[code];
     70  }
     71 
     72  static const uint32_t Total = 16;
     73  static const uint32_t TotalPhys = 16;
     74  static const uint32_t Allocatable = 14;
     75 #endif
     76 
     77  static uint32_t SetSize(SetType x) {
     78    static_assert(sizeof(SetType) <= 4, "SetType must be, at most, 32 bits");
     79    return mozilla::CountPopulation32(x);
     80  }
     81  static uint32_t FirstBit(SetType x) {
     82    return mozilla::CountTrailingZeroes32(x);
     83  }
     84  static uint32_t LastBit(SetType x) {
     85    return 31 - mozilla::CountLeadingZeroes32(x);
     86  }
     87 
     88  static Code FromName(const char* name) {
     89    for (size_t i = 0; i < Total; i++) {
     90      if (strcmp(GetName(Code(i)), name) == 0) {
     91        return Code(i);
     92      }
     93    }
     94    return Invalid;
     95  }
     96 
     97  static const Encoding StackPointer = X86Encoding::rsp;
     98  static const Encoding Invalid = X86Encoding::invalid_reg;
     99 
    100  static const SetType AllMask = (1 << Total) - 1;
    101 
    102 #if defined(JS_CODEGEN_X86)
    103  static const SetType ArgRegMask = 0;
    104 
    105  static const SetType VolatileMask = (1 << X86Encoding::rax) |
    106                                      (1 << X86Encoding::rcx) |
    107                                      (1 << X86Encoding::rdx);
    108 
    109  static const SetType WrapperMask = VolatileMask | (1 << X86Encoding::rbx);
    110 
    111  static const SetType SingleByteRegs =
    112      (1 << X86Encoding::rax) | (1 << X86Encoding::rcx) |
    113      (1 << X86Encoding::rdx) | (1 << X86Encoding::rbx);
    114 
    115  static const SetType NonAllocatableMask =
    116      (1 << X86Encoding::rsp) | (1 << X86Encoding::rbp);
    117 
    118  // Registers returned from a JS -> JS call.
    119  static const SetType JSCallMask =
    120      (1 << X86Encoding::rcx) | (1 << X86Encoding::rdx);
    121 
    122  // Registers returned from a JS -> C call.
    123  static const SetType CallMask = (1 << X86Encoding::rax);
    124 
    125 #elif defined(JS_CODEGEN_X64)
    126  static const SetType ArgRegMask =
    127 #  if !defined(_WIN64)
    128      (1 << X86Encoding::rdi) | (1 << X86Encoding::rsi) |
    129 #  endif
    130      (1 << X86Encoding::rdx) | (1 << X86Encoding::rcx) |
    131      (1 << X86Encoding::r8) | (1 << X86Encoding::r9);
    132 
    133  static const SetType VolatileMask = (1 << X86Encoding::rax) | ArgRegMask |
    134                                      (1 << X86Encoding::r10) |
    135                                      (1 << X86Encoding::r11);
    136 
    137  static const SetType WrapperMask = VolatileMask;
    138 
    139  static const SetType SingleByteRegs = AllMask & ~(1 << X86Encoding::rsp);
    140 
    141  static const SetType NonAllocatableMask =
    142      (1 << X86Encoding::rsp) | (1 << X86Encoding::rbp) |
    143      (1 << X86Encoding::r11);  // This is ScratchReg.
    144 
    145  // Registers returned from a JS -> JS call.
    146  static const SetType JSCallMask = (1 << X86Encoding::rcx);
    147 
    148  // Registers returned from a JS -> C call.
    149  static const SetType CallMask = (1 << X86Encoding::rax);
    150 
    151 #endif
    152 
    153  static const SetType NonVolatileMask =
    154      AllMask & ~VolatileMask & ~(1 << X86Encoding::rsp);
    155 
    156  static const SetType AllocatableMask = AllMask & ~NonAllocatableMask;
    157 };
    158 
    159 using PackedRegisterMask = Registers::SetType;
    160 
    161 class FloatRegisters {
    162 public:
    163  using Encoding = X86Encoding::XMMRegisterID;
    164 
    165  // Observe that there is a Simd128 type on both x86 and x64 whether SIMD is
    166  // implemented/enabled or not, and that the RegisterContent union is large
    167  // enough for a V128 datum always.  Producers and consumers of a register dump
    168  // must be aware of this even if they don't need to save/restore values in the
    169  // high lanes of the SIMD registers.  See the DumpAllRegs() implementations,
    170  // for example.
    171 
    172  enum ContentType {
    173    Single,   // 32-bit float.
    174    Double,   // 64-bit double.
    175    Simd128,  // 128-bit Wasm SIMD type.
    176    NumTypes
    177  };
    178 
    179  // Content spilled during bailouts.
    180  union RegisterContent {
    181    float s;
    182    double d;
    183    uint8_t v128[16];
    184  };
    185 
    186  static const char* GetName(Encoding code) {
    187    return X86Encoding::XMMRegName(code);
    188  }
    189 
    190  static Encoding FromName(const char* name) {
    191    for (size_t i = 0; i < Total; i++) {
    192      if (strcmp(GetName(Encoding(i)), name) == 0) {
    193        return Encoding(i);
    194      }
    195    }
    196    return Invalid;
    197  }
    198 
    199  static const Encoding Invalid = X86Encoding::invalid_xmm;
    200 
    201 #if defined(JS_CODEGEN_X86)
    202  static const uint32_t Total = 8 * NumTypes;
    203  static const uint32_t TotalPhys = 8;
    204  static const uint32_t Allocatable = 7;
    205  using SetType = uint32_t;
    206 #elif defined(JS_CODEGEN_X64)
    207  static const uint32_t Total = 16 * NumTypes;
    208  static const uint32_t TotalPhys = 16;
    209  static const uint32_t Allocatable = 15;
    210  using SetType = uint64_t;
    211 #endif
    212 
    213  static_assert(sizeof(SetType) * 8 >= Total,
    214                "SetType should be large enough to enumerate all registers.");
    215 
    216  // Magic values which are used to duplicate a mask of physical register for
    217  // a specific type of register. A multiplication is used to copy and shift
    218  // the bits of the physical register mask.
    219  static const SetType SpreadSingle = SetType(1)
    220                                      << (uint32_t(Single) * TotalPhys);
    221  static const SetType SpreadDouble = SetType(1)
    222                                      << (uint32_t(Double) * TotalPhys);
    223  static const SetType SpreadSimd128 = SetType(1)
    224                                       << (uint32_t(Simd128) * TotalPhys);
    225  static const SetType SpreadScalar = SpreadSingle | SpreadDouble;
    226  static const SetType SpreadVector = SpreadSimd128;
    227  static const SetType Spread = SpreadScalar | SpreadVector;
    228 
    229  static const SetType AllPhysMask = ((1 << TotalPhys) - 1);
    230  static const SetType AllMask = AllPhysMask * Spread;
    231  static const SetType AllDoubleMask = AllPhysMask * SpreadDouble;
    232  static const SetType AllSingleMask = AllPhysMask * SpreadSingle;
    233  static const SetType AllVector128Mask = AllPhysMask * SpreadSimd128;
    234 
    235 #if defined(JS_CODEGEN_X86)
    236  static const SetType NonAllocatableMask =
    237      Spread * (1 << X86Encoding::xmm7);  // This is ScratchDoubleReg.
    238 
    239 #elif defined(JS_CODEGEN_X64)
    240  static const SetType NonAllocatableMask =
    241      Spread * (1 << X86Encoding::xmm15);  // This is ScratchDoubleReg.
    242 #endif
    243 
    244 #if defined(JS_CODEGEN_X64) && defined(_WIN64)
    245  static const SetType VolatileMask =
    246      ((1 << X86Encoding::xmm0) | (1 << X86Encoding::xmm1) |
    247       (1 << X86Encoding::xmm2) | (1 << X86Encoding::xmm3) |
    248       (1 << X86Encoding::xmm4) | (1 << X86Encoding::xmm5)) *
    249      Spread;
    250 #else
    251  static const SetType VolatileMask = AllMask;
    252 #endif
    253 
    254  static const SetType NonVolatileMask = AllMask & ~VolatileMask;
    255  static const SetType WrapperMask = VolatileMask;
    256  static const SetType AllocatableMask = AllMask & ~NonAllocatableMask;
    257 };
    258 
    259 static const uint32_t SpillSlotSize =
    260    std::max(sizeof(Registers::RegisterContent),
    261             sizeof(FloatRegisters::RegisterContent));
    262 
    263 template <typename T>
    264 class TypedRegisterSet;
    265 
    266 struct FloatRegister {
    267  using Codes = FloatRegisters;
    268  using Code = size_t;
    269  using Encoding = Codes::Encoding;
    270  using SetType = Codes::SetType;
    271  static uint32_t SetSize(SetType x) {
    272    // Count the number of non-aliased registers, for the moment.
    273    //
    274    // Copy the set bits of each typed register to the low part of the of
    275    // the Set, and count the number of registers. This is made to avoid
    276    // registers which are allocated twice with different types (such as in
    277    // AllMask).
    278    x |= x >> (2 * Codes::TotalPhys);
    279    x |= x >> Codes::TotalPhys;
    280    x &= Codes::AllPhysMask;
    281    static_assert(Codes::AllPhysMask <= 0xffff,
    282                  "We can safely use CountPopulation32");
    283    return mozilla::CountPopulation32(x);
    284  }
    285 
    286 #if defined(JS_CODEGEN_X86)
    287  static uint32_t FirstBit(SetType x) {
    288    static_assert(sizeof(SetType) == 4, "SetType must be 32 bits");
    289    return mozilla::CountTrailingZeroes32(x);
    290  }
    291  static uint32_t LastBit(SetType x) {
    292    return 31 - mozilla::CountLeadingZeroes32(x);
    293  }
    294 
    295 #elif defined(JS_CODEGEN_X64)
    296  static uint32_t FirstBit(SetType x) {
    297    static_assert(sizeof(SetType) == 8, "SetType must be 64 bits");
    298    return mozilla::CountTrailingZeroes64(x);
    299  }
    300  static uint32_t LastBit(SetType x) {
    301    return 63 - mozilla::CountLeadingZeroes64(x);
    302  }
    303 #endif
    304 
    305 private:
    306  // Note: These fields are using one extra bit to make the invalid enumerated
    307  // values fit, and thus prevent a warning.
    308  Codes::Encoding reg_ : 5;
    309  Codes::ContentType type_ : 3;
    310  bool isInvalid_ : 1;
    311 
    312  // Constants used for exporting/importing the float register code.
    313 #if defined(JS_CODEGEN_X86)
    314  static const size_t RegSize = 3;
    315 #elif defined(JS_CODEGEN_X64)
    316  static const size_t RegSize = 4;
    317 #endif
    318  static const size_t RegMask = (1 << RegSize) - 1;
    319 
    320 public:
    321  constexpr FloatRegister()
    322      : reg_(Codes::Encoding(0)), type_(Codes::Single), isInvalid_(true) {}
    323  constexpr FloatRegister(uint32_t r, Codes::ContentType k)
    324      : reg_(Codes::Encoding(r)), type_(k), isInvalid_(false) {}
    325  constexpr FloatRegister(Codes::Encoding r, Codes::ContentType k)
    326      : reg_(r), type_(k), isInvalid_(false) {}
    327 
    328  static FloatRegister FromCode(uint32_t i) {
    329    MOZ_ASSERT(i < Codes::Total);
    330    return FloatRegister(i & RegMask, Codes::ContentType(i >> RegSize));
    331  }
    332 
    333  bool isSingle() const {
    334    MOZ_ASSERT(!isInvalid());
    335    return type_ == Codes::Single;
    336  }
    337  bool isDouble() const {
    338    MOZ_ASSERT(!isInvalid());
    339    return type_ == Codes::Double;
    340  }
    341  bool isSimd128() const {
    342    MOZ_ASSERT(!isInvalid());
    343    return type_ == Codes::Simd128;
    344  }
    345  bool isInvalid() const { return isInvalid_; }
    346 
    347  FloatRegister asSingle() const {
    348    MOZ_ASSERT(!isInvalid());
    349    return FloatRegister(reg_, Codes::Single);
    350  }
    351  FloatRegister asDouble() const {
    352    MOZ_ASSERT(!isInvalid());
    353    return FloatRegister(reg_, Codes::Double);
    354  }
    355  FloatRegister asSimd128() const {
    356    MOZ_ASSERT(!isInvalid());
    357    return FloatRegister(reg_, Codes::Simd128);
    358  }
    359 
    360  uint32_t size() const {
    361    MOZ_ASSERT(!isInvalid());
    362    if (isSingle()) {
    363      return sizeof(float);
    364    }
    365    if (isDouble()) {
    366      return sizeof(double);
    367    }
    368    MOZ_ASSERT(isSimd128());
    369    return 4 * sizeof(int32_t);
    370  }
    371 
    372  Code code() const {
    373    MOZ_ASSERT(!isInvalid());
    374    MOZ_ASSERT(uint32_t(reg_) < Codes::TotalPhys);
    375    // :TODO: ARM is doing the same thing, but we should avoid this, except
    376    // that the RegisterSets depends on this.
    377    return Code(reg_ | (type_ << RegSize));
    378  }
    379  Encoding encoding() const {
    380    MOZ_ASSERT(!isInvalid());
    381    MOZ_ASSERT(uint32_t(reg_) < Codes::TotalPhys);
    382    return reg_;
    383  }
    384  // defined in Assembler-x86-shared.cpp
    385  const char* name() const;
    386  bool volatile_() const {
    387    return !!((SetType(1) << code()) & FloatRegisters::VolatileMask);
    388  }
    389  bool operator!=(FloatRegister other) const {
    390    return other.reg_ != reg_ || other.type_ != type_;
    391  }
    392  bool operator==(FloatRegister other) const {
    393    return other.reg_ == reg_ && other.type_ == type_;
    394  }
    395  bool aliases(FloatRegister other) const { return other.reg_ == reg_; }
    396  // Check if two floating point registers have the same type.
    397  bool equiv(FloatRegister other) const { return other.type_ == type_; }
    398 
    399  uint32_t numAliased() const { return Codes::NumTypes; }
    400  uint32_t numAlignedAliased() const { return numAliased(); }
    401 
    402  FloatRegister aliased(uint32_t aliasIdx) const {
    403    MOZ_ASSERT(aliasIdx < Codes::NumTypes);
    404    return FloatRegister(
    405        reg_, Codes::ContentType((aliasIdx + type_) % Codes::NumTypes));
    406  }
    407  FloatRegister alignedAliased(uint32_t aliasIdx) const {
    408    return aliased(aliasIdx);
    409  }
    410 
    411  SetType alignedOrDominatedAliasedSet() const { return Codes::Spread << reg_; }
    412 
    413  static constexpr RegTypeName DefaultType = RegTypeName::Float64;
    414 
    415  template <RegTypeName = DefaultType>
    416  static SetType LiveAsIndexableSet(SetType s) {
    417    return SetType(0);
    418  }
    419 
    420  template <RegTypeName Name = DefaultType>
    421  static SetType AllocatableAsIndexableSet(SetType s) {
    422    static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable");
    423    return LiveAsIndexableSet<Name>(s);
    424  }
    425 
    426  static TypedRegisterSet<FloatRegister> ReduceSetForPush(
    427      const TypedRegisterSet<FloatRegister>& s);
    428  static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister>& s);
    429  uint32_t getRegisterDumpOffsetInBytes();
    430 };
    431 
    432 template <>
    433 inline FloatRegister::SetType
    434 FloatRegister::LiveAsIndexableSet<RegTypeName::Float32>(SetType set) {
    435  return set & FloatRegisters::AllSingleMask;
    436 }
    437 
    438 template <>
    439 inline FloatRegister::SetType
    440 FloatRegister::LiveAsIndexableSet<RegTypeName::Float64>(SetType set) {
    441  return set & FloatRegisters::AllDoubleMask;
    442 }
    443 
    444 template <>
    445 inline FloatRegister::SetType
    446 FloatRegister::LiveAsIndexableSet<RegTypeName::Vector128>(SetType set) {
    447  return set & FloatRegisters::AllVector128Mask;
    448 }
    449 
    450 template <>
    451 inline FloatRegister::SetType
    452 FloatRegister::LiveAsIndexableSet<RegTypeName::Any>(SetType set) {
    453  return set;
    454 }
    455 
    456 // Arm/D32 has double registers that can NOT be treated as float32
    457 // and this requires some dances in lowering.
    458 inline bool hasUnaliasedDouble() { return false; }
    459 
    460 // On ARM, Dn aliases both S2n and S2n+1, so if you need to convert a float32
    461 // to a double as a temporary, you need a temporary double register.
    462 inline bool hasMultiAlias() { return false; }
    463 
    464 }  // namespace jit
    465 }  // namespace js
    466 
    467 #endif /* jit_x86_shared_Architecture_x86_h */