/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * 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_riscv64_Architecture_riscv64_h #define jit_riscv64_Architecture_riscv64_h // JitSpewer.h is included through MacroAssembler implementations for other // platforms, so include it here to avoid inadvertent build bustage. #include "mozilla/MathAlgorithms.h" #include #include #include "jit/JitSpewer.h" #include "jit/shared/Architecture-shared.h" #include "js/Utility.h" namespace js { namespace jit { static const uint32_t SimdMemoryAlignment = 16; // Make it 4 to avoid a bunch of div-by-zero warnings // RISCV64 has 32 64-bit integer registers, x0 though x31. // The program counter is not accessible as a register. // RISCV INT Register Convention: // Name Alias Usage // x0 zero hardwired to 0, ignores writes // x1 ra return address for calls // x2 sp stack pointer // x3 gp global pointer // x4 tp thread pointer // x5-x7 t0-t2 temporary register 0 // x8 fp/s0 Callee-saved register 0 or frame pointer // x9 s1 Callee-saved register 1 // x10-x11 a0-a1 return value or function argument // x12-x17 a2-a7 function argument 2 // x18-x27 s2-s11 Callee-saved register // x28-x31 t3-t6 temporary register 3 // RISCV-64 FP Register Convention: // Name Alias Usage // $f0-$f7 $ft0-$ft7 Temporary registers // $f8-$f9 $fs0-$fs1 Callee-saved registers // $f10-$f11 $fa0-$fa1 Return values // $f12-$f17 $fa2-$fa7 Args values // $f18-$f27 $fs2-$fs11 Callee-saved registers // $f28-$f31 $ft8-$ft11 Temporary registers class Registers { public: enum RegisterID { x0 = 0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31, zero = x0, ra = x1, sp = x2, gp = x3, tp = x4, t0 = x5, t1 = x6, t2 = x7, fp = x8, s1 = x9, a0 = x10, a1 = x11, a2 = x12, a3 = x13, a4 = x14, a5 = x15, a6 = x16, a7 = x17, s2 = x18, s3 = x19, s4 = x20, s5 = x21, s6 = x22, s7 = x23, s8 = x24, s9 = x25, s10 = x26, s11 = x27, t3 = x28, t4 = x29, t5 = x30, t6 = x31, invalid_reg, }; typedef uint8_t Code; typedef RegisterID Encoding; union RegisterContent { uintptr_t r; }; typedef uint32_t SetType; 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); } static const char* GetName(uint32_t code) { static const char* const Names[] = { "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "fp", "s1", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"}; static_assert(Total == std::size(Names), "Table is the correct size"); if (code >= Total) { return "invalid"; } return Names[code]; } static Code FromName(const char*); static const Encoding StackPointer = sp; static const Encoding Invalid = invalid_reg; static const uint32_t Total = 32; static const uint32_t TotalPhys = 32; static const uint32_t Allocatable = 24; static const SetType NoneMask = 0x0; static const SetType AllMask = 0xFFFFFFFF; static const SetType ArgRegMask = (1 << Registers::a0) | (1 << Registers::a1) | (1 << Registers::a2) | (1 << Registers::a3) | (1 << Registers::a4) | (1 << Registers::a5) | (1 << Registers::a6) | (1 << Registers::a7); static const SetType VolatileMask = ArgRegMask | (1 << Registers::t0) | (1 << Registers::t1) | (1 << Registers::t2) | (1 << Registers::t3) | (1 << Registers::t4) | (1 << Registers::t5) | (1 << Registers::t6); // We use this constant to save registers when entering functions. This // is why $ra is added here even though it is not "Non Volatile". static const SetType NonVolatileMask = (1 << Registers::ra) | (1 << Registers::fp) | (1 << Registers::s1) | (1 << Registers::s2) | (1 << Registers::s3) | (1 << Registers::s4) | (1 << Registers::s5) | (1 << Registers::s6) | (1 << Registers::s7) | (1 << Registers::s8) | (1 << Registers::s9) | (1 << Registers::s10) | (1 << Registers::s11); static const SetType NonAllocatableMask = (1 << Registers::zero) | // Always be zero. (1 << Registers::t4) | // Scratch reg (1 << Registers::t5) | // Scratch reg (1 << Registers::t6) | // Scratch reg or call reg (1 << Registers::s11) | // Scratch reg (1 << Registers::ra) | (1 << Registers::tp) | (1 << Registers::sp) | (1 << Registers::fp) | (1 << Registers::gp); static const SetType AllocatableMask = AllMask & ~NonAllocatableMask; // Registers returned from a JS -> JS call. static const SetType JSCallMask = (1 << Registers::a2); // Registers returned from a JS -> C call. static const SetType CallMask = (1 << Registers::a0); static const SetType WrapperMask = VolatileMask; }; // Smallest integer type that can hold a register bitmask. typedef uint32_t PackedRegisterMask; class FloatRegisters { public: enum FPRegisterID { f0 = 0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, f21, f22, f23, f24, f25, f26, f27, f28, f29, f30, f31, invalid_reg, ft0 = f0, ft1 = f1, ft2 = f2, ft3 = f3, ft4 = f4, ft5 = f5, ft6 = f6, ft7 = f7, fs0 = f8, fs1 = f9, fa0 = f10, fa1 = f11, fa2 = f12, fa3 = f13, fa4 = f14, fa5 = f15, fa6 = f16, fa7 = f17, fs2 = f18, fs3 = f19, fs4 = f20, fs5 = f21, fs6 = f22, fs7 = f23, fs8 = f24, fs9 = f25, fs10 = f26, fs11 = f27, // Scratch register ft8 = f28, ft9 = f29, ft10 = f30, // Scratch register ft11 = f31 }; enum Kind : uint8_t { Double, NumTypes, Single }; typedef FPRegisterID Code; typedef FPRegisterID Encoding; union RegisterContent { float s; double d; }; static const char* GetName(uint32_t code) { static const char* const Names[] = { "ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", "fs0", "fs2", "fa0", "fa1", "fa2", "fa3", "fa4", "fa5", "fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", "fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11"}; static_assert(TotalPhys == std::size(Names), "Table is the correct size"); if (code >= Total) { return "invalid"; } return Names[code]; } static Code FromName(const char* name); typedef uint32_t SetType; static const Code Invalid = invalid_reg; static const uint32_t Total = 32; static const uint32_t TotalPhys = 32; static const uint32_t Allocatable = 23; static const SetType AllPhysMask = 0xFFFFFFFF; static const SetType AllMask = 0xFFFFFFFF; static const SetType AllDoubleMask = AllMask; // Single values are stored as 64 bits values (NaN-boxed) when pushing them to // the stack, we do not require making distinctions between the 2 types, and // therefore the masks are overlapping.See The RISC-V Instruction Set Manual // for 14.2 NaN Boxing of Narrower Values. static const SetType AllSingleMask = AllMask; static const SetType NonVolatileMask = SetType((1 << FloatRegisters::fs0) | (1 << FloatRegisters::fs1) | (1 << FloatRegisters::fs2) | (1 << FloatRegisters::fs3) | (1 << FloatRegisters::fs4) | (1 << FloatRegisters::fs5) | (1 << FloatRegisters::fs6) | (1 << FloatRegisters::fs7) | (1 << FloatRegisters::fs8) | (1 << FloatRegisters::fs9) | (1 << FloatRegisters::fs10) | (1 << FloatRegisters::fs11)); static const SetType VolatileMask = AllMask & ~NonVolatileMask; // fs11/ft10 is the scratch register. static const SetType NonAllocatableMask = SetType((1 << FloatRegisters::fs11) | (1 << FloatRegisters::ft10)); static const SetType AllocatableMask = AllMask & ~NonAllocatableMask; }; template class TypedRegisterSet; struct FloatRegister { public: typedef FloatRegisters Codes; typedef Codes::Code Code; typedef Codes::Encoding Encoding; typedef Codes::SetType SetType; static uint32_t SetSize(SetType x) { static_assert(sizeof(SetType) == 4, "SetType must be 32 bits"); x &= FloatRegisters::AllPhysMask; return mozilla::CountPopulation32(x); } static uint32_t FirstBit(SetType x) { static_assert(sizeof(SetType) == 4, "SetType"); return mozilla::CountTrailingZeroes64(x); } static uint32_t LastBit(SetType x) { static_assert(sizeof(SetType) == 4, "SetType"); return 31 - mozilla::CountLeadingZeroes64(x); } static FloatRegister FromCode(uint32_t i) { uint32_t code = i & 0x1f; return FloatRegister(Code(code)); } bool isSimd128() const { return false; } bool isInvalid() const { return invalid_; } FloatRegister asSingle() const { MOZ_ASSERT(!invalid_); return FloatRegister(Encoding(encoding_), FloatRegisters::Single); } FloatRegister asDouble() const { MOZ_ASSERT(!invalid_); return FloatRegister(Encoding(encoding_), FloatRegisters::Double); } FloatRegister asSimd128() const { MOZ_CRASH(); } constexpr Code code() const { MOZ_ASSERT(!invalid_); return encoding_; } Encoding encoding() const { return encoding_; } const char* name() const { return FloatRegisters::GetName(code()); } bool volatile_() const { MOZ_ASSERT(!invalid_); return !!((SetType(1) << code()) & FloatRegisters::VolatileMask); } bool operator!=(FloatRegister other) const { return code() != other.code(); } bool operator==(FloatRegister other) const { return code() == other.code(); } bool aliases(FloatRegister other) const { return other.encoding_ == encoding_; } uint32_t numAliased() const { return 1; } FloatRegister aliased(uint32_t aliasIdx) const { MOZ_ASSERT(aliasIdx == 0); return *this; } // Ensure that two floating point registers' types are equivalent. bool equiv(FloatRegister other) const { MOZ_ASSERT(!invalid_); return kind_ == other.kind_; } constexpr uint32_t size() const { MOZ_ASSERT(!invalid_); if (kind_ == FloatRegisters::Double) { return sizeof(double); } MOZ_ASSERT(kind_ == FloatRegisters::Single); return sizeof(float); } uint32_t numAlignedAliased() { return numAliased(); } FloatRegister alignedAliased(uint32_t aliasIdx) { MOZ_ASSERT(aliasIdx < numAliased()); return aliased(aliasIdx); } SetType alignedOrDominatedAliasedSet() const { return SetType(1) << code(); } static constexpr RegTypeName DefaultType = RegTypeName::Float64; template static SetType LiveAsIndexableSet(SetType s) { return SetType(0); } template static SetType AllocatableAsIndexableSet(SetType s) { static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable"); printf("AllocatableAsIndexableSet\n"); return LiveAsIndexableSet(s); } FloatRegister singleOverlay() const; FloatRegister doubleOverlay() const; static TypedRegisterSet ReduceSetForPush( const TypedRegisterSet& s); uint32_t getRegisterDumpOffsetInBytes() { #ifdef ENABLE_WASM_SIMD # error "Needs more careful logic if SIMD is enabled" #endif return code() * sizeof(double); } static Code FromName(const char* name); // This is used in static initializers, so produce a bogus value instead of // crashing. static uint32_t GetPushSizeInBytes(const TypedRegisterSet& s); private: typedef Codes::Kind Kind; // These fields only hold valid values: an invalid register is always // represented as a valid encoding and kind with the invalid_ bit set. Encoding encoding_; // 32 encodings Kind kind_; // Double, Single; more later bool invalid_; public: constexpr FloatRegister(Encoding encoding, Kind kind) : encoding_(encoding), kind_(kind), invalid_(false) { MOZ_ASSERT(uint32_t(encoding) < Codes::Total); } constexpr FloatRegister(Encoding encoding) : encoding_(encoding), kind_(FloatRegisters::Double), invalid_(false) { MOZ_ASSERT(uint32_t(encoding) < Codes::Total); } constexpr FloatRegister() : encoding_(FloatRegisters::invalid_reg), kind_(FloatRegisters::Double), invalid_(true) {} bool isSingle() const { MOZ_ASSERT(!invalid_); return kind_ == FloatRegisters::Single; } bool isDouble() const { MOZ_ASSERT(!invalid_); return kind_ == FloatRegisters::Double; } Encoding code() { return encoding_; } }; template <> inline FloatRegister::SetType FloatRegister::LiveAsIndexableSet(SetType set) { return set & FloatRegisters::AllSingleMask; } template <> inline FloatRegister::SetType FloatRegister::LiveAsIndexableSet(SetType set) { return set & FloatRegisters::AllDoubleMask; } template <> inline FloatRegister::SetType FloatRegister::LiveAsIndexableSet(SetType set) { return set; } inline bool hasUnaliasedDouble() { return false; } inline bool hasMultiAlias() { return false; } static const uint32_t ShadowStackSpace = 0; static const uint32_t JumpImmediateRange = INT32_MAX; #ifdef JS_NUNBOX32 static const int32_t NUNBOX32_TYPE_OFFSET = 4; static const int32_t NUNBOX32_PAYLOAD_OFFSET = 0; #endif static const uint32_t SpillSlotSize = std::max(sizeof(Registers::RegisterContent), sizeof(FloatRegisters::RegisterContent)); inline uint32_t GetRISCV64Flags() { return 0; } } // namespace jit } // namespace js #endif /* jit_riscv64_Architecture_riscv64_h */