summaryrefslogtreecommitdiffstats
path: root/js/src/jit/riscv64/Architecture-riscv64.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/src/jit/riscv64/Architecture-riscv64.h
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit/riscv64/Architecture-riscv64.h')
-rw-r--r--js/src/jit/riscv64/Architecture-riscv64.h514
1 files changed, 514 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..c75bd05ff1
--- /dev/null
+++ b/js/src/jit/riscv64/Architecture-riscv64.h
@@ -0,0 +1,514 @@
+/* -*- 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");
+ 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_);
+ // On riscv64 arch, float register and double register using the same
+ // register file.
+ return kind_ == FloatRegisters::Single || kind_ == FloatRegisters::Double;
+ }
+ 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 */