diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/jit/riscv64/Architecture-riscv64.h | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/js/src/jit/riscv64/Architecture-riscv64.h b/js/src/jit/riscv64/Architecture-riscv64.h new file mode 100644 index 0000000000..e53273f2e2 --- /dev/null +++ b/js/src/jit/riscv64/Architecture-riscv64.h @@ -0,0 +1,513 @@ +/* -*- 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 <algorithm> +#include <iterator> + +#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 <typename T> +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 <RegTypeName Name = DefaultType> + static SetType LiveAsIndexableSet(SetType s) { + return SetType(0); + } + + template <RegTypeName Name = DefaultType> + static SetType AllocatableAsIndexableSet(SetType s) { + static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable"); + printf("AllocatableAsIndexableSet\n"); + return LiveAsIndexableSet<Name>(s); + } + + FloatRegister singleOverlay() const; + FloatRegister doubleOverlay() const; + + static TypedRegisterSet<FloatRegister> ReduceSetForPush( + const TypedRegisterSet<FloatRegister>& 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<FloatRegister>& 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<RegTypeName::Float32>(SetType set) { + return set & FloatRegisters::AllSingleMask; +} + +template <> +inline FloatRegister::SetType +FloatRegister::LiveAsIndexableSet<RegTypeName::Float64>(SetType set) { + return set & FloatRegisters::AllDoubleMask; +} + +template <> +inline FloatRegister::SetType +FloatRegister::LiveAsIndexableSet<RegTypeName::Any>(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 */ |