Architecture-riscv64.h (15789B)
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_riscv64_Architecture_riscv64_h 8 #define jit_riscv64_Architecture_riscv64_h 9 10 // JitSpewer.h is included through MacroAssembler implementations for other 11 // platforms, so include it here to avoid inadvertent build bustage. 12 #include "mozilla/MathAlgorithms.h" 13 14 #include <algorithm> 15 16 #include "jit/JitSpewer.h" 17 #include "jit/shared/Architecture-shared.h" 18 #include "js/Utility.h" 19 20 namespace js { 21 namespace jit { 22 23 static const uint32_t SimdMemoryAlignment = 24 16; // Make it 4 to avoid a bunch of div-by-zero warnings 25 26 // RISCV64 has 32 64-bit integer registers, x0 though x31. 27 // The program counter is not accessible as a register. 28 29 // RISCV INT Register Convention: 30 // Name Alias Usage 31 // x0 zero hardwired to 0, ignores writes 32 // x1 ra return address for calls 33 // x2 sp stack pointer 34 // x3 gp global pointer 35 // x4 tp thread pointer 36 // x5-x7 t0-t2 temporary register 0 37 // x8 fp/s0 Callee-saved register 0 or frame pointer 38 // x9 s1 Callee-saved register 1 39 // x10-x11 a0-a1 return value or function argument 40 // x12-x17 a2-a7 function argument 2 41 // x18-x27 s2-s11 Callee-saved register 42 // x28-x31 t3-t6 temporary register 3 43 44 // RISCV-64 FP Register Convention: 45 // Name Alias Usage 46 // $f0-$f7 $ft0-$ft7 Temporary registers 47 // $f8-$f9 $fs0-$fs1 Callee-saved registers 48 // $f10-$f11 $fa0-$fa1 Return values 49 // $f12-$f17 $fa2-$fa7 Args values 50 // $f18-$f27 $fs2-$fs11 Callee-saved registers 51 // $f28-$f31 $ft8-$ft11 Temporary registers 52 class Registers { 53 public: 54 enum RegisterID { 55 x0 = 0, 56 x1, 57 x2, 58 x3, 59 x4, 60 x5, 61 x6, 62 x7, 63 x8, 64 x9, 65 x10, 66 x11, 67 x12, 68 x13, 69 x14, 70 x15, 71 x16, 72 x17, 73 x18, 74 x19, 75 x20, 76 x21, 77 x22, 78 x23, 79 x24, 80 x25, 81 x26, 82 x27, 83 x28, 84 x29, 85 x30, 86 x31, 87 zero = x0, 88 ra = x1, 89 sp = x2, 90 gp = x3, 91 tp = x4, 92 t0 = x5, 93 t1 = x6, 94 t2 = x7, 95 fp = x8, 96 s1 = x9, 97 a0 = x10, 98 a1 = x11, 99 a2 = x12, 100 a3 = x13, 101 a4 = x14, 102 a5 = x15, 103 a6 = x16, 104 a7 = x17, 105 s2 = x18, 106 s3 = x19, 107 s4 = x20, 108 s5 = x21, 109 s6 = x22, 110 s7 = x23, 111 s8 = x24, 112 s9 = x25, 113 s10 = x26, 114 s11 = x27, 115 t3 = x28, 116 t4 = x29, 117 t5 = x30, 118 t6 = x31, 119 invalid_reg, 120 }; 121 typedef uint8_t Code; 122 typedef RegisterID Encoding; 123 union RegisterContent { 124 uintptr_t r; 125 }; 126 127 typedef uint32_t SetType; 128 129 static uint32_t SetSize(SetType x) { 130 static_assert(sizeof(SetType) == 4, "SetType must be 32 bits"); 131 return mozilla::CountPopulation32(x); 132 } 133 static uint32_t FirstBit(SetType x) { 134 return mozilla::CountTrailingZeroes32(x); 135 } 136 static uint32_t LastBit(SetType x) { 137 return 31 - mozilla::CountLeadingZeroes32(x); 138 } 139 static const char* GetName(uint32_t code) { 140 static const char* const Names[] = { 141 "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "fp", "s1", "a0", 142 "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3", "s4", "s5", 143 "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"}; 144 static_assert(Total == std::size(Names), "Table is the correct size"); 145 if (code >= Total) { 146 return "invalid"; 147 } 148 return Names[code]; 149 } 150 151 static Code FromName(const char*); 152 153 static const Encoding StackPointer = sp; 154 static const Encoding Invalid = invalid_reg; 155 static const uint32_t Total = 32; 156 static const uint32_t TotalPhys = 32; 157 static const uint32_t Allocatable = 24; 158 static const SetType NoneMask = 0x0; 159 static const SetType AllMask = 0xFFFFFFFF; 160 static const SetType ArgRegMask = 161 (1 << Registers::a0) | (1 << Registers::a1) | (1 << Registers::a2) | 162 (1 << Registers::a3) | (1 << Registers::a4) | (1 << Registers::a5) | 163 (1 << Registers::a6) | (1 << Registers::a7); 164 165 static const SetType VolatileMask = 166 ArgRegMask | (1 << Registers::t0) | (1 << Registers::t1) | 167 (1 << Registers::t2) | (1 << Registers::t3) | (1 << Registers::t4) | 168 (1 << Registers::t5) | (1 << Registers::t6); 169 170 // We use this constant to save registers when entering functions. This 171 // is why $ra is added here even though it is not "Non Volatile". 172 static const SetType NonVolatileMask = 173 (1 << Registers::ra) | (1 << Registers::fp) | (1 << Registers::s1) | 174 (1 << Registers::s2) | (1 << Registers::s3) | (1 << Registers::s4) | 175 (1 << Registers::s5) | (1 << Registers::s6) | (1 << Registers::s7) | 176 (1 << Registers::s8) | (1 << Registers::s9) | (1 << Registers::s10) | 177 (1 << Registers::s11); 178 179 static const SetType NonAllocatableMask = 180 (1 << Registers::zero) | // Always be zero. 181 (1 << Registers::t4) | // Scratch reg 182 (1 << Registers::t5) | // Scratch reg 183 (1 << Registers::t6) | // Scratch reg or call reg 184 (1 << Registers::s11) | // Scratch reg 185 (1 << Registers::ra) | (1 << Registers::tp) | (1 << Registers::sp) | 186 (1 << Registers::fp) | (1 << Registers::gp); 187 188 static const SetType AllocatableMask = AllMask & ~NonAllocatableMask; 189 190 // Registers returned from a JS -> JS call. 191 static const SetType JSCallMask = (1 << Registers::a2); 192 193 // Registers returned from a JS -> C call. 194 static const SetType CallMask = (1 << Registers::a0); 195 196 static const SetType WrapperMask = VolatileMask; 197 }; 198 199 // Smallest integer type that can hold a register bitmask. 200 typedef uint32_t PackedRegisterMask; 201 202 class FloatRegisters { 203 public: 204 enum FPRegisterID { 205 f0 = 0, 206 f1, 207 f2, 208 f3, 209 f4, 210 f5, 211 f6, 212 f7, 213 f8, 214 f9, 215 f10, 216 f11, 217 f12, 218 f13, 219 f14, 220 f15, 221 f16, 222 f17, 223 f18, 224 f19, 225 f20, 226 f21, 227 f22, 228 f23, 229 f24, 230 f25, 231 f26, 232 f27, 233 f28, 234 f29, 235 f30, 236 f31, 237 invalid_reg, 238 ft0 = f0, 239 ft1 = f1, 240 ft2 = f2, 241 ft3 = f3, 242 ft4 = f4, 243 ft5 = f5, 244 ft6 = f6, 245 ft7 = f7, 246 fs0 = f8, 247 fs1 = f9, 248 fa0 = f10, 249 fa1 = f11, 250 fa2 = f12, 251 fa3 = f13, 252 fa4 = f14, 253 fa5 = f15, 254 fa6 = f16, 255 fa7 = f17, 256 fs2 = f18, 257 fs3 = f19, 258 fs4 = f20, 259 fs5 = f21, 260 fs6 = f22, 261 fs7 = f23, 262 fs8 = f24, 263 fs9 = f25, 264 fs10 = f26, 265 fs11 = f27, // Scratch register 266 ft8 = f28, 267 ft9 = f29, 268 ft10 = f30, // Scratch register 269 ft11 = f31 270 }; 271 272 enum Kind : uint8_t { Double, Single, NumTypes }; 273 274 // (invalid << 7) | (kind << 5) | encoding 275 using Code = uint8_t; 276 using Encoding = FPRegisterID; 277 278 union RegisterContent { 279 float s; 280 double d; 281 }; 282 283 static const char* GetName(Code code) { 284 static const char* const Names[] = { 285 "ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", 286 "fs0", "fs1", "fa0", "fa1", "fa2", "fa3", "fa4", "fa5", 287 "fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", 288 "fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11"}; 289 static_assert(TotalPhys == std::size(Names), "Table is the correct size"); 290 if (code >= Total) { 291 return "invalid"; 292 } 293 return Names[code & 0x1f]; 294 } 295 296 static Code FromName(const char* name); 297 298 using SetType = uint64_t; 299 300 static const Code Invalid = Code(0b10000000); 301 static const uint32_t TotalPhys = 32; 302 static const uint32_t Total = TotalPhys * NumTypes; 303 static const uint32_t Allocatable = 23; 304 305 static_assert(sizeof(SetType) * 8 >= Total, 306 "SetType should be large enough to enumerate all registers."); 307 308 // Magic values which are used to duplicate a mask of physical register for 309 // a specific type of register. A multiplication is used to copy and shift 310 // the bits of the physical register mask. 311 static const SetType SpreadSingle = SetType(1) 312 << (uint32_t(Kind::Single) * TotalPhys); 313 static const SetType SpreadDouble = SetType(1) 314 << (uint32_t(Kind::Double) * TotalPhys); 315 static const SetType Spread = SpreadSingle | SpreadDouble; 316 317 static const SetType AllPhysMask = ((SetType(1) << TotalPhys) - 1); 318 static const SetType AllMask = AllPhysMask * Spread; 319 static const SetType AllSingleMask = AllPhysMask * SpreadSingle; 320 static const SetType AllDoubleMask = AllPhysMask * SpreadDouble; 321 static const SetType NoneMask = SetType(0); 322 323 static const SetType NonVolatileMask = 324 SetType((1 << FloatRegisters::fs0) | (1 << FloatRegisters::fs1) | 325 (1 << FloatRegisters::fs2) | (1 << FloatRegisters::fs3) | 326 (1 << FloatRegisters::fs4) | (1 << FloatRegisters::fs5) | 327 (1 << FloatRegisters::fs6) | (1 << FloatRegisters::fs7) | 328 (1 << FloatRegisters::fs8) | (1 << FloatRegisters::fs9) | 329 (1 << FloatRegisters::fs10) | (1 << FloatRegisters::fs11)) * 330 Spread; 331 static const SetType VolatileMask = AllMask & ~NonVolatileMask; 332 333 // fs11/ft10 is the scratch register. 334 static const SetType NonAllocatableMask = 335 ((SetType(1) << FloatRegisters::fs11) | 336 (SetType(1) << FloatRegisters::ft10)) * 337 Spread; 338 339 static const SetType AllocatableMask = AllMask & ~NonAllocatableMask; 340 }; 341 342 template <typename T> 343 class TypedRegisterSet; 344 345 struct FloatRegister { 346 public: 347 using Codes = FloatRegisters; 348 using Code = Codes::Code; 349 using Encoding = Codes::Encoding; 350 using SetType = Codes::SetType; 351 352 static uint32_t SetSize(SetType x) { 353 static_assert(sizeof(SetType) == 8, "SetType must be 64 bits"); 354 x |= x >> FloatRegisters::TotalPhys; 355 x &= FloatRegisters::AllPhysMask; 356 return mozilla::CountPopulation32(x); 357 } 358 359 static uint32_t FirstBit(SetType x) { 360 static_assert(sizeof(SetType) == 8, "SetType must be 64 bits"); 361 return mozilla::CountTrailingZeroes64(x); 362 } 363 static uint32_t LastBit(SetType x) { 364 static_assert(sizeof(SetType) == 8, "SetType must be 64 bits"); 365 return 63 - mozilla::CountLeadingZeroes64(x); 366 } 367 368 static FloatRegister FromCode(Code code) { 369 MOZ_ASSERT(code < Codes::Total); 370 const Encoding encoding = Encoding(code & 0x1f); 371 const Kind kind = Kind((code >> 5) & 0x3); 372 return FloatRegister(encoding, kind); 373 } 374 bool isSimd128() const { return false; } 375 bool isInvalid() const { return invalid_; } 376 FloatRegister asSingle() const { 377 MOZ_ASSERT(!invalid_); 378 return FloatRegister(Encoding(encoding_), FloatRegisters::Single); 379 } 380 FloatRegister asDouble() const { 381 MOZ_ASSERT(!invalid_); 382 return FloatRegister(Encoding(encoding_), FloatRegisters::Double); 383 } 384 FloatRegister asSimd128() const { MOZ_CRASH(); } 385 constexpr Code code() const { 386 MOZ_ASSERT(!invalid_); 387 return Code((invalid_ << 7) | ((static_cast<uint8_t>(kind_) & 0x3) << 5) | 388 (static_cast<uint8_t>(encoding_) & 0x1f)); 389 } 390 constexpr Encoding encoding() const { return encoding_; } 391 const char* name() const { return FloatRegisters::GetName(encoding()); } 392 bool volatile_() const { 393 MOZ_ASSERT(!invalid_); 394 return !!((SetType(1) << code()) & FloatRegisters::VolatileMask); 395 } 396 bool operator!=(FloatRegister other) const { return code() != other.code(); } 397 bool operator==(FloatRegister other) const { return code() == other.code(); } 398 bool aliases(FloatRegister other) const { 399 return other.encoding_ == encoding_; 400 } 401 uint32_t numAliased() const { return FloatRegisters::NumTypes; } 402 FloatRegister aliased(uint32_t aliasIdx) const { 403 MOZ_ASSERT(!invalid_); 404 MOZ_ASSERT(aliasIdx < numAliased()); 405 return FloatRegister(Encoding(encoding_), 406 Kind((aliasIdx + kind_) % numAliased())); 407 } 408 // Ensure that two floating point registers' types are equivalent. 409 bool equiv(FloatRegister other) const { 410 MOZ_ASSERT(!invalid_); 411 return kind_ == other.kind_; 412 } 413 constexpr uint32_t size() const { 414 MOZ_ASSERT(!invalid_); 415 if (kind_ == FloatRegisters::Double) { 416 return sizeof(double); 417 } 418 MOZ_ASSERT(kind_ == FloatRegisters::Single); 419 return sizeof(float); 420 } 421 uint32_t numAlignedAliased() { return numAliased(); } 422 FloatRegister alignedAliased(uint32_t aliasIdx) { 423 MOZ_ASSERT(aliasIdx < numAliased()); 424 return aliased(aliasIdx); 425 } 426 SetType alignedOrDominatedAliasedSet() const { 427 return Codes::Spread << encoding_; 428 } 429 static constexpr RegTypeName DefaultType = RegTypeName::Float64; 430 431 template <RegTypeName Name = DefaultType> 432 static SetType LiveAsIndexableSet(SetType s) { 433 return SetType(0); 434 } 435 436 template <RegTypeName Name = DefaultType> 437 static SetType AllocatableAsIndexableSet(SetType s) { 438 static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable"); 439 return LiveAsIndexableSet<Name>(s); 440 } 441 442 FloatRegister singleOverlay() const; 443 FloatRegister doubleOverlay() const; 444 445 static TypedRegisterSet<FloatRegister> ReduceSetForPush( 446 const TypedRegisterSet<FloatRegister>& s); 447 448 uint32_t getRegisterDumpOffsetInBytes() { 449 #ifdef ENABLE_WASM_SIMD 450 # error "Needs more careful logic if SIMD is enabled" 451 #endif 452 453 return encoding() * sizeof(double); 454 } 455 static Code FromName(const char* name); 456 457 // This is used in static initializers, so produce a bogus value instead of 458 // crashing. 459 static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister>& s); 460 461 private: 462 using Kind = Codes::Kind; 463 // These fields only hold valid values: an invalid register is always 464 // represented as a valid encoding and kind with the invalid_ bit set. 465 Encoding encoding_; // 32 encodings 466 Kind kind_; // Double, Single; more later 467 bool invalid_; 468 469 public: 470 constexpr FloatRegister(Encoding encoding, Kind kind) 471 : encoding_(encoding), kind_(kind), invalid_(false) { 472 MOZ_ASSERT(uint32_t(encoding) < Codes::Total); 473 } 474 475 constexpr explicit FloatRegister(Encoding encoding) 476 : encoding_(encoding), kind_(FloatRegisters::Double), invalid_(false) { 477 MOZ_ASSERT(uint32_t(encoding) < Codes::Total); 478 } 479 480 constexpr FloatRegister() 481 : encoding_(FloatRegisters::invalid_reg), 482 kind_(FloatRegisters::Double), 483 invalid_(true) {} 484 485 bool isSingle() const { 486 MOZ_ASSERT(!invalid_); 487 return kind_ == FloatRegisters::Single; 488 } 489 490 bool isDouble() const { 491 MOZ_ASSERT(!invalid_); 492 return kind_ == FloatRegisters::Double; 493 } 494 }; 495 496 template <> 497 inline FloatRegister::SetType 498 FloatRegister::LiveAsIndexableSet<RegTypeName::Float32>(SetType set) { 499 return set & FloatRegisters::AllSingleMask; 500 } 501 502 template <> 503 inline FloatRegister::SetType 504 FloatRegister::LiveAsIndexableSet<RegTypeName::Float64>(SetType set) { 505 return set & FloatRegisters::AllDoubleMask; 506 } 507 508 template <> 509 inline FloatRegister::SetType 510 FloatRegister::LiveAsIndexableSet<RegTypeName::Any>(SetType set) { 511 return set; 512 } 513 514 inline bool hasUnaliasedDouble() { return false; } 515 inline bool hasMultiAlias() { return false; } 516 517 static constexpr uint32_t ShadowStackSpace = 0; 518 static const uint32_t JumpImmediateRange = INT32_MAX; 519 520 #ifdef JS_NUNBOX32 521 static const int32_t NUNBOX32_TYPE_OFFSET = 4; 522 static const int32_t NUNBOX32_PAYLOAD_OFFSET = 0; 523 #endif 524 525 static const uint32_t SpillSlotSize = 526 std::max(sizeof(Registers::RegisterContent), 527 sizeof(FloatRegisters::RegisterContent)); 528 529 inline uint32_t GetRISCV64Flags() { return 0; } 530 531 } // namespace jit 532 } // namespace js 533 534 #endif /* jit_riscv64_Architecture_riscv64_h */