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 */