tor-browser

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

Architecture-loong64.h (15563B)


      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_loong64_Architecture_loong64_h
      8 #define jit_loong64_Architecture_loong64_h
      9 
     10 #include "mozilla/MathAlgorithms.h"
     11 
     12 #include <algorithm>
     13 
     14 #include "jit/shared/Architecture-shared.h"
     15 
     16 #include "js/Utility.h"
     17 
     18 namespace js {
     19 namespace jit {
     20 
     21 // LoongArch64 has 32 64-bit integer registers, r0 though r31.
     22 //  The program counter is not accessible as a register.
     23 //
     24 // SIMD and scalar floating-point registers share a register bank.
     25 //  Floating-point registers are f0 through f31.
     26 //  128 bit SIMD registers are vr0 through vr31.
     27 //  e.g., f0 is the bottom 64 bits of vr0.
     28 
     29 // LoongArch64 INT Register Convention:
     30 //  Name         Alias           Usage
     31 //  $r0          $zero           Constant zero
     32 //  $r1          $ra             Return address
     33 //  $r2          $tp             TLS
     34 //  $r3          $sp             Stack pointer
     35 //  $r4-$r11     $a0-$a7         Argument registers
     36 //  $r4-$r5      $v0-$v1         Return values
     37 //  $r12-$r20    $t0-$t8         Temporary registers
     38 //  $r21         $x              Reserved
     39 //  $r22         $fp             Frame pointer
     40 //  $r23-$r31    $s0-$s8         Callee-saved registers
     41 
     42 // LoongArch64 FP Register Convention:
     43 //  Name         Alias           Usage
     44 //  $f0-$f7      $fa0-$fa7       Argument registers
     45 //  $f0-$f1      $fv0-$fv1       Return values
     46 //  $f8-f23      $ft0-$ft15      Temporary registers
     47 //  $f24-$f31    $fs0-$fs7       Callee-saved registers
     48 
     49 class Registers {
     50 public:
     51  enum RegisterID {
     52    r0 = 0,
     53    r1,
     54    r2,
     55    r3,
     56    r4,
     57    r5,
     58    r6,
     59    r7,
     60    r8,
     61    r9,
     62    r10,
     63    r11,
     64    r12,
     65    r13,
     66    r14,
     67    r15,
     68    r16,
     69    r17,
     70    r18,
     71    r19,
     72    r20,
     73    r21,
     74    r22,
     75    r23,
     76    r24,
     77    r25,
     78    r26,
     79    r27,
     80    r28,
     81    r29,
     82    r30,
     83    r31,
     84    zero = r0,
     85    ra = r1,
     86    tp = r2,
     87    sp = r3,
     88    a0 = r4,
     89    a1 = r5,
     90    a2 = r6,
     91    a3 = r7,
     92    a4 = r8,
     93    a5 = r9,
     94    a6 = r10,
     95    a7 = r11,
     96    t0 = r12,
     97    t1 = r13,
     98    t2 = r14,
     99    t3 = r15,
    100    t4 = r16,
    101    t5 = r17,
    102    t6 = r18,
    103    t7 = r19,
    104    t8 = r20,
    105    rx = r21,
    106    fp = r22,
    107    s0 = r23,
    108    s1 = r24,
    109    s2 = r25,
    110    s3 = r26,
    111    s4 = r27,
    112    s5 = r28,
    113    s6 = r29,
    114    s7 = r30,
    115    s8 = r31,
    116    invalid_reg,
    117  };
    118  typedef uint8_t Code;
    119  typedef RegisterID Encoding;
    120  typedef uint32_t SetType;
    121 
    122  static const Encoding StackPointer = sp;
    123  static const Encoding Invalid = invalid_reg;
    124 
    125  // Content spilled during bailouts.
    126  union RegisterContent {
    127    uintptr_t r;
    128  };
    129 
    130  static uint32_t SetSize(SetType x) {
    131    static_assert(sizeof(SetType) == 4, "SetType must be 32 bits");
    132    return mozilla::CountPopulation32(x);
    133  }
    134  static uint32_t FirstBit(SetType x) {
    135    return mozilla::CountTrailingZeroes32(x);
    136  }
    137  static uint32_t LastBit(SetType x) {
    138    return 31 - mozilla::CountLeadingZeroes32(x);
    139  }
    140 
    141  static const char* GetName(uint32_t code) {
    142    static const char* const Names[] = {
    143        "zero", "ra", "tp", "sp", "a0", "a1", "a2", "a3", "a4", "a5", "a6",
    144        "a7",   "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "rx",
    145        "fp",   "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8"};
    146    static_assert(Total == std::size(Names), "Table is the correct size");
    147    if (code >= Total) {
    148      return "invalid";
    149    }
    150    return Names[code];
    151  }
    152 
    153  static Code FromName(const char* name);
    154 
    155  static const uint32_t Total = 32;
    156  static const uint32_t TotalPhys = 32;
    157  static const uint32_t Allocatable =
    158      23;  // No named special-function registers.
    159 
    160  static const SetType AllMask = 0xFFFFFFFF;
    161  static const SetType NoneMask = 0x0;
    162 
    163  static const SetType ArgRegMask =
    164      (1U << Registers::a0) | (1U << Registers::a1) | (1U << Registers::a2) |
    165      (1U << Registers::a3) | (1U << Registers::a4) | (1U << Registers::a5) |
    166      (1U << Registers::a6) | (1U << Registers::a7);
    167 
    168  static const SetType VolatileMask =
    169      (1U << Registers::a0) | (1U << Registers::a1) | (1U << Registers::a2) |
    170      (1U << Registers::a3) | (1U << Registers::a4) | (1U << Registers::a5) |
    171      (1U << Registers::a6) | (1U << Registers::a7) | (1U << Registers::t0) |
    172      (1U << Registers::t1) | (1U << Registers::t2) | (1U << Registers::t3) |
    173      (1U << Registers::t4) | (1U << Registers::t5) | (1U << Registers::t6) |
    174      (1U << Registers::t7) | (1U << Registers::t8);
    175 
    176  // We use this constant to save registers when entering functions. This
    177  // is why $ra is added here even though it is not "Non Volatile".
    178  static const SetType NonVolatileMask =
    179      (1U << Registers::ra) | (1U << Registers::fp) | (1U << Registers::s0) |
    180      (1U << Registers::s1) | (1u << Registers::s2) | (1U << Registers::s3) |
    181      (1U << Registers::s4) | (1U << Registers::s5) | (1U << Registers::s6) |
    182      (1U << Registers::s7) | (1U << Registers::s8);
    183 
    184  static const SetType NonAllocatableMask =
    185      (1U << Registers::zero) |  // Always be zero.
    186      (1U << Registers::t7) |    // Scratch register.
    187      (1U << Registers::t8) |    // Scratch register.
    188      (1U << Registers::s8) |    // Saved scratch register.
    189      (1U << Registers::rx) |    // Reserved Register.
    190      (1U << Registers::ra) | (1U << Registers::tp) | (1U << Registers::sp) |
    191      (1U << Registers::fp);
    192 
    193  static const SetType WrapperMask = VolatileMask;
    194 
    195  // Registers returned from a JS -> JS call.
    196  static const SetType JSCallMask = (1 << Registers::a2);
    197 
    198  // Registers returned from a JS -> C call.
    199  static const SetType CallMask = (1 << Registers::a0);
    200 
    201  static const SetType AllocatableMask = AllMask & ~NonAllocatableMask;
    202 };
    203 
    204 // Smallest integer type that can hold a register bitmask.
    205 typedef uint32_t PackedRegisterMask;
    206 
    207 template <typename T>
    208 class TypedRegisterSet;
    209 
    210 class FloatRegisters {
    211 public:
    212  enum FPRegisterID {
    213    f0 = 0,
    214    f1,
    215    f2,
    216    f3,
    217    f4,
    218    f5,
    219    f6,
    220    f7,
    221    f8,
    222    f9,
    223    f10,
    224    f11,
    225    f12,
    226    f13,
    227    f14,
    228    f15,
    229    f16,
    230    f17,
    231    f18,
    232    f19,
    233    f20,
    234    f21,
    235    f22,
    236    f23,  // Scratch register.
    237    f24,
    238    f25,
    239    f26,
    240    f27,
    241    f28,
    242    f29,
    243    f30,
    244    f31,
    245  };
    246 
    247  // Eight bits: (invalid << 7) | (kind << 5) | encoding
    248  typedef uint8_t Code;
    249  typedef FPRegisterID Encoding;
    250  typedef uint64_t SetType;
    251 
    252  enum Kind : uint8_t { Double, Single, NumTypes };
    253 
    254  static constexpr Code Invalid = 0x80;
    255 
    256  static const char* GetName(uint32_t code) {
    257    static const char* const Names[] = {
    258        "f0",  "f1",  "f2",  "f3",  "f4",  "f5",  "f6",  "f7",
    259        "f8",  "f9",  "f10", "f11", "f12", "f13", "f14", "f15",
    260        "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
    261        "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31"};
    262    static_assert(TotalPhys == std::size(Names), "Table is the correct size");
    263    if (code >= Total) {
    264      return "invalid";
    265    }
    266    // Single and double fpu registers share same names on LoongArch.
    267    return Names[code % TotalPhys];
    268  }
    269 
    270  static Code FromName(const char* name);
    271 
    272  static const uint32_t TotalPhys = 32;
    273  static const uint32_t Total = TotalPhys * NumTypes;
    274  static const uint32_t Allocatable = 31;  // Without f23, the scratch register.
    275 
    276  static_assert(sizeof(SetType) * 8 >= Total,
    277                "SetType should be large enough to enumerate all registers.");
    278 
    279  // Magic values which are used to duplicate a mask of physical register for
    280  // a specific type of register. A multiplication is used to copy and shift
    281  // the bits of the physical register mask.
    282  static const SetType SpreadSingle = SetType(1)
    283                                      << (uint32_t(Single) * TotalPhys);
    284  static const SetType SpreadDouble = SetType(1)
    285                                      << (uint32_t(Double) * TotalPhys);
    286  static const SetType Spread = SpreadSingle | SpreadDouble;
    287 
    288  static const SetType AllPhysMask = ((SetType(1) << TotalPhys) - 1);
    289  static const SetType AllMask = AllPhysMask * Spread;
    290  static const SetType AllSingleMask = AllPhysMask * SpreadSingle;
    291  static const SetType AllDoubleMask = AllPhysMask * SpreadDouble;
    292  static const SetType NoneMask = SetType(0);
    293 
    294  // TODO(loong64): Much less than ARM64 here.
    295  static const SetType NonVolatileMask =
    296      SetType((1U << FloatRegisters::f24) | (1U << FloatRegisters::f25) |
    297              (1U << FloatRegisters::f26) | (1U << FloatRegisters::f27) |
    298              (1U << FloatRegisters::f28) | (1U << FloatRegisters::f29) |
    299              (1U << FloatRegisters::f30) | (1U << FloatRegisters::f31)) *
    300      Spread;
    301 
    302  static const SetType VolatileMask = AllMask & ~NonVolatileMask;
    303 
    304  static const SetType WrapperMask = VolatileMask;
    305 
    306  // f23 is the scratch register.
    307  static const SetType NonAllocatableMask =
    308      (SetType(1) << FloatRegisters::f23) * Spread;
    309 
    310  static const SetType AllocatableMask = AllMask & ~NonAllocatableMask;
    311 
    312  // Content spilled during bailouts.
    313  union RegisterContent {
    314    float s;
    315    double d;
    316  };
    317 
    318  static constexpr Encoding encoding(Code c) {
    319    // assert() not available in constexpr function.
    320    // assert(c < Total);
    321    return Encoding(c & 31);
    322  }
    323 
    324  static constexpr Kind kind(Code c) {
    325    // assert() not available in constexpr function.
    326    // assert(c < Total && ((c >> 5) & 3) < NumTypes);
    327    return Kind((c >> 5) & 3);
    328  }
    329 
    330  static constexpr Code fromParts(uint32_t encoding, uint32_t kind,
    331                                  uint32_t invalid) {
    332    return Code((invalid << 7) | (kind << 5) | encoding);
    333  }
    334 };
    335 
    336 static const uint32_t SpillSlotSize =
    337    std::max(sizeof(Registers::RegisterContent),
    338             sizeof(FloatRegisters::RegisterContent));
    339 
    340 static constexpr uint32_t ShadowStackSpace = 0;
    341 static const uint32_t SizeOfReturnAddressAfterCall = 0;
    342 
    343 // When our only strategy for far jumps is to encode the offset directly, and
    344 // not insert any jump islands during assembly for even further jumps, then the
    345 // architecture restricts us to -2^27 .. 2^27-4, to fit into a signed 28-bit
    346 // value.  We further reduce this range to allow the far-jump inserting code to
    347 // have some breathing room.
    348 static const uint32_t JumpImmediateRange = ((1 << 27) - (20 * 1024 * 1024));
    349 
    350 struct FloatRegister {
    351  typedef FloatRegisters Codes;
    352  typedef size_t Code;
    353  typedef Codes::Encoding Encoding;
    354  typedef Codes::SetType SetType;
    355 
    356  static uint32_t SetSize(SetType x) {
    357    static_assert(sizeof(SetType) == 8, "SetType must be 64 bits");
    358    x |= x >> FloatRegisters::TotalPhys;
    359    x &= FloatRegisters::AllPhysMask;
    360    return mozilla::CountPopulation32(x);
    361  }
    362 
    363  static uint32_t FirstBit(SetType x) {
    364    static_assert(sizeof(SetType) == 8, "SetType");
    365    return mozilla::CountTrailingZeroes64(x);
    366  }
    367  static uint32_t LastBit(SetType x) {
    368    static_assert(sizeof(SetType) == 8, "SetType");
    369    return 63 - mozilla::CountLeadingZeroes64(x);
    370  }
    371 
    372 private:
    373  // These fields only hold valid values: an invalid register is always
    374  // represented as a valid encoding and kind with the invalid_ bit set.
    375  uint8_t encoding_;  // 32 encodings
    376  uint8_t kind_;      // Double, Single; more later
    377  bool invalid_;
    378 
    379  typedef Codes::Kind Kind;
    380 
    381 public:
    382  constexpr FloatRegister(Encoding encoding, Kind kind)
    383      : encoding_(encoding), kind_(kind), invalid_(false) {
    384    // assert(uint32_t(encoding) < Codes::TotalPhys);
    385  }
    386 
    387  constexpr FloatRegister()
    388      : encoding_(0), kind_(FloatRegisters::Double), invalid_(true) {}
    389 
    390  static FloatRegister FromCode(uint32_t i) {
    391    MOZ_ASSERT(i < Codes::Total);
    392    return FloatRegister(FloatRegisters::encoding(i), FloatRegisters::kind(i));
    393  }
    394 
    395  bool isSingle() const {
    396    MOZ_ASSERT(!invalid_);
    397    return kind_ == FloatRegisters::Single;
    398  }
    399  bool isDouble() const {
    400    MOZ_ASSERT(!invalid_);
    401    return kind_ == FloatRegisters::Double;
    402  }
    403  bool isSimd128() const {
    404    MOZ_ASSERT(!invalid_);
    405    return false;
    406  }
    407  bool isInvalid() const { return invalid_; }
    408 
    409  FloatRegister asSingle() const {
    410    MOZ_ASSERT(!invalid_);
    411    return FloatRegister(Encoding(encoding_), FloatRegisters::Single);
    412  }
    413  FloatRegister asDouble() const {
    414    MOZ_ASSERT(!invalid_);
    415    return FloatRegister(Encoding(encoding_), FloatRegisters::Double);
    416  }
    417  FloatRegister asSimd128() const { MOZ_CRASH(); }
    418 
    419  constexpr uint32_t size() const {
    420    MOZ_ASSERT(!invalid_);
    421    if (kind_ == FloatRegisters::Double) {
    422      return sizeof(double);
    423    }
    424    MOZ_ASSERT(kind_ == FloatRegisters::Single);
    425    return sizeof(float);
    426  }
    427 
    428  constexpr Code code() const {
    429    // assert(!invalid_);
    430    return Codes::fromParts(encoding_, kind_, invalid_);
    431  }
    432 
    433  constexpr Encoding encoding() const {
    434    MOZ_ASSERT(!invalid_);
    435    return Encoding(encoding_);
    436  }
    437 
    438  const char* name() const { return FloatRegisters::GetName(code()); }
    439  bool volatile_() const {
    440    MOZ_ASSERT(!invalid_);
    441    return !!((SetType(1) << code()) & FloatRegisters::VolatileMask);
    442  }
    443  constexpr bool operator!=(FloatRegister other) const {
    444    return code() != other.code();
    445  }
    446  constexpr bool operator==(FloatRegister other) const {
    447    return code() == other.code();
    448  }
    449 
    450  bool aliases(FloatRegister other) const {
    451    return other.encoding_ == encoding_;
    452  }
    453  // Ensure that two floating point registers' types are equivalent.
    454  bool equiv(FloatRegister other) const {
    455    MOZ_ASSERT(!invalid_);
    456    return kind_ == other.kind_;
    457  }
    458 
    459  uint32_t numAliased() const { return Codes::NumTypes; }
    460  uint32_t numAlignedAliased() { return numAliased(); }
    461 
    462  FloatRegister aliased(uint32_t aliasIdx) {
    463    MOZ_ASSERT(!invalid_);
    464    MOZ_ASSERT(aliasIdx < numAliased());
    465    return FloatRegister(Encoding(encoding_),
    466                         Kind((aliasIdx + kind_) % numAliased()));
    467  }
    468  FloatRegister alignedAliased(uint32_t aliasIdx) {
    469    MOZ_ASSERT(aliasIdx < numAliased());
    470    return aliased(aliasIdx);
    471  }
    472  SetType alignedOrDominatedAliasedSet() const {
    473    return Codes::Spread << encoding_;
    474  }
    475 
    476  static constexpr RegTypeName DefaultType = RegTypeName::Float64;
    477 
    478  template <RegTypeName Name = DefaultType>
    479  static SetType LiveAsIndexableSet(SetType s) {
    480    return SetType(0);
    481  }
    482 
    483  template <RegTypeName Name = DefaultType>
    484  static SetType AllocatableAsIndexableSet(SetType s) {
    485    static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable");
    486    return LiveAsIndexableSet<Name>(s);
    487  }
    488 
    489  static TypedRegisterSet<FloatRegister> ReduceSetForPush(
    490      const TypedRegisterSet<FloatRegister>& s);
    491  static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister>& s);
    492  uint32_t getRegisterDumpOffsetInBytes();
    493 };
    494 
    495 template <>
    496 inline FloatRegister::SetType
    497 FloatRegister::LiveAsIndexableSet<RegTypeName::Float32>(SetType set) {
    498  return set & FloatRegisters::AllSingleMask;
    499 }
    500 
    501 template <>
    502 inline FloatRegister::SetType
    503 FloatRegister::LiveAsIndexableSet<RegTypeName::Float64>(SetType set) {
    504  return set & FloatRegisters::AllDoubleMask;
    505 }
    506 
    507 template <>
    508 inline FloatRegister::SetType
    509 FloatRegister::LiveAsIndexableSet<RegTypeName::Any>(SetType set) {
    510  return set;
    511 }
    512 
    513 // LoongArch doesn't have double registers that cannot be treated as float32.
    514 inline bool hasUnaliasedDouble() { return false; }
    515 
    516 // LoongArch doesn't have double registers that alias multiple floats.
    517 inline bool hasMultiAlias() { return false; }
    518 
    519 uint32_t GetLOONG64Flags();
    520 
    521 }  // namespace jit
    522 }  // namespace js
    523 
    524 #endif /* jit_loong64_Architecture_loong64_h */