22302 lines
726 KiB
Diff
22302 lines
726 KiB
Diff
# HG changeset patch
|
|
# User Cameron Kaiser <spectre@floodgap.com>
|
|
# Date 1723177432 25200
|
|
# Thu Aug 08 21:23:52 2024 -0700
|
|
# Node ID 9c245f4665be241b51f2b8b02ba90a054f55252f
|
|
# Parent 485b15bb4a20860aaa2d58a028fd09bc7b9638e0
|
|
ppc64 Baseline JIT
|
|
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/Architecture-ppc64.cpp
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/Architecture-ppc64.cpp Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,141 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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/. */
|
|
+
|
|
+#include "jit/ppc64/Architecture-ppc64.h"
|
|
+
|
|
+#include <fcntl.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#include "jit/RegisterSets.h"
|
|
+
|
|
+#if defined(XP_LINUX)
|
|
+// Use the getauxval() function if available.
|
|
+// ARCH_3_00 wasn't defined until glibc 2.23, so include just in case.
|
|
+# include <sys/auxv.h>
|
|
+# ifndef PPC_FEATURE2_ARCH_3_00
|
|
+# define PPC_FEATURE2_ARCH_3_00 0x00800000
|
|
+# endif
|
|
+#endif
|
|
+
|
|
+namespace js {
|
|
+namespace jit {
|
|
+
|
|
+// Don't bother checking if the JIT is statically enabled (ISA 3.0+ on LE).
|
|
+#if !defined(__POWER9_VECTOR__) || !defined(__LITTLE_ENDIAN__)
|
|
+bool
|
|
+HasPPCISA3() {
|
|
+ // This is duplicated from mozglue/build/ppc.* since mozglue for some
|
|
+ // reason can't be used here.
|
|
+
|
|
+ // The operation could be expensive, so cache the result.
|
|
+ static bool checked = false;
|
|
+ static bool result = false;
|
|
+
|
|
+ if (checked) {
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ checked = true;
|
|
+#if defined(XP_LINUX)
|
|
+ // Try getauxval().
|
|
+ unsigned long int cap = getauxval(AT_HWCAP);
|
|
+ unsigned long int cap2 = getauxval(AT_HWCAP2);
|
|
+
|
|
+ if ((cap & PPC_FEATURE_HAS_ALTIVEC) &&
|
|
+ (cap & PPC_FEATURE_HAS_VSX) &&
|
|
+ (cap2 & PPC_FEATURE2_ARCH_3_00)) {
|
|
+ result = true;
|
|
+ }
|
|
+#else
|
|
+ // Non-Linux detection here. Currently, on systems other than Linux,
|
|
+ // no CPU features will be detected.
|
|
+#endif
|
|
+
|
|
+ return result;
|
|
+}
|
|
+#endif
|
|
+
|
|
+void
|
|
+FlushICache(void* code, size_t size) {
|
|
+#if defined(__GNUC__)
|
|
+ intptr_t end = reinterpret_cast<intptr_t>(code) + size;
|
|
+ __builtin___clear_cache(reinterpret_cast<char*>(code),
|
|
+ reinterpret_cast<char*>(end));
|
|
+#else
|
|
+ _flush_cache(reinterpret_cast<char*>(code), size, BCACHE);
|
|
+#endif
|
|
+}
|
|
+
|
|
+// We do it on demand because we're service-oriented. Flags as a Service.
|
|
+bool CPUFlagsHaveBeenComputed() { return true; }
|
|
+
|
|
+Registers::Code
|
|
+Registers::FromName(const char *name)
|
|
+{
|
|
+ // Check for some register aliases first.
|
|
+ if (strcmp(name, "sp")==0 || strcmp(name, "r1")== 0)
|
|
+ return Code(1);
|
|
+ if (strcmp(name, "r12") == 0)
|
|
+ return Code(12);
|
|
+ if (strcmp(name, "r3") == 0)
|
|
+ return Code(3); // Dispatch, this is Floodgap, Code 3. Over.
|
|
+
|
|
+ for (uint32_t i = 0; i < Total; i++) {
|
|
+ if (strcmp(GetName(i), name) == 0)
|
|
+ return Code(i);
|
|
+ }
|
|
+
|
|
+ return Invalid;
|
|
+}
|
|
+
|
|
+FloatRegisters::Code
|
|
+FloatRegisters::FromName(const char *name)
|
|
+{
|
|
+ for (size_t i = 0; i < TotalPhys; i++) { // no alternate names
|
|
+ if (strcmp(GetName(i), name) == 0)
|
|
+ return Code(i); // thus double
|
|
+ }
|
|
+
|
|
+ return Invalid;
|
|
+}
|
|
+
|
|
+FloatRegisterSet FloatRegister::ReduceSetForPush(const FloatRegisterSet& s) {
|
|
+#ifdef ENABLE_WASM_SIMD
|
|
+# error "Needs more careful logic if SIMD is enabled"
|
|
+#endif
|
|
+
|
|
+ LiveFloatRegisterSet mod;
|
|
+ for (FloatRegisterIterator iter(s); iter.more(); ++iter) {
|
|
+ if ((*iter).isSingle()) {
|
|
+ // Even for floats, save a full double.
|
|
+ mod.addUnchecked((*iter).asDouble());
|
|
+ } else {
|
|
+ mod.addUnchecked(*iter);
|
|
+ }
|
|
+ }
|
|
+ return mod.set();
|
|
+}
|
|
+
|
|
+uint32_t FloatRegister::GetPushSizeInBytes(const FloatRegisterSet& s) {
|
|
+#ifdef ENABLE_WASM_SIMD
|
|
+# error "Needs more careful logic if SIMD is enabled"
|
|
+#endif
|
|
+
|
|
+ FloatRegisterSet ss = s.reduceSetForPush();
|
|
+ uint64_t bits = ss.bits();
|
|
+ // We only push double registers.
|
|
+ MOZ_ASSERT((bits & 0xffffffff00000000) == 0);
|
|
+ uint32_t ret = mozilla::CountPopulation32(bits) * sizeof(double);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+uint32_t FloatRegister::getRegisterDumpOffsetInBytes() {
|
|
+ return encoding() * sizeof(double);
|
|
+}
|
|
+
|
|
+} // namespace ion
|
|
+} // namespace js
|
|
+
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/Architecture-ppc64.h
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/Architecture-ppc64.h Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,645 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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_ppc_Architecture_ppc_h
|
|
+#define jit_ppc_Architecture_ppc_h
|
|
+
|
|
+#include "mozilla/Assertions.h"
|
|
+#include "mozilla/MathAlgorithms.h"
|
|
+
|
|
+#include "jit/shared/Architecture-shared.h"
|
|
+
|
|
+#include "js/Utility.h"
|
|
+
|
|
+namespace js {
|
|
+namespace jit {
|
|
+
|
|
+// Despite my hopes, this does not help protect Wasm Frames from ABI callouts
|
|
+// unknowingly stomping on them expecting a regular linkage area; the "shadow
|
|
+// stack space" that this allocates is actually allocated at *higher* addresses
|
|
+// than the Frame. The Frame demands to be on top of the stack, but that's
|
|
+// exactly where the linkage area is supposed to go, and everything assumes
|
|
+// that the Frame will be exactly two pointers in size which defeats my earlier
|
|
+// attempt to just add the linkage area to the Frame. (On top of that, Wasm GC
|
|
+// won't let you nab more than 32 bytes anyway, the bare minimum space required
|
|
+// for simply LR, TOC, CR and SP, and includes no parameter area.) Instead, for
|
|
+// now we have to tediously pull down dummy frames on demand when calling out
|
|
+// to heavy functions that are ABI-compliant. This also does nothing for the
|
|
+// regular JIT, where periodically we need to do the same thing.
|
|
+//
|
|
+// See also MacroAssembler::call(wasm::SymbolicAddress) in
|
|
+// MacroAssembler-ppc64.cpp.
|
|
+static const uint32_t ShadowStackSpace = 0;
|
|
+
|
|
+// The return address is in LR, not in memory/stack.
|
|
+static const uint32_t SizeOfReturnAddressAfterCall = 0u;
|
|
+
|
|
+// Size of each bailout table entry.
|
|
+// For Power ISA this is a single bl.
|
|
+static const uint32_t BAILOUT_TABLE_ENTRY_SIZE = sizeof(void *);
|
|
+
|
|
+// Range of an immediate jump (26 bit jumps). Take a fudge out in case.
|
|
+static constexpr uint32_t JumpImmediateRange = (32 * 1024 * 1024) - 32;
|
|
+
|
|
+// GPRs.
|
|
+class Registers
|
|
+{
|
|
+ public:
|
|
+ enum RegisterID {
|
|
+ r0 = 0,
|
|
+ tempRegister = r0,
|
|
+ r1,
|
|
+ sp = r1,
|
|
+ stackPointerRegister = r1,
|
|
+ r2,
|
|
+ r3,
|
|
+ r4,
|
|
+ r5,
|
|
+ r6,
|
|
+ r7,
|
|
+ r8,
|
|
+ r9,
|
|
+ r10,
|
|
+ r11,
|
|
+ r12,
|
|
+ addressTempRegister = r12,
|
|
+ r13,
|
|
+ r14,
|
|
+ r15,
|
|
+ r16,
|
|
+ r17,
|
|
+ r18,
|
|
+ r19,
|
|
+ r20,
|
|
+ r21,
|
|
+ r22,
|
|
+ r23,
|
|
+ r24,
|
|
+ r25,
|
|
+ r26,
|
|
+ r27,
|
|
+ r28,
|
|
+ r29,
|
|
+ r30,
|
|
+ r31,
|
|
+ invalid_reg
|
|
+ };
|
|
+
|
|
+ typedef uint8_t Code;
|
|
+ typedef uint32_t Encoding;
|
|
+ typedef uint32_t SetType;
|
|
+
|
|
+ // Content spilled during bailouts.
|
|
+ union RegisterContent {
|
|
+ uintptr_t r;
|
|
+ };
|
|
+
|
|
+ static const char *GetName(Code code) {
|
|
+ static const char *Names[] = {
|
|
+ "r0", "sp", "toc", "r3", "r4", "r5", "r6", "r7",
|
|
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
|
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
|
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"};
|
|
+ return Names[code];
|
|
+ }
|
|
+ static const char *GetName(uint32_t i) {
|
|
+ MOZ_ASSERT(i < Total);
|
|
+ return GetName(Code(i));
|
|
+ }
|
|
+
|
|
+ static Code FromName(const char *name);
|
|
+
|
|
+ static const Encoding StackPointer = sp;
|
|
+ static const Encoding Invalid = invalid_reg;
|
|
+
|
|
+ // XXX: Currently Safepoints restricts us to a uint32_t-sized non-FPR
|
|
+ // mask, so we can't work SPRs into this yet.
|
|
+ static const uint32_t Total = 32;
|
|
+ static const uint32_t Allocatable = 23;
|
|
+
|
|
+ static const SetType AllMask = 0xffffffff;
|
|
+ static const SetType ArgRegMask =
|
|
+ (1 << Registers::r3) |
|
|
+ (1 << Registers::r4) |
|
|
+ (1 << Registers::r5) |
|
|
+ (1 << Registers::r6) |
|
|
+ (1 << Registers::r7) |
|
|
+ (1 << Registers::r8) |
|
|
+ (1 << Registers::r9) |
|
|
+ (1 << Registers::r10);
|
|
+
|
|
+ // We use this constant to save registers when entering functions.
|
|
+ // Don't bother saving r0, r1, r11 or r12.
|
|
+ static const SetType VolatileMask = ArgRegMask;
|
|
+
|
|
+ static const SetType NonVolatileMask = (
|
|
+ (1 << Registers::r2) |
|
|
+ (1 << Registers::r13) |
|
|
+ (1 << Registers::r14) |
|
|
+ (1 << Registers::r15) |
|
|
+ (1 << Registers::r16) |
|
|
+ (1 << Registers::r17) |
|
|
+ (1 << Registers::r18) |
|
|
+ (1 << Registers::r19) |
|
|
+ (1 << Registers::r20) |
|
|
+ (1 << Registers::r21) |
|
|
+ (1 << Registers::r22) |
|
|
+ (1 << Registers::r23) |
|
|
+ (1 << Registers::r24) |
|
|
+ (1 << Registers::r25) |
|
|
+ (1 << Registers::r26) |
|
|
+ (1 << Registers::r27) |
|
|
+ (1 << Registers::r28) |
|
|
+ (1 << Registers::r29) |
|
|
+ (1 << Registers::r30) |
|
|
+ (1 << Registers::r31)
|
|
+ // Watch out for sign extension if this is ever 64-bit!
|
|
+ ) & AllMask;
|
|
+
|
|
+ // Also uses r11.
|
|
+ static const SetType WrapperMask = VolatileMask;
|
|
+
|
|
+ static const SetType NonAllocatableMask =
|
|
+ // Used by assembler.
|
|
+ (1 << Registers::r0) |
|
|
+ (1 << Registers::sp) |
|
|
+ (1 << Registers::r2) |
|
|
+ // Temp registers.
|
|
+ (1 << Registers::r11) |
|
|
+ (1 << Registers::r12) |
|
|
+ // r13 is the pointer for TLS in ELF v2.
|
|
+ (1 << Registers::r13) |
|
|
+ // Non-volatile work registers.
|
|
+ (1 << Registers::r16) |
|
|
+ // r17 is the InterpreterPCReg and must be allocatable.
|
|
+ // r18 is the WasmTlsReg and must be allocatable.
|
|
+ // Despite its use as a rectifier, r19 must be allocatable (see
|
|
+ // ICCallScriptedCompiler::generateStubCode).
|
|
+ // r28 used by trampoline for stack recovery.
|
|
+ (1 << Registers::r28) |
|
|
+ // r31 is the Frame Pointer and is not allocatable.
|
|
+ (1 << Registers::r31) |
|
|
+ 0;
|
|
+
|
|
+ // Registers that can be allocated without being saved, generally.
|
|
+ static const SetType TempMask = VolatileMask & ~NonAllocatableMask;
|
|
+
|
|
+ // Registers returned from a JS -> JS call.
|
|
+ static const SetType JSCallMask =
|
|
+ (1 << Registers::r5);
|
|
+
|
|
+ // Registers returned from a JS -> C call.
|
|
+ static const SetType CallMask =
|
|
+ (1 << Registers::r3);
|
|
+
|
|
+ static const SetType AllocatableMask = (
|
|
+ // Be explicit
|
|
+ (1 << Registers::r3) |
|
|
+ (1 << Registers::r4) |
|
|
+ (1 << Registers::r5) |
|
|
+ (1 << Registers::r6) |
|
|
+ (1 << Registers::r7) |
|
|
+ (1 << Registers::r8) |
|
|
+ (1 << Registers::r9) |
|
|
+ (1 << Registers::r10) |
|
|
+ (1 << Registers::r14) |
|
|
+ (1 << Registers::r15) |
|
|
+ (1 << Registers::r17) |
|
|
+ (1 << Registers::r18) |
|
|
+ (1 << Registers::r19) |
|
|
+ (1 << Registers::r20) |
|
|
+ (1 << Registers::r21) |
|
|
+ (1 << Registers::r22) |
|
|
+ (1 << Registers::r23) |
|
|
+ (1 << Registers::r24) |
|
|
+ //(1 << Registers::r25) |
|
|
+ (1 << Registers::r26) |
|
|
+ (1 << Registers::r27) |
|
|
+ //(1 << Registers::r28) |
|
|
+ (1 << Registers::r29) |
|
|
+ (1 << Registers::r30) |
|
|
+ //(1 << Registers::r31)
|
|
+ // Watch out for sign extension!
|
|
+ 0) & AllMask;
|
|
+
|
|
+ static uint32_t SetSize(SetType x) {
|
|
+ // XXX: see above
|
|
+ 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) {
|
|
+ // XXX: see above (31 would be 63 if we used uint64_t)
|
|
+ return 31 - mozilla::CountLeadingZeroes32(x);
|
|
+ }
|
|
+};
|
|
+
|
|
+// Smallest integer type that can hold a register bitmask. (GPRs.)
|
|
+// Safepoints asserts this is 32-bit or smaller.
|
|
+typedef uint32_t PackedRegisterMask;
|
|
+
|
|
+// FPRs.
|
|
+// PowerPC FPRs can be both double and single precision, like MIPS. We tell
|
|
+// Ion there are 64 FPRs, but each is an aliased pair.
|
|
+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_freg
|
|
+ };
|
|
+ static_assert(f31 == 31);
|
|
+ // Eight bits: (invalid << 7) | (kind << 5) | encoding
|
|
+ typedef uint8_t Code;
|
|
+ typedef FPRegisterID Encoding;
|
|
+ typedef uint64_t SetType;
|
|
+
|
|
+ // Make clear that the base type is Double; Single is a veneer upon it. XXX: VSX
|
|
+ enum Kind : uint8_t {
|
|
+ Double,
|
|
+ Single,
|
|
+ NumTypes
|
|
+ };
|
|
+
|
|
+ // Content spilled during bailouts.
|
|
+ union RegisterContent {
|
|
+ float s;
|
|
+ double d;
|
|
+ };
|
|
+ static const Code Invalid = 0x80;
|
|
+
|
|
+ static const char *GetName(Encoding code) {
|
|
+ static const char * const Names[] = { "f0", "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"};
|
|
+ MOZ_ASSERT(code < TotalPhys);
|
|
+ return Names[code];
|
|
+ }
|
|
+ static const char *GetName(Code i) {
|
|
+ MOZ_ASSERT(i < Total);
|
|
+ MOZ_ASSERT(i != Invalid);
|
|
+ return GetName(Encoding(i % TotalPhys));
|
|
+ }
|
|
+
|
|
+ static Code FromName(const char *name);
|
|
+
|
|
+
|
|
+ static const uint32_t TotalPhys = 32;
|
|
+ static const uint32_t NumScalarTypes = 2;
|
|
+ static const uint32_t Total = TotalPhys * NumScalarTypes;
|
|
+ static const uint32_t TotalWithSimd = TotalPhys * NumTypes;
|
|
+ static const uint32_t Allocatable = 31; // Without f0, the scratch register.
|
|
+
|
|
+ static_assert(sizeof(SetType) * 8 >= Total,
|
|
+ "SetType should be large enough to enumerate all registers.");
|
|
+
|
|
+ static const SetType SpreadSingle = SetType(1)
|
|
+ << (uint32_t(Single) * TotalPhys);
|
|
+ static const SetType SpreadDouble = SetType(1)
|
|
+ << (uint32_t(Double) * TotalPhys);
|
|
+ static const SetType Spread = SpreadSingle | SpreadDouble;
|
|
+
|
|
+ static const SetType AllPhysMask = (SetType(1) << TotalPhys) - 1;
|
|
+ static const SetType AllMask = AllPhysMask * Spread;
|
|
+ static const SetType AllDoubleMask = AllPhysMask * SpreadDouble;
|
|
+ static const SetType AllSingleMask = AllPhysMask * SpreadSingle;
|
|
+ static const SetType NoneMask = SetType(0);
|
|
+
|
|
+ // f0 is the ScratchFloatReg.
|
|
+ static const SetType VolatileMask =
|
|
+ SetType((1 << FloatRegisters::f1) | (1 << FloatRegisters::f2) |
|
|
+ (1 << FloatRegisters::f3) | (1 << FloatRegisters::f4) |
|
|
+ (1 << FloatRegisters::f5) | (1 << FloatRegisters::f6) |
|
|
+ (1 << FloatRegisters::f7) | (1 << FloatRegisters::f8) |
|
|
+ (1 << FloatRegisters::f9) | (1 << FloatRegisters::f10) |
|
|
+ (1 << FloatRegisters::f11) | (1 << FloatRegisters::f12) |
|
|
+ (1 << FloatRegisters::f13)) *
|
|
+ Spread;
|
|
+
|
|
+ static const SetType NonVolatileMask = AllMask & ~VolatileMask;
|
|
+
|
|
+ static const SetType WrapperMask = VolatileMask;
|
|
+
|
|
+ // d31 is the ScratchFloatReg.
|
|
+ static const SetType NonAllocatableMask =
|
|
+ (SetType(1) << FloatRegisters::f0) * Spread;
|
|
+
|
|
+ static const SetType AllocatableMask = AllMask & ~NonAllocatableMask;
|
|
+
|
|
+ static constexpr Encoding encoding(Code c) {
|
|
+ // assert() not available in constexpr function.
|
|
+ // assert(c < TotalWithSimd);
|
|
+ return Encoding(c & 31);
|
|
+ }
|
|
+
|
|
+ static constexpr Kind kind(Code c) {
|
|
+ // assert() not available in constexpr function.
|
|
+ // assert(c < TotalWithSimd && ((c >> 5) & 3) < NumTypes);
|
|
+ return Kind((c >> 5) & 3);
|
|
+ }
|
|
+
|
|
+ static constexpr Code fromParts(uint32_t encoding, uint32_t kind,
|
|
+ uint32_t invalid) {
|
|
+ return Code((invalid << 7) | (kind << 5) | encoding);
|
|
+ }
|
|
+};
|
|
+
|
|
+template <typename T>
|
|
+class TypedRegisterSet;
|
|
+
|
|
+// Shamelessly rip off aargh, um, aarch64
|
|
+struct FloatRegister {
|
|
+ typedef FloatRegisters Codes;
|
|
+ typedef Codes::Code Code;
|
|
+ typedef Codes::Encoding Encoding;
|
|
+ typedef Codes::SetType SetType;
|
|
+ typedef Codes::Kind Kind;
|
|
+
|
|
+ static uint32_t SetSize(SetType x) {
|
|
+ static_assert(sizeof(SetType) == 8, "SetType must be 64 bits");
|
|
+ x |= x >> FloatRegisters::TotalPhys;
|
|
+ x &= FloatRegisters::AllPhysMask;
|
|
+ return mozilla::CountPopulation32(x);
|
|
+ }
|
|
+
|
|
+ static uint32_t FirstBit(SetType x) {
|
|
+ static_assert(sizeof(SetType) == 8, "SetType");
|
|
+ return mozilla::CountTrailingZeroes64(x);
|
|
+ }
|
|
+ static uint32_t LastBit(SetType x) {
|
|
+ static_assert(sizeof(SetType) == 8, "SetType");
|
|
+ return 63 - mozilla::CountLeadingZeroes64(x);
|
|
+ }
|
|
+
|
|
+ static constexpr size_t SizeOfSimd128 = 16;
|
|
+
|
|
+ private:
|
|
+ // These fields only hold valid values: an invalid register is always
|
|
+ // represented as a valid encoding and kind with the invalid_ bit set.
|
|
+ uint8_t encoding_; // 32 encodings
|
|
+ uint8_t kind_; // Double, Single, Simd128
|
|
+ bool invalid_;
|
|
+
|
|
+ public:
|
|
+ constexpr FloatRegister(Encoding encoding, Kind kind = FloatRegisters::Double)
|
|
+ : encoding_(encoding), kind_(kind), invalid_(false) {
|
|
+ // assert(uint32_t(encoding) < Codes::TotalPhys);
|
|
+ }
|
|
+ constexpr FloatRegister(uint32_t r, Kind kind = FloatRegisters::Double)
|
|
+ : encoding_(Codes::Encoding(r)), kind_(kind), invalid_(false) {}
|
|
+ constexpr FloatRegister()
|
|
+ : encoding_(0), kind_(FloatRegisters::Double), invalid_(true) {}
|
|
+
|
|
+ static FloatRegister FromCode(uint32_t i) {
|
|
+ MOZ_ASSERT(i < Codes::TotalWithSimd);
|
|
+ return FloatRegister(FloatRegisters::encoding(i), FloatRegisters::kind(i));
|
|
+ }
|
|
+
|
|
+ bool isSingle() const {
|
|
+ MOZ_ASSERT(!invalid_);
|
|
+ return kind_ == FloatRegisters::Single;
|
|
+ }
|
|
+ bool isDouble() const {
|
|
+ MOZ_ASSERT(!invalid_);
|
|
+ return kind_ == FloatRegisters::Double;
|
|
+ }
|
|
+ bool isSimd128() const {
|
|
+ MOZ_ASSERT(!invalid_);
|
|
+ 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("No SIMD support");
|
|
+ }
|
|
+
|
|
+ constexpr uint32_t size() const {
|
|
+ MOZ_ASSERT(!invalid_);
|
|
+ if (kind_ == FloatRegisters::Double) {
|
|
+ return sizeof(double);
|
|
+ }
|
|
+ if (kind_ == FloatRegisters::Single) {
|
|
+ return sizeof(float); // not used in stack calculations, apparently
|
|
+ }
|
|
+ MOZ_CRASH("No SIMD support");
|
|
+ }
|
|
+
|
|
+ constexpr Code code() const {
|
|
+ // assert(!invalid_);
|
|
+ return Codes::fromParts(encoding_, kind_, invalid_);
|
|
+ }
|
|
+
|
|
+ constexpr Encoding encoding() const {
|
|
+ MOZ_ASSERT(!invalid_);
|
|
+ return Encoding(encoding_);
|
|
+ }
|
|
+
|
|
+ const char* name() const { return FloatRegisters::GetName(code()); }
|
|
+ bool volatile_() const {
|
|
+ MOZ_ASSERT(!invalid_);
|
|
+ return !!((SetType(1) << code()) & FloatRegisters::VolatileMask);
|
|
+ }
|
|
+ constexpr bool operator!=(FloatRegister other) const {
|
|
+ return code() != other.code();
|
|
+ }
|
|
+ constexpr bool operator==(FloatRegister other) const {
|
|
+ return code() == other.code();
|
|
+ }
|
|
+
|
|
+ bool aliases(FloatRegister other) const {
|
|
+ return other.encoding_ == encoding_;
|
|
+ }
|
|
+ // All spades are groovy. -- Firesign Theatre
|
|
+ bool equiv(FloatRegister other) const {
|
|
+ MOZ_ASSERT(!invalid_);
|
|
+ return kind_ == other.kind_;
|
|
+ }
|
|
+
|
|
+ // numAliased is used only by Ion's register allocator, ergo we ignore SIMD
|
|
+ // registers here as Ion will not be exposed to SIMD on this platform.
|
|
+ uint32_t numAliased() const { return Codes::NumScalarTypes; }
|
|
+ uint32_t numAlignedAliased() { return numAliased(); }
|
|
+
|
|
+ FloatRegister aliased(uint32_t aliasIdx) {
|
|
+ MOZ_ASSERT(!invalid_);
|
|
+ MOZ_ASSERT(aliasIdx < numAliased());
|
|
+ return FloatRegister(Encoding(encoding_),
|
|
+ Kind((aliasIdx + kind_) % numAliased()));
|
|
+ }
|
|
+ FloatRegister alignedAliased(uint32_t aliasIdx) {
|
|
+ MOZ_ASSERT(aliasIdx < numAliased());
|
|
+ return aliased(aliasIdx);
|
|
+ }
|
|
+ SetType alignedOrDominatedAliasedSet() const {
|
|
+ return Codes::Spread << encoding_;
|
|
+ }
|
|
+
|
|
+ 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);
|
|
+ }
|
|
+
|
|
+ static TypedRegisterSet<FloatRegister> ReduceSetForPush(
|
|
+ const TypedRegisterSet<FloatRegister>& s);
|
|
+ static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister>& s);
|
|
+ uint32_t getRegisterDumpOffsetInBytes();
|
|
+};
|
|
+
|
|
+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; }
|
|
+
|
|
+// XXX: This needs to be rolled into somewhere else
|
|
+
|
|
+// SPRs (PPC backend specific).
|
|
+// These have no peer in lesser chips. That is because PPC has no peer in
|
|
+// lesser chips. These don't count against the register cap because the
|
|
+// allocator is unaware of them. In fact, we don't treat these as regular
|
|
+// registers at all (hey, they're Special Purpose anyway).
|
|
+#if(0)
|
|
+class SPRs // Class definition not yet supported.
|
|
+{
|
|
+ public:
|
|
+#endif
|
|
+ enum SPRegisterID {
|
|
+ xer = 1,
|
|
+ lr_spr = 8, /* I'm in the REAL witness protection program. */
|
|
+ ctr = 9,
|
|
+ vrsave = 256, /* for future SIMD JS */
|
|
+ invalid_spreg
|
|
+ };
|
|
+#if(0)
|
|
+ static const char *getSPRName(SPRegisterID code) {
|
|
+#define XXX "INVALID"
|
|
+ static const char *N_vrsave = "vrsave";
|
|
+ static const char *N_bogus = XXX;
|
|
+ static const char *Names[] = {
|
|
+ XXX, "xer", XXX, XXX, XXX, XXX, XXX, XXX,
|
|
+ "lr","ctr"
|
|
+ };
|
|
+#undef XXX
|
|
+ return
|
|
+ (code == vrsave) ? N_vrsave :
|
|
+ (code > ctr) ? N_bogus :
|
|
+ Names[code];
|
|
+ }
|
|
+};
|
|
+#endif
|
|
+
|
|
+// CRs (PPC backend specific).
|
|
+// We have eight condition registers, each for how unconditionally wonderful
|
|
+// PowerPC is, and sometimes for storing condition results.
|
|
+// Assume CR0 as default.
|
|
+#if(0)
|
|
+class CRs // Class definition not yet supported.
|
|
+{
|
|
+ public:
|
|
+#endif
|
|
+ enum CRegisterID {
|
|
+ cr0 = 0,
|
|
+ cr1,
|
|
+/*
|
|
+Suppress CR2-CR4 because these are non-volatile.
|
|
+ cr2,
|
|
+ cr3,
|
|
+ cr4,
|
|
+*/
|
|
+ cr5 = 5,
|
|
+ cr6,
|
|
+ cr7,
|
|
+ invalid_creg
|
|
+ };
|
|
+#if(0)
|
|
+ static const char *getCRName(CRegisterID code) {
|
|
+ static const char *Names[] = {
|
|
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7"
|
|
+ };
|
|
+ return Names[code];
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+inline uint32_t GetPPC64Flags() { return 0; /* XXX? */ }
|
|
+
|
|
+// Statically true if --mcpu=power9.
|
|
+#if defined(__POWER9_VECTOR__) && defined(__LITTLE_ENDIAN__)
|
|
+inline bool HasPPCISA3() { return true; }
|
|
+#else
|
|
+bool HasPPCISA3();
|
|
+#endif
|
|
+
|
|
+} // namespace jit
|
|
+} // namespace js
|
|
+
|
|
+#endif /* jit_ppc_Architecture_ppc_h */
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/Assembler-ppc64.cpp
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/Assembler-ppc64.cpp Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,2048 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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/. */
|
|
+
|
|
+#include "jit/ppc64/Assembler-ppc64.h"
|
|
+#include "mozilla/DebugOnly.h"
|
|
+#include "jit/AutoWritableJitCode.h"
|
|
+#include "jit/FlushICache.h"
|
|
+
|
|
+
|
|
+#if DEBUG
|
|
+#define spew(...) JitSpew(JitSpew_Codegen, __VA_ARGS__)
|
|
+#else
|
|
+#define spew(...)
|
|
+#endif
|
|
+
|
|
+using mozilla::DebugOnly;
|
|
+
|
|
+using namespace js;
|
|
+using namespace js::jit;
|
|
+
|
|
+ABIArgGenerator::ABIArgGenerator()
|
|
+ : stackOffset_(0),
|
|
+ usedGPRs_(0),
|
|
+ usedFPRs_(0),
|
|
+ current_()
|
|
+{}
|
|
+
|
|
+// This is used for inter-Wasm procedure calls as well as regular ABI calls,
|
|
+// so when we're compiling Wasm we can "expand" the ABI.
|
|
+// However, we must not do anything that assumes the presence of an argument
|
|
+// area, since our variant Frame doesn't have one.
|
|
+
|
|
+ABIArg
|
|
+ABIArgGenerator::next(MIRType type)
|
|
+{
|
|
+ switch (type) {
|
|
+ case MIRType::Int32:
|
|
+ case MIRType::Int64:
|
|
+ case MIRType::Pointer:
|
|
+ case MIRType::WasmAnyRef:
|
|
+ case MIRType::StackResults: {
|
|
+ if (usedGPRs_ > 7) {
|
|
+ // We only support spilling arguments to the stack with Wasm calls,
|
|
+ // but we could be generating Wasm code from the interpreter, so
|
|
+ // we can't assume there is a JIT context available.
|
|
+ MOZ_ASSERT(!MaybeGetJitContext() || IsCompilingWasm(), "no stack corruption from GPR overflow kthxbye");
|
|
+ current_ = ABIArg(stackOffset_);
|
|
+ stackOffset_ += sizeof(uintptr_t);
|
|
+ break;
|
|
+ }
|
|
+ // Note: we could be passing a full 64-bit quantity as an argument to,
|
|
+ // say, uint32_t. We have to compensate for that in other ways when
|
|
+ // it makes a difference (see notes in wasm).
|
|
+ current_ = ABIArg(Register::FromCode((Register::Code)(usedGPRs_ + 3)));
|
|
+ usedGPRs_++;
|
|
+ break;
|
|
+ }
|
|
+ case MIRType::Float32:
|
|
+ case MIRType::Double: {
|
|
+ if (usedFPRs_ == 12) {
|
|
+ MOZ_ASSERT(!MaybeGetJitContext() || IsCompilingWasm(), "no stack corruption from FPR overflow kthxbye");
|
|
+ current_ = ABIArg(stackOffset_);
|
|
+ stackOffset_ += sizeof(double); // keep stack aligned to double
|
|
+ break;
|
|
+ }
|
|
+ current_ = ABIArg(FloatRegister(FloatRegisters::Encoding(usedFPRs_ + 1),
|
|
+ type == MIRType::Double ? FloatRegisters::Double : FloatRegisters::Single));
|
|
+ usedGPRs_++;
|
|
+ usedFPRs_++;
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ MOZ_CRASH("Unexpected argument type");
|
|
+ }
|
|
+ return current_;
|
|
+}
|
|
+
|
|
+uintptr_t
|
|
+Assembler::GetPointer(uint8_t* instPtr)
|
|
+{
|
|
+ Instruction* inst = (Instruction*)instPtr;
|
|
+ return Assembler::ExtractLoad64Value(inst);
|
|
+}
|
|
+
|
|
+static JitCode *
|
|
+CodeFromJump(Instruction* jump)
|
|
+{
|
|
+ uint8_t* target = (uint8_t*)Assembler::ExtractLoad64Value(jump);
|
|
+ return JitCode::FromExecutable(target);
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader)
|
|
+{
|
|
+ while (reader.more()) {
|
|
+ JitCode* child = CodeFromJump((Instruction*)(code->raw() + reader.readUnsigned()));
|
|
+ TraceManuallyBarrieredEdge(trc, &child, "rel32");
|
|
+ }
|
|
+}
|
|
+
|
|
+#if(0) // old code
|
|
+static void
|
|
+TraceOneDataRelocation(JSTracer* trc, Instruction* inst)
|
|
+{
|
|
+ void* ptr = (void*)Assembler::ExtractLoad64Value(inst);
|
|
+ void* prior = ptr;
|
|
+
|
|
+ // All pointers will have the top bits clear. If those bits
|
|
+ // are not cleared, this must be a Value.
|
|
+ uintptr_t word = reinterpret_cast<uintptr_t>(ptr);
|
|
+ if (word >> JSVAL_TAG_SHIFT) {
|
|
+ Value v = Value::fromRawBits(word);
|
|
+ TraceManuallyBarrieredEdge(trc, &v, "ion-masm-value");
|
|
+ ptr = (void*)v.bitsAsPunboxPointer();
|
|
+ } else {
|
|
+ // No barrier needed since these are constants.
|
|
+ TraceManuallyBarrieredGenericPointerEdge(trc, reinterpret_cast<gc::Cell**>(&ptr),
|
|
+ "ion-masm-ptr");
|
|
+ }
|
|
+
|
|
+ if (ptr != prior) {
|
|
+ Assembler::UpdateLoad64Value(inst, uint64_t(ptr));
|
|
+ FlushICache(inst, 5 * sizeof(uint32_t));
|
|
+ }
|
|
+}
|
|
+
|
|
+/* static */ void
|
|
+Assembler::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader)
|
|
+{
|
|
+ mozilla::Maybe<AutoWritableJitCode> awjc;
|
|
+
|
|
+ while (reader.more()) {
|
|
+ size_t offset = reader.readUnsigned();
|
|
+ Instruction* inst = (Instruction*)(code->raw() + offset);
|
|
+ if (awjc.isNothing()) {
|
|
+ awjc.emplace(code);
|
|
+ }
|
|
+ TraceOneDataRelocation(trc, inst);
|
|
+ }
|
|
+}
|
|
+#else
|
|
+static void TraceOneDataRelocation(JSTracer* trc,
|
|
+ mozilla::Maybe<AutoWritableJitCode>& awjc,
|
|
+ JitCode* code, Instruction* inst) {
|
|
+ void* ptr = (void*)Assembler::ExtractLoad64Value(inst);
|
|
+ void* prior = ptr;
|
|
+
|
|
+ // Data relocations can be for Values or for raw pointers. If a Value is
|
|
+ // zero-tagged, we can trace it as if it were a raw pointer. If a Value
|
|
+ // is not zero-tagged, we have to interpret it as a Value to ensure that the
|
|
+ // tag bits are masked off to recover the actual pointer.
|
|
+ uintptr_t word = reinterpret_cast<uintptr_t>(ptr);
|
|
+ if (word >> JSVAL_TAG_SHIFT) {
|
|
+ // This relocation is a Value with a non-zero tag.
|
|
+ Value v = Value::fromRawBits(word);
|
|
+ TraceManuallyBarrieredEdge(trc, &v, "jit-masm-value");
|
|
+ ptr = (void*)v.bitsAsPunboxPointer();
|
|
+ } else {
|
|
+ // This relocation is a raw pointer or a Value with a zero tag.
|
|
+ // No barrier needed since these are constants.
|
|
+ TraceManuallyBarrieredGenericPointerEdge(
|
|
+ trc, reinterpret_cast<gc::Cell**>(&ptr), "jit-masm-ptr");
|
|
+ }
|
|
+
|
|
+ if (ptr != prior) {
|
|
+ if (awjc.isNothing()) {
|
|
+ awjc.emplace(code);
|
|
+ }
|
|
+ Assembler::UpdateLoad64Value(inst, uint64_t(ptr));
|
|
+ FlushICache(inst, 5 * sizeof(uint32_t));
|
|
+ }
|
|
+}
|
|
+
|
|
+/* static */
|
|
+void Assembler::TraceDataRelocations(JSTracer* trc, JitCode* code,
|
|
+ CompactBufferReader& reader) {
|
|
+ mozilla::Maybe<AutoWritableJitCode> awjc;
|
|
+ while (reader.more()) {
|
|
+ size_t offset = reader.readUnsigned();
|
|
+ Instruction* inst = (Instruction*)(code->raw() + offset);
|
|
+ TraceOneDataRelocation(trc, awjc, code, inst);
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+void
|
|
+Assembler::Bind(uint8_t* rawCode, const CodeLabel& label)
|
|
+{
|
|
+ if (label.patchAt().bound()) {
|
|
+ auto mode = label.linkMode();
|
|
+ intptr_t offset = label.patchAt().offset();
|
|
+ intptr_t target = label.target().offset();
|
|
+ spew("# Bind mode=%d rawCode=%p offset=%lx target=%lx", (int)mode, rawCode, offset, target);
|
|
+
|
|
+ if (mode == CodeLabel::RawPointer) {
|
|
+ *reinterpret_cast<const void**>(rawCode + offset) = rawCode + target;
|
|
+ } else {
|
|
+ MOZ_ASSERT(mode == CodeLabel::MoveImmediate || mode == CodeLabel::JumpImmediate);
|
|
+ Instruction* inst = (Instruction*) (rawCode + offset);
|
|
+ Assembler::UpdateLoad64Value(inst, (uint64_t)(rawCode + target));
|
|
+ FlushICache(inst, 5 * sizeof(uint32_t));
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::bind(InstImm* inst, uintptr_t b, uintptr_t target, bool bound)
|
|
+{
|
|
+#if 0 // TODO: Assembler::bind()
|
|
+ int64_t offset = target - branch;
|
|
+ InstImm inst_bgezal = InstImm(op_regimm, r0, rt_bgezal, BOffImm16(0));
|
|
+ InstImm inst_beq = InstImm(PPC_bc, r0, r0, BOffImm16(0));
|
|
+
|
|
+ // If encoded offset is 4, then the jump must be short
|
|
+ if (BOffImm16(inst[0]).decode() == 4) {
|
|
+ MOZ_ASSERT(BOffImm16::IsInRange(offset));
|
|
+ inst[0].setBOffImm16(BOffImm16(offset));
|
|
+ inst[1].makeOp_nop();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Generate the long jump for calls because return address has to be the
|
|
+ // address after the reserved block.
|
|
+ if (inst[0].encode() == inst_bgezal.encode()) {
|
|
+ addLongJump(BufferOffset(branch));
|
|
+ Assembler::WriteLoad64Instructions(inst, ScratchRegister, LabelBase::INVALID_OFFSET);
|
|
+ inst[4] = InstReg(PPC_b | LinkB, ScratchRegister, r0).encode();
|
|
+ // There is 1 nop after this.
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (BOffImm16::IsInRange(offset)) {
|
|
+ // Don't skip trailing nops can improve performance
|
|
+ // on Loongson3 platform.
|
|
+ bool skipNops = (inst[0].encode() != inst_bgezal.encode() &&
|
|
+ inst[0].encode() != inst_beq.encode());
|
|
+
|
|
+ inst[0].setBOffImm16(BOffImm16(offset));
|
|
+ inst[1].makeOp_nop();
|
|
+
|
|
+ if (skipNops) {
|
|
+ inst[2] = InstImm(op_regimm, r0, rt_bgez, BOffImm16(5 * sizeof(uint32_t))).encode();
|
|
+ // There are 4 nops after this
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (inst[0].encode() == inst_beq.encode()) {
|
|
+ // Handle long unconditional jump.
|
|
+ addLongJump(BufferOffset(branch));
|
|
+ Assembler::WriteLoad64Instructions(inst, ScratchRegister, LabelBase::INVALID_OFFSET);
|
|
+ inst[4] = InstReg(op_special, ScratchRegister, r0, r0, ff_jr).encode();
|
|
+ // There is 1 nop after this.
|
|
+ } else {
|
|
+ // Handle long conditional jump.
|
|
+ inst[0] = invertBranch(inst[0], BOffImm16(7 * sizeof(uint32_t)));
|
|
+ // No need for a "nop" here because we can clobber scratch.
|
|
+ addLongJump(BufferOffset(branch + sizeof(uint32_t)));
|
|
+ Assembler::WriteLoad64Instructions(&inst[1], ScratchRegister, LabelBase::INVALID_OFFSET);
|
|
+ inst[5] = InstReg(op_special, ScratchRegister, r0, r0, ff_jr).encode();
|
|
+ // There is 1 nop after this.
|
|
+ }
|
|
+#endif
|
|
+ int64_t offset = target - b;
|
|
+ spew("# bind %lx %lx (instruction: %08x)", b, target, inst[0].encode());
|
|
+ MOZ_ASSERT(!(offset & 3));
|
|
+
|
|
+ if (inst[0].isOpcode(PPC_addis)) { // pre-existing long stanza
|
|
+ spew("# pending long jump");
|
|
+ addLongJump(BufferOffset(b), BufferOffset(target));
|
|
+ } else if (inst[0].isOpcode(PPC_tw)) { // tagged trap
|
|
+ TrapTag t = inst[0].traptag();
|
|
+
|
|
+ if (t == BCTag) {
|
|
+ // Reverse-sense long stanza:
|
|
+ // bc inverted, fail
|
|
+ // tagged trap << inst[0] patch to lis
|
|
+ // .long next_in_chain patch to ori
|
|
+ // nop patch to rldicr
|
|
+ // nop patch to oris
|
|
+ // nop patch to ori
|
|
+ // nop patch to mtctr
|
|
+ // nop patch to bctr
|
|
+
|
|
+ // The negative index is required because of an invariant in ::retarget() that
|
|
+ // always expects the next-in-chain word in slot 1 (any value there could be
|
|
+ // potentially valid, including a trap word, so no sentinel value can
|
|
+ // disambiguate).
|
|
+ MOZ_ASSERT(inst[-1].isOpcode(PPC_bc));
|
|
+ // I always like a clean stanza.
|
|
+ MOZ_ASSERT(inst[2].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[3].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[4].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[5].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[6].encode() == PPC_nop);
|
|
+
|
|
+ // If this was actually assigned, see if it's a short jump after all.
|
|
+ if (bound && BOffImm16::IsInSignedRange(offset + sizeof(uint32_t))) { // see below
|
|
+ // It's a short jump after all.
|
|
+ // It's a short jump after all.
|
|
+ // It's a short jump after all.
|
|
+ // It's a short, short jump.
|
|
+ // Patch bc directly by inverting the sense, adding an instruction to
|
|
+ // offset the negative index.
|
|
+
|
|
+ spew("# writing in long jump as short bc");
|
|
+ // Make sure we're not going to patch in the wrong place.
|
|
+ MOZ_ASSERT(inst[7].encode() != PPC_bctr);
|
|
+ // Weirdo instructions like bdnz shouldn't come through here, just any
|
|
+ // bc with a BO of 0x04 or 0x0c (i.e., CR bit set or CR bit not set).
|
|
+ MOZ_ASSERT((inst[-1].encode() & 0x03e00000) == 0x00800000 ||
|
|
+ (inst[-1].encode() & 0x03e00000) == 0x01800000);
|
|
+ // XXX: should invert likely bits, too.
|
|
+ inst[-1].setData(((inst[-1].encode() ^ 0x01000000) & (0xffff0003)) | BOffImm16(offset+sizeof(uint32_t)).encode());
|
|
+ inst[0].setData(PPC_nop); // obliterate tagged trap
|
|
+ inst[1].setData(PPC_nop); // obliterate next in chain
|
|
+ } else if (bound && JOffImm26::IsInRange(offset)) {
|
|
+ // It's a short(er) jump after all.
|
|
+ // It's a short(er) jump after all.
|
|
+ // It's ... why did you pick up that chainsaw?
|
|
+ // Why is it running?
|
|
+
|
|
+ spew("# writing in long jump as short bc/b");
|
|
+ // Make sure we're not going to patch in the wrong place.
|
|
+ MOZ_ASSERT(inst[7].encode() != PPC_bctr);
|
|
+ inst[0].setData(PPC_b | JOffImm26(offset).encode());
|
|
+ inst[1].setData(PPC_nop); // obliterate next in chain
|
|
+ } else {
|
|
+ // Dang, no Disney songs for this.
|
|
+ // Although this should be to Ion code, use r12 to keep calls "as expected."
|
|
+
|
|
+ spew("# writing in and pending long bc");
|
|
+ addLongJump(BufferOffset(b), BufferOffset(target));
|
|
+ Assembler::WriteLoad64Instructions(inst, SecondScratchReg, LabelBase::INVALID_OFFSET);
|
|
+ inst[5].makeOp_mtctr(SecondScratchReg);
|
|
+ inst[6].makeOp_bctr(DontLinkB);
|
|
+ }
|
|
+ } else if (t == CallTag) {
|
|
+ // I wanna taste pizzazz, all the taste clean Stanza has!
|
|
+ // I wanna clean, I wanna ... Stan-za.
|
|
+ MOZ_ASSERT(inst[2].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[3].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[4].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[5].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[6].encode() == PPC_nop);
|
|
+
|
|
+ // And I get to sing Disney songs again!
|
|
+ // See if it's a short jump after all!
|
|
+ if (bound && JOffImm26::IsInRange(offset - 24)) {
|
|
+ // It's a short jump after all!
|
|
+ // It's a short #${{@~NO CARRIER
|
|
+ spew("# writing in short call");
|
|
+ // Make sure we're not going to patch in the wrong place.
|
|
+ MOZ_ASSERT(inst[7].encode() != PPC_bctr);
|
|
+ inst[0].setData(PPC_nop); // obliterate trap
|
|
+ inst[1].setData(PPC_nop); // obliterate next-in-chain
|
|
+ // So that the return is after the stanza, the link call
|
|
+ // must be the last instruction in the stanza, not the
|
|
+ // first. This means we also need to adjust the offset,
|
|
+ // which is where the 24 comes from (stanza length minus
|
|
+ // the bl instruction).
|
|
+ offset -= 24;
|
|
+ inst[6].setData(PPC_b | JOffImm26(offset).encode() | LinkB);
|
|
+ } else {
|
|
+ // Why doesn't anyone like my singing?
|
|
+ spew("# writing in and pending long call");
|
|
+ addLongJump(BufferOffset(b), BufferOffset(target));
|
|
+ Assembler::WriteLoad64Instructions(inst, SecondScratchReg, LabelBase::INVALID_OFFSET);
|
|
+ inst[5].makeOp_mtctr(SecondScratchReg);
|
|
+ inst[6].makeOp_bctr(LinkB);
|
|
+ }
|
|
+ } else if (t == BTag) {
|
|
+ // More or less a degenerate case of BCTag. But do they let me
|
|
+ // sing Disney songs about that? Noooooooooo. They said it was
|
|
+ // an HR violation and would summon lawyers from their undead
|
|
+ // crypts to lay waste upon the earth. Wimps.
|
|
+ MOZ_ASSERT(inst[2].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[3].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[4].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[5].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[6].encode() == PPC_nop);
|
|
+
|
|
+ if (bound && JOffImm26::IsInRange(offset)) {
|
|
+ spew("# writing in short b");
|
|
+ // Make sure we're not going to patch in the wrong place.
|
|
+ MOZ_ASSERT(inst[7].encode() != PPC_bctr);
|
|
+ // The branch, in this case, really is in slot 0.
|
|
+ inst[0].setData(PPC_b | JOffImm26(offset).encode());
|
|
+ inst[1].setData(PPC_nop); // obliterate next in chain
|
|
+ } else {
|
|
+ spew("# writing in and pending long b");
|
|
+ addLongJump(BufferOffset(b), BufferOffset(target));
|
|
+ Assembler::WriteLoad64Instructions(inst, SecondScratchReg, LabelBase::INVALID_OFFSET);
|
|
+ inst[5].makeOp_mtctr(SecondScratchReg);
|
|
+ inst[6].makeOp_bctr(DontLinkB);
|
|
+ }
|
|
+ } else {
|
|
+ MOZ_CRASH("unhandled trap in slot 0");
|
|
+ }
|
|
+ } else if (inst[0].isOpcode(PPC_b)) {
|
|
+ // Short jump emitted by ma_b. Set the b target and nop out the next-in-chain.
|
|
+ spew("# setting short b");
|
|
+ MOZ_ASSERT(JOffImm26::IsInRange(offset));
|
|
+ inst[0].setData(PPC_b | JOffImm26(offset).encode());
|
|
+ inst[1].setData(PPC_nop); // obliterate next-in-chain
|
|
+ } else if (inst[0].isOpcode(PPC_bc)) {
|
|
+ // Short jump emitted by ma_bc. Set the bc target and nop out the next-in-chain.
|
|
+ spew("# setting short bc");
|
|
+ MOZ_ASSERT(BOffImm16::IsInSignedRange(offset));
|
|
+ inst[0].setData((inst[0].encode() & 0xffff0000) | BOffImm16(offset).encode());
|
|
+ inst[1].setData(PPC_nop); // obliterate next-in-chain
|
|
+ } else {
|
|
+ MOZ_CRASH("Unhandled bind()");
|
|
+ }
|
|
+
|
|
+#if 0
|
|
+ InstImm inst_beq = InstImm(op_beq, r0, r0, BOffImm16(0));
|
|
+ uint64_t offset = dest.getOffset() - label->offset();
|
|
+
|
|
+ // If first instruction is lui, then this is a long jump.
|
|
+ // If second instruction is lui, then this is a loop backedge.
|
|
+ if (inst[0].extractOpcode() == (uint32_t(op_lui) >> OpcodeShift)) {
|
|
+ // For unconditional long branches generated by ma_liPatchable,
|
|
+ // such as under:
|
|
+ // jumpWithpatch
|
|
+ addLongJump(BufferOffset(label->offset()));
|
|
+ } else if (inst[1].extractOpcode() == (uint32_t(op_lui) >> OpcodeShift) ||
|
|
+ BOffImm16::IsInRange(offset))
|
|
+ {
|
|
+ // Handle code produced by:
|
|
+ // backedgeJump
|
|
+ MOZ_ASSERT(BOffImm16::IsInRange(offset));
|
|
+ MOZ_ASSERT(inst[0].extractOpcode() == (uint32_t(op_beq) >> OpcodeShift) ||
|
|
+ inst[0].extractOpcode() == (uint32_t(op_bne) >> OpcodeShift) ||
|
|
+ inst[0].extractOpcode() == (uint32_t(op_blez) >> OpcodeShift) ||
|
|
+ inst[0].extractOpcode() == (uint32_t(op_bgtz) >> OpcodeShift) ||
|
|
+ (inst[0].extractOpcode() == (uint32_t(op_regimm) >> OpcodeShift) &&
|
|
+ inst[0].extractRT() == (uint32_t(rt_bltz) >> RTShift)));
|
|
+ inst[0].setBOffImm16(BOffImm16(offset));
|
|
+ } else if (inst[0].encode() == inst_beq.encode()) {
|
|
+ // Handle open long unconditional jumps created by
|
|
+ // MacroAssemblerMIPSShared::ma_bc(..., wasm::Trap, ...).
|
|
+ // We need to add it to long jumps array here.
|
|
+ MOZ_ASSERT(inst[1].encode() == NopInst);
|
|
+ MOZ_ASSERT(inst[2].encode() == NopInst);
|
|
+ MOZ_ASSERT(inst[3].encode() == NopInst);
|
|
+ MOZ_ASSERT(inst[4].encode() == NopInst);
|
|
+ MOZ_ASSERT(inst[5].encode() == NopInst);
|
|
+ addLongJump(BufferOffset(label->offset()));
|
|
+ Assembler::WriteLoad64Instructions(inst, ScratchRegister, LabelBase::INVALID_OFFSET);
|
|
+ inst[4] = InstReg(op_special, ScratchRegister, r0, r0, ff_jr).encode();
|
|
+ } else {
|
|
+ // Handle open long conditional jumps created by
|
|
+ // MacroAssemblerMIPSShared::ma_bc(..., wasm::Trap, ...).
|
|
+ inst[0] = invertBranch(inst[0], BOffImm16(7 * sizeof(uint32_t)));
|
|
+ // No need for a "nop" here because we can clobber scratch.
|
|
+ // We need to add it to long jumps array here.
|
|
+ // See MacroAssemblerMIPS64::branchWithCode().
|
|
+ MOZ_ASSERT(inst[1].encode() == NopInst);
|
|
+ MOZ_ASSERT(inst[2].encode() == NopInst);
|
|
+ MOZ_ASSERT(inst[3].encode() == NopInst);
|
|
+ MOZ_ASSERT(inst[4].encode() == NopInst);
|
|
+ MOZ_ASSERT(inst[5].encode() == NopInst);
|
|
+ MOZ_ASSERT(inst[6].encode() == NopInst);
|
|
+ addLongJump(BufferOffset(label->offset() + sizeof(uint32_t)));
|
|
+ Assembler::WriteLoad64Instructions(&inst[1], ScratchRegister, LabelBase::INVALID_OFFSET);
|
|
+ inst[5] = InstReg(op_special, ScratchRegister, r0, r0, ff_jr).encode();
|
|
+ }
|
|
+#endif
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::bind(Label* label, BufferOffset boff)
|
|
+{
|
|
+ spew(".set Llabel %p", label);
|
|
+ // If our caller didn't give us an explicit target to bind to
|
|
+ // then we want to bind to the location of the next instruction
|
|
+ BufferOffset dest = boff.assigned() ? boff : nextOffset();
|
|
+ if (label->used()) {
|
|
+ int32_t next;
|
|
+
|
|
+ // A used label holds a link to branch that uses it.
|
|
+ BufferOffset b(label);
|
|
+ do {
|
|
+ // Even a 0 offset may be invalid if we're out of memory.
|
|
+ if (oom()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Instruction* inst = editSrc(b);
|
|
+
|
|
+ // Second word holds a pointer to the next branch in label's chain.
|
|
+ next = inst[1].encode();
|
|
+ bind(reinterpret_cast<InstImm*>(inst), b.getOffset(), dest.getOffset(), boff.assigned());
|
|
+
|
|
+ b = BufferOffset(next);
|
|
+ } while (next != LabelBase::INVALID_OFFSET);
|
|
+ }
|
|
+ label->bind(dest.getOffset());
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::retarget(Label* label, Label* target)
|
|
+{
|
|
+ spew("retarget %p -> %p", label, target);
|
|
+ if (label->used() && !oom()) {
|
|
+ if (target->bound()) {
|
|
+ bind(label, BufferOffset(target));
|
|
+ } else if (target->used()) {
|
|
+ // The target is not bound but used. Prepend label's branch list
|
|
+ // onto target's.
|
|
+ int32_t next;
|
|
+ BufferOffset labelBranchOffset(label);
|
|
+
|
|
+ // Find the head of the use chain for label.
|
|
+ do {
|
|
+ Instruction* inst = editSrc(labelBranchOffset);
|
|
+
|
|
+ // Second word holds a pointer to the next branch in chain.
|
|
+ next = inst[1].encode();
|
|
+ labelBranchOffset = BufferOffset(next);
|
|
+ } while (next != LabelBase::INVALID_OFFSET);
|
|
+
|
|
+ // Then patch the head of label's use chain to the tail of
|
|
+ // target's use chain, prepending the entire use chain of target.
|
|
+ Instruction* inst = editSrc(labelBranchOffset);
|
|
+ int32_t prev = target->offset();
|
|
+ target->use(label->offset());
|
|
+ inst[1].setData(prev);
|
|
+ } else {
|
|
+ // The target is unbound and unused. We can just take the head of
|
|
+ // the list hanging off of label, and dump that into target.
|
|
+ target->use(label->offset());
|
|
+ }
|
|
+ }
|
|
+ label->reset();
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::processCodeLabels(uint8_t* rawCode)
|
|
+{
|
|
+ for (const CodeLabel& label : codeLabels_) {
|
|
+ Bind(rawCode, label);
|
|
+ }
|
|
+}
|
|
+
|
|
+uint32_t
|
|
+Assembler::PatchWrite_NearCallSize()
|
|
+{
|
|
+ // Load an address needs 5 instructions, mtctr, bctrl
|
|
+ return (5 + 2) * sizeof(uint32_t);
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall)
|
|
+{
|
|
+ // Overwrite whatever instruction used to be here with a call.
|
|
+ MOZ_ASSERT(PatchWrite_NearCallSize() == 7 * sizeof(uint32_t));
|
|
+ Instruction* inst = (Instruction*) start.raw();
|
|
+ uint8_t* dest = toCall.raw();
|
|
+
|
|
+ // We use a long stanza, but if we can, put nops/bl instead of a full
|
|
+ // lis/ori/rldicr/lis/ori/mtctr/bctr because we can speculate better.
|
|
+ // If we short it, the short branch is at the END of the stanza because
|
|
+ // the return address must follow it.
|
|
+ int64_t offset = ((uint64_t)dest - (uint64_t)inst) - 24;
|
|
+
|
|
+ if (JOffImm26::IsInRange(offset)) {
|
|
+ // "$5 says he shorts it."
|
|
+ // Since this is a near call, we expect we won't need to repatch this.
|
|
+ inst[0] = Instruction(PPC_nop);
|
|
+ inst[1] = Instruction(PPC_nop);
|
|
+ inst[2] = Instruction(PPC_nop);
|
|
+ inst[3] = Instruction(PPC_nop);
|
|
+ inst[4] = Instruction(PPC_nop);
|
|
+ inst[5] = Instruction(PPC_nop);
|
|
+ inst[6].setData(PPC_b | JOffImm26(offset).encode() | LinkB);
|
|
+ } else {
|
|
+ // Long jump required ...
|
|
+ Assembler::WriteLoad64Instructions(inst, ScratchRegister, (uint64_t)dest);
|
|
+ inst[5].makeOp_mtctr(ScratchRegister);
|
|
+ inst[6].makeOp_bctr(LinkB);
|
|
+ }
|
|
+
|
|
+ // Ensure everyone sees the code that was just written into memory.
|
|
+ FlushICache(inst, PatchWrite_NearCallSize());
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm)
|
|
+{
|
|
+ uint32_t *l = (uint32_t *)label.raw();
|
|
+
|
|
+ *(l - 1) = imm.value;
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::UpdateLisOriValue(Instruction *inst0, Instruction *inst1,
|
|
+ uint32_t value)
|
|
+{
|
|
+ MOZ_ASSERT(inst0->extractOpcode() == (uint32_t)PPC_addis);
|
|
+ MOZ_ASSERT(inst1->extractOpcode() == (uint32_t)PPC_ori);
|
|
+
|
|
+ reinterpret_cast<InstImm*>(inst0)->setImm16(value >> 16);
|
|
+ reinterpret_cast<InstImm*>(inst1)->setImm16(value & 0xffff);
|
|
+}
|
|
+
|
|
+uint64_t
|
|
+Assembler::ExtractLoad64Value(Instruction* inst0)
|
|
+{
|
|
+ InstImm* i0 = (InstImm*) inst0; // lis
|
|
+ InstImm* i1 = (InstImm*) i0->next(); // ori
|
|
+ Instruction* i2 = (Instruction*) i1->next(); // rldicr
|
|
+ InstImm* i3 = (InstImm*) i2->next(); // oris
|
|
+ InstImm* i4 = (InstImm*) i3->next(); // ori
|
|
+
|
|
+ MOZ_ASSERT(i0->extractOpcode() == (uint32_t)PPC_addis);
|
|
+ MOZ_ASSERT(i1->extractOpcode() == (uint32_t)PPC_ori);
|
|
+ MOZ_ASSERT(i2->extractOpcode() == (uint32_t)PPC_rldicl); // XXX: 0x78000000
|
|
+ MOZ_ASSERT(i3->extractOpcode() == (uint32_t)PPC_oris);
|
|
+ MOZ_ASSERT(i4->extractOpcode() == (uint32_t)PPC_ori);
|
|
+
|
|
+ uint64_t value = (uint64_t(i0->extractImm16Value()) << 48) |
|
|
+ (uint64_t(i1->extractImm16Value()) << 32) |
|
|
+ (uint64_t(i3->extractImm16Value()) << 16) |
|
|
+ uint64_t(i4->extractImm16Value());
|
|
+ return value;
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::UpdateLoad64Value(Instruction* inst0, uint64_t value)
|
|
+{
|
|
+ InstImm* i0 = (InstImm*) inst0;
|
|
+ InstImm* i1 = (InstImm*) i0->next();
|
|
+ Instruction* i2 = (Instruction*) i1->next();
|
|
+ InstImm* i3 = (InstImm*) i2->next();
|
|
+ InstImm* i4 = (InstImm*) i3->next();
|
|
+
|
|
+ MOZ_ASSERT(i0->extractOpcode() == (uint32_t)PPC_addis);
|
|
+ MOZ_ASSERT(i1->extractOpcode() == (uint32_t)PPC_ori);
|
|
+ MOZ_ASSERT(i2->extractOpcode() == (uint32_t)PPC_rldicl); // XXX
|
|
+ MOZ_ASSERT(i3->extractOpcode() == (uint32_t)PPC_oris);
|
|
+ MOZ_ASSERT(i4->extractOpcode() == (uint32_t)PPC_ori);
|
|
+
|
|
+ i0->setImm16(Imm16::Upper(Imm32(value >> 32)));
|
|
+ i1->setImm16(Imm16::Lower(Imm32(value >> 32)));
|
|
+ i3->setImm16(Imm16::Upper(Imm32(value)));
|
|
+ i4->setImm16(Imm16::Lower(Imm32(value)));
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::WriteLoad64Instructions(Instruction* inst0, Register reg, uint64_t value)
|
|
+{
|
|
+ Instruction* inst1 = inst0->next();
|
|
+ Instruction* inst2 = inst1->next();
|
|
+ Instruction* inst3 = inst2->next();
|
|
+ Instruction* inst4 = inst3->next();
|
|
+
|
|
+ *inst0 = InstImm(PPC_addis, reg, r0, Imm16::Upper(Imm32(value >> 32)).encode()); // mscdfr0
|
|
+ *inst1 = InstImm(PPC_ori, reg, reg, Imm16::Lower(Imm32(value >> 32)).encode());
|
|
+ // rldicr reg, reg, 32, 31
|
|
+ *inst2 = InstImm(PPC_rldicr, reg, reg, ((31 << 6) | (32 >> 4)));
|
|
+ *inst3 = InstImm(PPC_oris, reg, reg, Imm16::Upper(Imm32(value)).encode());
|
|
+ *inst4 = InstImm(PPC_ori, reg, reg, Imm16::Lower(Imm32(value)).encode());
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::PatchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue,
|
|
+ ImmPtr expectedValue)
|
|
+{
|
|
+ PatchDataWithValueCheck(label, PatchedImmPtr(newValue.value),
|
|
+ PatchedImmPtr(expectedValue.value));
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue,
|
|
+ PatchedImmPtr expectedValue)
|
|
+{
|
|
+ spew("# PatchDataWithValueCheck 0x%p", label.raw());
|
|
+ Instruction* inst = (Instruction*) label.raw();
|
|
+
|
|
+ // Extract old Value
|
|
+ DebugOnly<uint64_t> value = Assembler::ExtractLoad64Value(inst);
|
|
+ MOZ_ASSERT(value == uint64_t(expectedValue.value));
|
|
+
|
|
+ // Replace with new value
|
|
+ Assembler::UpdateLoad64Value(inst, uint64_t(newValue.value));
|
|
+ FlushICache(inst, 5 * sizeof(uint32_t));
|
|
+}
|
|
+
|
|
+// See MacroAssemblerPPC64Compat::toggledJump and
|
|
+// MacroAssemblerPPC64Compat::toggledCall.
|
|
+// These patch our suspicious oris r0,r0,0 to either skip the next
|
|
+// instruction or not.
|
|
+
|
|
+void
|
|
+Assembler::ToggleCall(CodeLocationLabel inst_, bool enabled)
|
|
+{
|
|
+ Instruction* inst = (Instruction*)inst_.raw();
|
|
+ MOZ_ASSERT((inst->encode() == PPC_oris) || (inst->encode() == (PPC_b | 0x20)));
|
|
+ if (enabled) {
|
|
+ inst->setData(PPC_oris); // oris 0,0,0
|
|
+ } else {
|
|
+ inst->setData(PPC_b | 0x20); // b .+32
|
|
+ }
|
|
+
|
|
+ FlushICache(inst, sizeof(uint32_t));
|
|
+}
|
|
+
|
|
+// JMP and CMP are from the icky x86 perspective. Since we have a jump
|
|
+// stanza, making it a "JMP" means setting the gate instruction to oris
|
|
+// so the stanza is run; making it a "CMP" means setting it to b+.32 so it
|
|
+// isn't.
|
|
+
|
|
+void
|
|
+Assembler::ToggleToJmp(CodeLocationLabel inst_)
|
|
+{
|
|
+ Instruction* inst = (Instruction*)inst_.raw();
|
|
+ MOZ_ASSERT(inst->encode() == (PPC_b | 0x20));
|
|
+ inst->setData(PPC_oris);
|
|
+
|
|
+ FlushICache(inst, sizeof(uint32_t));
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::ToggleToCmp(CodeLocationLabel inst_)
|
|
+{
|
|
+ Instruction* inst = (Instruction*)inst_.raw();
|
|
+ MOZ_ASSERT(inst->encode() == PPC_oris);
|
|
+ inst->setData(PPC_b | 0x20);
|
|
+
|
|
+ FlushICache(inst, sizeof(uint32_t));
|
|
+}
|
|
+
|
|
+Assembler::Condition
|
|
+Assembler::InvertCondition( Condition cond)
|
|
+{
|
|
+ switch (cond) {
|
|
+ case Equal:
|
|
+ return NotEqual;
|
|
+ case NotEqual:
|
|
+ return Equal;
|
|
+ case LessThan:
|
|
+ return GreaterThanOrEqual;
|
|
+ case LessThanOrEqual:
|
|
+ return GreaterThan;
|
|
+ case GreaterThan:
|
|
+ return LessThanOrEqual;
|
|
+ case GreaterThanOrEqual:
|
|
+ return LessThan;
|
|
+ case Above:
|
|
+ return BelowOrEqual;
|
|
+ case AboveOrEqual:
|
|
+ return Below;
|
|
+ case Below:
|
|
+ return AboveOrEqual;
|
|
+ case BelowOrEqual:
|
|
+ return Above;
|
|
+ case BitEqual:
|
|
+ return BitNotEqual;
|
|
+ case BitNotEqual:
|
|
+ return BitEqual;
|
|
+ case Zero:
|
|
+ return NonZero;
|
|
+ case NonZero:
|
|
+ return Zero;
|
|
+ case Signed:
|
|
+ return NotSigned;
|
|
+ case NotSigned:
|
|
+ return Signed;
|
|
+ case SOBit:
|
|
+ return NSOBit;
|
|
+ case NSOBit:
|
|
+ return SOBit;
|
|
+ default:
|
|
+ MOZ_CRASH("unexpected condition");
|
|
+ }
|
|
+}
|
|
+
|
|
+Assembler::DoubleCondition
|
|
+Assembler::InvertCondition( DoubleCondition cond)
|
|
+{
|
|
+ switch (cond) {
|
|
+ case DoubleOrdered:
|
|
+ return DoubleUnordered;
|
|
+ case DoubleEqual:
|
|
+ return DoubleNotEqualOrUnordered;
|
|
+ case DoubleNotEqual:
|
|
+ return DoubleEqualOrUnordered;
|
|
+ case DoubleGreaterThan:
|
|
+ return DoubleLessThanOrEqualOrUnordered;
|
|
+ case DoubleGreaterThanOrEqual:
|
|
+ return DoubleLessThanOrUnordered;
|
|
+ case DoubleLessThan:
|
|
+ return DoubleGreaterThanOrEqualOrUnordered;
|
|
+ case DoubleLessThanOrEqual:
|
|
+ return DoubleGreaterThanOrUnordered;
|
|
+ case DoubleUnordered:
|
|
+ return DoubleOrdered;
|
|
+ case DoubleEqualOrUnordered:
|
|
+ return DoubleNotEqual;
|
|
+ case DoubleNotEqualOrUnordered:
|
|
+ return DoubleEqual;
|
|
+ case DoubleGreaterThanOrUnordered:
|
|
+ return DoubleLessThanOrEqual;
|
|
+ case DoubleGreaterThanOrEqualOrUnordered:
|
|
+ return DoubleLessThan;
|
|
+ case DoubleLessThanOrUnordered:
|
|
+ return DoubleGreaterThanOrEqual;
|
|
+ case DoubleLessThanOrEqualOrUnordered:
|
|
+ return DoubleGreaterThan;
|
|
+ default:
|
|
+ MOZ_CRASH("unexpected condition");
|
|
+ }
|
|
+}
|
|
+
|
|
+bool
|
|
+Assembler::swapBuffer(wasm::Bytes& bytes) {
|
|
+ // For now, specialize to the one use case. As long as wasm::Bytes is a
|
|
+ // Vector, not a linked-list of chunks, there's not much we can do other
|
|
+ // than copy.
|
|
+ MOZ_ASSERT(bytes.empty());
|
|
+ if (!bytes.resize(bytesNeeded())) {
|
|
+ return false;
|
|
+ }
|
|
+ m_buffer.executableCopy(bytes.begin());
|
|
+ return true;
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::copyJumpRelocationTable(uint8_t* dest)
|
|
+{
|
|
+ if (jumpRelocations_.length()) {
|
|
+ memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length());
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::copyDataRelocationTable(uint8_t* dest)
|
|
+{
|
|
+ if (dataRelocations_.length()) {
|
|
+ memcpy(dest, dataRelocations_.buffer(), dataRelocations_.length());
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::finish()
|
|
+{
|
|
+ MOZ_ASSERT(!isFinished);
|
|
+ isFinished = true;
|
|
+}
|
|
+
|
|
+void
|
|
+Assembler::executableCopy(uint8_t* buffer)
|
|
+{
|
|
+ spew("# EXECUTABLE COPY TO %p\n", buffer);
|
|
+
|
|
+ MOZ_ASSERT(isFinished);
|
|
+ m_buffer.executableCopy(buffer);
|
|
+}
|
|
+
|
|
+bool
|
|
+Assembler::appendRawCode(unsigned char const *bytes, unsigned long length)
|
|
+{
|
|
+ return m_buffer.appendRawCode(bytes, length);
|
|
+}
|
|
+
|
|
+bool Assembler::oom() const {
|
|
+ return AssemblerShared::oom() || m_buffer.oom() || jumpRelocations_.oom() ||
|
|
+ dataRelocations_.oom();
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::haltingAlign(int align)
|
|
+{
|
|
+ BufferOffset ret;
|
|
+ MOZ_ASSERT(m_buffer.isAligned(4));
|
|
+ if (align == 8) {
|
|
+ if (!m_buffer.isAligned(align)) {
|
|
+ BufferOffset tmp = xs_trap();
|
|
+ if (!ret.assigned()) {
|
|
+ ret = tmp;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ MOZ_ASSERT((align& (align- 1)) == 0);
|
|
+ while (size() & (align- 1)) {
|
|
+ BufferOffset tmp = xs_trap();
|
|
+ if (!ret.assigned()) {
|
|
+ ret = tmp;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::nopAlign(int align)
|
|
+{
|
|
+ BufferOffset ret;
|
|
+ MOZ_ASSERT(m_buffer.isAligned(4));
|
|
+ if (align == 8) {
|
|
+ if (!m_buffer.isAligned(align)) {
|
|
+ BufferOffset tmp = as_nop();
|
|
+ if (!ret.assigned()) {
|
|
+ ret = tmp;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ MOZ_ASSERT((align& (align- 1)) == 0);
|
|
+ while (size() & (align- 1)) {
|
|
+ BufferOffset tmp = as_nop();
|
|
+ if (!ret.assigned()) {
|
|
+ ret = tmp;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+size_t
|
|
+Assembler::size() const
|
|
+{
|
|
+ return m_buffer.size();
|
|
+}
|
|
+
|
|
+size_t
|
|
+Assembler::dataRelocationTableBytes() const
|
|
+{
|
|
+ return dataRelocations_.length();
|
|
+}
|
|
+
|
|
+size_t
|
|
+Assembler::jumpRelocationTableBytes() const
|
|
+{
|
|
+ return jumpRelocations_.length();
|
|
+}
|
|
+
|
|
+size_t
|
|
+Assembler::bytesNeeded() const
|
|
+{
|
|
+ return m_buffer.size() +
|
|
+ jumpRelocations_.length() +
|
|
+ dataRelocations_.length() +
|
|
+ preBarriers_.length();
|
|
+}
|
|
+
|
|
+BufferOffset
|
|
+Assembler::writeInst(uint32_t x, uint32_t *dest)
|
|
+{
|
|
+ if (dest == nullptr)
|
|
+ return m_buffer.putInt(x);
|
|
+
|
|
+ *dest = x;
|
|
+ return BufferOffset();
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_nop()
|
|
+{
|
|
+ spew("nop");
|
|
+ return writeInst(PPC_nop);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_eieio()
|
|
+{
|
|
+ // Old McDonald had to order memory access ...
|
|
+ spew("eieio");
|
|
+ return writeInst(PPC_eieio);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_isync()
|
|
+{
|
|
+ spew("isync");
|
|
+ return writeInst(PPC_isync);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::xs_lwsync()
|
|
+{
|
|
+ spew("lwsync");
|
|
+ return writeInst(PPC_lwsync);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_sync()
|
|
+{
|
|
+ spew("sync");
|
|
+ return writeInst(PPC_sync);
|
|
+}
|
|
+
|
|
+// Branch and jump instructions.
|
|
+BufferOffset Assembler::as_b(JOffImm26 off, BranchAddressType bat, LinkBit lb)
|
|
+{
|
|
+ return as_b(off.encode(), bat, lb);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_b(int32_t off, BranchAddressType bat, LinkBit lb)
|
|
+{
|
|
+ spew("b%s%s\t%x", bat == AbsoluteBranch ? "a" : "", lb ? "l" : "", off);
|
|
+ MOZ_ASSERT(!(off & 0x03));
|
|
+ return writeInst(PPC_b | ((uint32_t)off & 0x3fffffc) | bat | lb);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_blr(LinkBit lb)
|
|
+{
|
|
+ spew("blr%s", lb ? "l" : "");
|
|
+ return writeInst(PPC_blr | lb);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_bctr(LinkBit lb)
|
|
+{
|
|
+ spew("bctr%s", lb ? "l" : "");
|
|
+ return writeInst(PPC_bctr | lb);
|
|
+}
|
|
+
|
|
+// Conditional branches.
|
|
+//
|
|
+// These utility functions turn a condition (possibly synthetic) and CR
|
|
+// field number into BO _|_ BI. With DoubleConditions we may issue CR bit
|
|
+// twiddles and change the op.
|
|
+uint16_t Assembler::computeConditionCode(DoubleCondition op, CRegisterID cr)
|
|
+{
|
|
+ // Use condition register logic to combine the FU (FUUUU-! I mean, unordered)
|
|
+ // bit with the actual condition bit.
|
|
+ const uint8_t condBit = crBit(cr, op);
|
|
+ const uint8_t fuBit = crBit(cr, DoubleUnordered);
|
|
+ uint32_t newop = (uint32_t)op & 255;
|
|
+
|
|
+ if (op & DoubleConditionUnordered) {
|
|
+ // branch if condition true OR Unordered
|
|
+ if ((op & BranchOptionMask) == BranchOnClear) {
|
|
+ // invert the condBit, or it with fuBit, and branch on Set
|
|
+ as_crorc(condBit, fuBit, condBit);
|
|
+ newop |= BranchOnSet;
|
|
+ } else {
|
|
+ // or the condBit with fuBit, and then branch on Set
|
|
+ if (condBit != fuBit)
|
|
+ as_cror(condBit, fuBit, condBit);
|
|
+ }
|
|
+ } else {
|
|
+ // branch if condition true AND ordered
|
|
+ if ((op & BranchOptionMask) == BranchOnClear) {
|
|
+ // or the condBit with fuBit, and branch on Clear
|
|
+ if (condBit != fuBit)
|
|
+ as_cror(condBit, fuBit, condBit);
|
|
+ } else {
|
|
+ // and the condBit with (!fuBit), and branch on Set, but
|
|
+ // don't clear SO if this is actually DoubleUnordered
|
|
+ // (fuBit == condBit), which is NOT a synthetic condition.
|
|
+ if (condBit != fuBit)
|
|
+ as_crandc(condBit, condBit, fuBit);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Set BIF to the proper CR. In cr0, the normal state, this just returns newop.
|
|
+ return (newop + ((uint8_t)cr << 6));
|
|
+}
|
|
+
|
|
+// Do the same for GPR compares and XER-based conditions.
|
|
+uint16_t Assembler::computeConditionCode(Condition op, CRegisterID cr)
|
|
+{
|
|
+ // Mask off the synthetic bits, if present. Hopefully we handled them already!
|
|
+ uint32_t newop = (uint32_t)op & 255;
|
|
+
|
|
+ // If this is an XER-mediated condition, then extract it.
|
|
+ if (op & ConditionOnlyXER) {
|
|
+ MOZ_ASSERT(op == Overflow);
|
|
+ // Get XER into CR.
|
|
+ as_mcrxrx(cr);
|
|
+ // Convert op to GT (using the OV32 bit).
|
|
+ newop = (uint32_t)GreaterThan;
|
|
+ }
|
|
+
|
|
+ // Set BIF to the proper CR. In cr0, the normal state, this just returns newop.
|
|
+ return (newop + ((uint8_t)cr << 6));
|
|
+}
|
|
+
|
|
+// Given BO _|_ BI in a 16-bit quantity, emit the two halves suitable for bit masking.
|
|
+static uint32_t makeOpMask(uint16_t op)
|
|
+{
|
|
+ MOZ_ASSERT(!(op & 0xfc00)); // must fit in 10 bits
|
|
+ return ((op & 0x0f) << 21) | ((op & 0xfff0) << 12);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_bcctr(Condition cond, CRegisterID cr, LikelyBit lkb,
|
|
+ LinkBit lb)
|
|
+{
|
|
+ return as_bcctr(computeConditionCode(cond, cr), lkb, lb);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_bcctr(DoubleCondition cond, CRegisterID cr,
|
|
+ LikelyBit lkb, LinkBit lb)
|
|
+{
|
|
+ return as_bcctr(computeConditionCode(cond, cr), lkb, lb);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_bc(BOffImm16 off, Condition cond, CRegisterID cr,
|
|
+ LikelyBit lkb, LinkBit lb)
|
|
+{
|
|
+ // fall through to the next one
|
|
+ return as_bc(off.encode(), cond, cr, lkb, lb);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_bc(int16_t off, Condition cond, CRegisterID cr,
|
|
+ LikelyBit lkb, LinkBit lb)
|
|
+{
|
|
+ // No current need to adjust for the |mcrxr|; see ma_bc() in the
|
|
+ // MacroAssembler for why (no branch we generate that uses the result
|
|
+ // needs to be retargeted).
|
|
+ return as_bc(off, computeConditionCode(cond, cr), lkb, lb);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_bc(BOffImm16 off, DoubleCondition cond,
|
|
+ CRegisterID cr, LikelyBit lkb, LinkBit lb)
|
|
+{
|
|
+ // fall through to the next one
|
|
+ return as_bc(off.encode(), cond, cr, lkb, lb);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_bc(int16_t off, DoubleCondition cond, CRegisterID cr,
|
|
+ LikelyBit lkb, LinkBit lb)
|
|
+{
|
|
+ // Adjust for issued CR twiddles, if any.
|
|
+ uint32_t offs = currentOffset();
|
|
+ uint16_t op = computeConditionCode(cond, cr);
|
|
+ offs = currentOffset() - offs;
|
|
+ MOZ_ASSERT(offs == 0 || offs == 4); // currently zero or one instruction
|
|
+ return as_bc((off-offs), op, lkb, lb);
|
|
+}
|
|
+
|
|
+// These are the actual instruction emitters after conditions are converted
|
|
+// and any offsets recalculated.
|
|
+BufferOffset Assembler::as_bc(int16_t off, uint16_t op, LikelyBit lkb, LinkBit lb)
|
|
+{
|
|
+ spew("bc%s%s BO_BI=0x%04x,%d", (lb) ? "l" : "", (lkb) ? "+" : "", op, off);
|
|
+ MOZ_ASSERT(!(off & 0x03));
|
|
+ return writeInst(Instruction(PPC_bc | makeOpMask(op) | lkb << 21 | ((uint16_t)off & 0xfffc) | lb).encode());
|
|
+}
|
|
+BufferOffset Assembler::as_bcctr(uint16_t op, LikelyBit lkb, LinkBit lb)
|
|
+{
|
|
+ spew("bcctr%s%s", (lb) ? "l" : "", (lkb) ? "+" : "");
|
|
+ return writeInst(PPC_bcctr | makeOpMask(op) | lkb << 21 | lb);
|
|
+}
|
|
+
|
|
+// SPR operations.
|
|
+BufferOffset Assembler::as_mtspr(SPRegisterID spr, Register ra)
|
|
+{
|
|
+ spew("mtspr\t%d,%3s", spr, ra.name());
|
|
+ return writeInst(PPC_mtspr | ra.code() << 21 | PPC_SPR(spr) << 11);
|
|
+}
|
|
+BufferOffset Assembler::as_mfspr(Register rd, SPRegisterID spr)
|
|
+{
|
|
+ spew("mfspr\t%3s,%d", rd.name(), spr);
|
|
+ return writeInst(PPC_mfspr | rd.code() << 21 | PPC_SPR(spr) << 11);
|
|
+}
|
|
+
|
|
+// CR operations.
|
|
+#define DEF_CRCR(op) \
|
|
+ BufferOffset Assembler::as_##op(uint8_t t, uint8_t a, uint8_t b) { \
|
|
+ spew(#op"\t%d,%d,%d", t, a, b); \
|
|
+ return writeInst(PPC_##op | t << 21 | a << 16 | b << 11); }
|
|
+DEF_CRCR(crand)
|
|
+DEF_CRCR(crandc)
|
|
+DEF_CRCR(cror)
|
|
+DEF_CRCR(crorc)
|
|
+DEF_CRCR(crxor)
|
|
+#undef DEF_CRCR
|
|
+
|
|
+BufferOffset Assembler::as_mtcrf(uint32_t mask, Register rs)
|
|
+{
|
|
+ spew("mtcrf %d,%3s", mask, rs.name());
|
|
+ return writeInst(PPC_mtcrf | rs.code() << 21 | mask << 12);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_mfcr(Register rd)
|
|
+{
|
|
+ spew("mfcr %3s", rd.name());
|
|
+ return writeInst(PPC_mfcr | rd.code() << 21);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_mfocrf(Register rd, CRegisterID crfs)
|
|
+{
|
|
+ spew("mfocrf %3s,cr%d", rd.name(), crfs);
|
|
+ return writeInst(PPC_mfocrf | rd.code() << 21 | crfs << 12);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_mcrxrx(CRegisterID cr)
|
|
+{
|
|
+ spew("mcrxrx\tcr%d", cr);
|
|
+ return writeInst(PPC_mcrxrx | cr << 23);
|
|
+}
|
|
+
|
|
+// GPR operations and load-stores.
|
|
+BufferOffset Assembler::as_neg(Register rd, Register rs)
|
|
+{
|
|
+ spew("neg %3s,%3s", rd.name(), rs.name());
|
|
+ return writeInst(InstReg(PPC_neg, rd, rs, r0).encode());
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_nego(Register rd, Register rs)
|
|
+{
|
|
+ spew("nego %3s,%3s", rd.name(), rs.name());
|
|
+ return writeInst(InstReg(PPC_nego, rd, rs, r0).encode());
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_cmpd(CRegisterID cr, Register ra, Register rb)
|
|
+{
|
|
+ spew("cmpd\tcr%d,%3s,%3s", cr, ra.name(), rb.name());
|
|
+ return writeInst(PPC_cmpd | cr << 23 | ra.code() << 16 | rb.code() << 11);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_cmpdi(CRegisterID cr, Register ra, int16_t im)
|
|
+{
|
|
+ spew("cmpdi\tcr%d,%3s,%d", cr, ra.name(), im);
|
|
+ return writeInst(PPC_cmpdi | cr << 23 | ra.code() << 16 | ((uint16_t)im & 0xffff));
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_cmpld(CRegisterID cr, Register ra, Register rb)
|
|
+{
|
|
+ spew("cmpld\tcr%d,%3s,%3s", cr, ra.name(), rb.name());
|
|
+ return writeInst(PPC_cmpld | cr << 23 | ra.code() << 16 | rb.code() << 11);
|
|
+}
|
|
+BufferOffset Assembler::as_cmpldi(CRegisterID cr, Register ra, int16_t im)
|
|
+{
|
|
+ spew("cmpldi\tcr%d,%3s,%d", cr, ra.name(), im);
|
|
+ return writeInst(PPC_cmpldi | cr << 23 | ra.code() << 16 | ((uint16_t)im & 0xffff));
|
|
+}
|
|
+BufferOffset Assembler::as_cmpw(CRegisterID cr, Register ra, Register rb)
|
|
+{
|
|
+ spew("cmpw\t%3s,%3s", ra.name(), rb.name());
|
|
+ return writeInst(PPC_cmpw | cr << 23 | ra.code() << 16 | rb.code() << 11);
|
|
+}
|
|
+BufferOffset Assembler::as_cmpwi(CRegisterID cr, Register ra, int16_t im)
|
|
+{
|
|
+ spew("cmpwi\tcr%d,%3s,%d", cr, ra.name(), im);
|
|
+ return writeInst(PPC_cmpwi | cr << 23 | ra.code() << 16 | ((uint16_t)im & 0xffff));
|
|
+}
|
|
+BufferOffset Assembler::as_cmplw(CRegisterID cr, Register ra, Register rb)
|
|
+{
|
|
+ spew("cmplw\tcr%d,%3s,%3s", cr, ra.name(), rb.name());
|
|
+ return writeInst(PPC_cmplw | cr << 23 | ra.code() << 16 | rb.code() << 11);
|
|
+}
|
|
+BufferOffset Assembler::as_cmplwi(CRegisterID cr, Register ra, int16_t im)
|
|
+{
|
|
+ spew("cmplwi\tcr%d,%3s,%d", cr, ra.name(), im);
|
|
+ return writeInst(PPC_cmplwi | cr << 23 | ra.code() << 16 | ((uint16_t)im & 0xffff));
|
|
+}
|
|
+BufferOffset Assembler::as_cmpd(Register ra, Register rb)
|
|
+{
|
|
+ spew("cmpd\t%3s,%3s", ra.name(), rb.name());
|
|
+ return writeInst(PPC_cmpd | ra.code() << 16 | rb.code() << 11);
|
|
+}
|
|
+BufferOffset Assembler::as_cmpdi(Register ra, int16_t im)
|
|
+{
|
|
+ spew("cmpdi\t%3s,%d", ra.name(), im);
|
|
+ return writeInst(PPC_cmpdi | ra.code() << 16 | ((uint16_t)im & 0xffff));
|
|
+}
|
|
+BufferOffset Assembler::as_cmpld(Register ra, Register rb)
|
|
+{
|
|
+ spew("cmpld\t%3s,%3s", ra.name(), rb.name());
|
|
+ return writeInst(PPC_cmpld | ra.code() << 16 | rb.code() << 11);
|
|
+}
|
|
+BufferOffset Assembler::as_cmpldi(Register ra, int16_t im)
|
|
+{
|
|
+ spew("cmpldi\t%3s,%d", ra.name(), im);
|
|
+ return writeInst(PPC_cmpldi | ra.code() << 16 | ((uint16_t)im & 0xffff));
|
|
+}
|
|
+BufferOffset Assembler::as_cmpw(Register ra, Register rb)
|
|
+{
|
|
+ spew("cmpw\t%3s,%3s", ra.name(), rb.name());
|
|
+ return writeInst(PPC_cmpw | ra.code() << 16 | rb.code() << 11);
|
|
+}
|
|
+BufferOffset Assembler::as_cmpwi(Register ra, int16_t im)
|
|
+{
|
|
+ spew("cmpwi\t%3s,%d", ra.name(), im);
|
|
+ return writeInst(PPC_cmpwi | ra.code() << 16 | ((uint16_t)im & 0xffff));
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_cmplw(Register ra, Register rb)
|
|
+{
|
|
+ spew("cmplw\t%3s,%3s", ra.name(), rb.name());
|
|
+ return writeInst(PPC_cmplw | ra.code() << 16 | rb.code() << 11);
|
|
+}
|
|
+BufferOffset Assembler::as_cmplwi(Register ra, int16_t im)
|
|
+{
|
|
+ spew("cmplwi\t%3s,%d", ra.name(), im);
|
|
+ return writeInst(PPC_cmplwi | ra.code() << 16 | ((uint16_t)im & 0xffff));
|
|
+}
|
|
+
|
|
+static uint32_t
|
|
+AForm(uint32_t op, FloatRegister frt, FloatRegister fra, FloatRegister frb,
|
|
+ FloatRegister frc, bool rc)
|
|
+{
|
|
+ return (op | (frt.encoding() << 21) | (fra.encoding() << 16) | (frb.encoding() << 11) |
|
|
+ (frc.encoding() << 6) | rc);
|
|
+}
|
|
+
|
|
+static uint32_t
|
|
+XForm(uint32_t op, FloatRegister frt, FloatRegister fra, FloatRegister frb, bool rc)
|
|
+{
|
|
+ return (op | (frt.encoding() << 21) | (fra.encoding() << 16) | (frb.encoding() << 11) | rc);
|
|
+}
|
|
+
|
|
+static uint32_t
|
|
+XForm(uint32_t op, FloatRegister frt, Register ra, Register rb, bool rc)
|
|
+{
|
|
+ return (op | (frt.encoding() << 21) | (ra.code() << 16) | (rb.code() << 11) | rc);
|
|
+}
|
|
+
|
|
+static uint32_t
|
|
+DForm(uint32_t op, FloatRegister frt, Register ra, int16_t imm)
|
|
+{
|
|
+ return (op | (frt.encoding() << 21) | (ra.code() << 16) | ((uint16_t)imm & 0xffff));
|
|
+}
|
|
+
|
|
+#define DEF_XFORM(op) \
|
|
+ BufferOffset Assembler::as_##op(Register rd, Register ra, Register rb) { \
|
|
+ spew(#op "\t%3s,%3s,%3s", rd.name(), ra.name(), rb.name()); \
|
|
+ return writeInst(InstReg(PPC_##op, rd, ra, rb).encode()); }
|
|
+
|
|
+#define DEF_XFORM_RC(op) \
|
|
+ BufferOffset Assembler::as_##op##_rc(Register rd, Register ra, Register rb) {\
|
|
+ spew(#op ".\t%3s,%3s,%3s", rd.name(), ra.name(), rb.name()); \
|
|
+ return writeInst(InstReg(PPC_##op, rd, ra, rb).encode() | 0x1); }
|
|
+
|
|
+#define DEF_XFORMS(op) \
|
|
+ BufferOffset Assembler::as_##op(Register rd, Register ra, Register rb) { \
|
|
+ spew(#op "\t%3s,%3s,%3s", rd.name(), ra.name(), rb.name()); \
|
|
+ return writeInst(InstReg(PPC_##op, ra, rd, rb).encode()); }
|
|
+
|
|
+#define DEF_XFORMS_RC(op) \
|
|
+ BufferOffset Assembler::as_##op##_rc(Register rd, Register ra, Register rb) {\
|
|
+ spew(#op ".\t%3s,%3s,%3s", rd.name(), ra.name(), rb.name()); \
|
|
+ return writeInst(InstReg(PPC_##op, ra, rd, rb).encode() | 0x1); }
|
|
+
|
|
+#define DEF_AFORM_C(op) \
|
|
+ BufferOffset Assembler::as_##op(FloatRegister rd, FloatRegister ra, FloatRegister rc) { \
|
|
+ spew(#op "\t%3s,%3s,%3s", rd.name(), ra.name(), rc.name()); \
|
|
+ return writeInst(AForm(PPC_##op, rd, ra, f0, rc, false)); }
|
|
+
|
|
+#define DEF_AFORM_C_RC(op) \
|
|
+ BufferOffset Assembler::as_##op##_rc(FloatRegister rd, FloatRegister ra, FloatRegister rc) {\
|
|
+ spew(#op ".\t%3s,%3s,%3s", rd.name(), ra.name(), rc.name()); \
|
|
+ return writeInst(AForm(PPC_##op, rd, ra, f0, rc, true)); }
|
|
+
|
|
+#define DEF_AFORM_B(op) \
|
|
+ BufferOffset Assembler::as_##op(FloatRegister rd, FloatRegister ra, FloatRegister rb) { \
|
|
+ spew(#op "\t%3s,%3s,%3s", rd.name(), ra.name(), rb.name()); \
|
|
+ return writeInst(AForm(PPC_##op, rd, ra, rb, f0, false)); }
|
|
+
|
|
+#define DEF_AFORM_B_RC(op) \
|
|
+ BufferOffset Assembler::as_##op##_rc(FloatRegister rd, FloatRegister ra, FloatRegister rb) {\
|
|
+ spew(#op ".\t%3s,%3s,%3s", rd.name(), ra.name(), rb.name()); \
|
|
+ return writeInst(AForm(PPC_##op, rd, ra, rb, f0, true)); }
|
|
+
|
|
+#define DEF_AFORM(op) \
|
|
+ BufferOffset Assembler::as_##op(FloatRegister rd, FloatRegister ra, FloatRegister rc, FloatRegister rb) { \
|
|
+ spew(#op "\t%3s,%3s,%3s,%3s", rd.name(), ra.name(), rc.name(), rb.name()); \
|
|
+ return writeInst(AForm(PPC_##op, rd, ra, rb, rc, false)); }
|
|
+
|
|
+#define DEF_AFORM_RC(op) \
|
|
+ BufferOffset Assembler::as_##op##_rc(FloatRegister rd, FloatRegister ra, FloatRegister rc, FloatRegister rb) {\
|
|
+ spew(#op ".\t%3s,%3s,%3s,%3s", rd.name(), ra.name(), rc.name(), rb.name()); \
|
|
+ return writeInst(AForm(PPC_##op, rd, ra, rb, rc, true)); }
|
|
+
|
|
+#define DEF_XFORMS_I(op) \
|
|
+ BufferOffset Assembler::as_##op(Register rd, Register ra, uint8_t sh) { \
|
|
+ spew(#op "\t%3s,%3s,%d", rd.name(), ra.name(), sh); \
|
|
+ return writeInst(PPC_##op | ra.code() << 21 | rd.code() << 16 | sh << 11); }
|
|
+
|
|
+#define DEF_XFORMS_I_RC(op) \
|
|
+ BufferOffset Assembler::as_##op##_rc(Register rd, Register ra, uint8_t sh) {\
|
|
+ spew(#op ".\t%3s,%3s,%d", rd.name(), ra.name(), sh); \
|
|
+ return writeInst(PPC_##op | ra.code() << 21 | rd.code() << 16 | sh << 11 | 0x1); }
|
|
+
|
|
+#define DEF_XFORM2(op) \
|
|
+ BufferOffset Assembler::as_##op(Register rd, Register ra) { \
|
|
+ spew(#op "\t%3s,%3s", rd.name(), ra.name()); \
|
|
+ return writeInst(InstReg(PPC_##op, rd, ra, r0).encode()); }
|
|
+
|
|
+#define DEF_XFORM2_RC(op) \
|
|
+ BufferOffset Assembler::as_##op##_rc(Register rd, Register ra) {\
|
|
+ spew(#op ".\t%3s,%3s", rd.name(), ra.name()); \
|
|
+ return writeInst(InstReg(PPC_##op, rd, ra, r0).encode() | 0x1); }
|
|
+
|
|
+#define DEF_XFORM2_F(op) \
|
|
+ BufferOffset Assembler::as_##op(FloatRegister rd, FloatRegister ra) { \
|
|
+ spew(#op "\t%3s,%3s", rd.name(), ra.name()); \
|
|
+ return writeInst(XForm(PPC_##op, rd, f0, ra, false)); }
|
|
+
|
|
+#define DEF_XFORM2_F_RC(op) \
|
|
+ BufferOffset Assembler::as_##op##_rc(FloatRegister rd, FloatRegister ra) {\
|
|
+ spew(#op ".\t%3s,%3s", rd.name(), ra.name()); \
|
|
+ return writeInst(XForm(PPC_##op, rd, f0, ra, true)); }
|
|
+
|
|
+#define DEF_XFORM2S(op) \
|
|
+ BufferOffset Assembler::as_##op(Register rd, Register ra) { \
|
|
+ spew(#op "\t%3s,%3s", rd.name(), ra.name()); \
|
|
+ return writeInst(InstReg(PPC_##op, ra, rd, r0).encode()); }
|
|
+
|
|
+#define DEF_XFORM2S_RC(op) \
|
|
+ BufferOffset Assembler::as_##op##_rc(Register rd, Register ra) {\
|
|
+ spew(#op ".\t%3s,%3s", rd.name(), ra.name()); \
|
|
+ return writeInst(InstReg(PPC_##op, ra, rd, r0).encode() | 0x1); }
|
|
+
|
|
+#define DEF_DFORM(op) \
|
|
+ BufferOffset Assembler::as_##op(Register ra, Register rs, int16_t im) { \
|
|
+ spew(#op "\t%3s,%d(%3s)", ra.name(), im, rs.name()); \
|
|
+ MOZ_ASSERT(rs != r0); \
|
|
+ return writeInst(InstImm(PPC_##op, ra, rs, im).encode()); }
|
|
+
|
|
+#define DEF_DFORMS(op) \
|
|
+ BufferOffset Assembler::as_##op(Register ra, Register rs, uint16_t im) { \
|
|
+ spew(#op "\t%3s,%d(%3s)", ra.name(), im, rs.name()); \
|
|
+ return writeInst(InstImm(PPC_##op, rs, ra, im).encode()); }
|
|
+
|
|
+#define DEF_DFORM_F(op) \
|
|
+ BufferOffset Assembler::as_##op(FloatRegister rt, Register ra, int16_t im) { \
|
|
+ spew(#op "\t%3s,%d(%3s)", rt.name(), im, ra.name()); \
|
|
+ MOZ_ASSERT(ra != r0); \
|
|
+ return writeInst(DForm(PPC_##op, rt, ra, im)); }
|
|
+
|
|
+#define DEF_MFORM(op) \
|
|
+ BufferOffset Assembler::as_##op(Register ra, Register rs, Register rb, uint8_t mb, uint8_t me) {\
|
|
+ spew(#op "\t%3s,%3s,%3s,%d,%d", ra.name(), rs.name(), rb.name(), mb, me); \
|
|
+ return writeInst(PPC_##op | rs.code() << 21 | ra.code() << 16 | rb.code() << 11 | mb << 6 | me << 1); }
|
|
+
|
|
+#define DEF_MFORM_RC(op) \
|
|
+ BufferOffset Assembler::as_##op##_rc(Register ra, Register rs, Register rb, uint8_t mb, uint8_t me) {\
|
|
+ spew(#op ".\t%3s,%3s,%3s,%d,%d", ra.name(), rs.name(), rb.name(), mb, me); \
|
|
+ return writeInst(PPC_##op | rs.code() << 21 | ra.code() << 16 | rb.code() << 11 | mb << 6 | me << 1 | 1); }
|
|
+
|
|
+#define DEF_MFORM_I(op) \
|
|
+ BufferOffset Assembler::as_##op(Register ra, Register rs, uint8_t sh, uint8_t mb, uint8_t me) {\
|
|
+ spew(#op "\t%3s,%3s,%d,%d,%d", ra.name(), rs.name(), sh, mb, me); \
|
|
+ MOZ_ASSERT(sh < 32); \
|
|
+ MOZ_ASSERT(mb < 32); \
|
|
+ return writeInst(PPC_##op | rs.code() << 21 | ra.code() << 16 | sh << 11 | mb << 6 | me << 1); }
|
|
+
|
|
+#define DEF_MFORM_I_RC(op) \
|
|
+ BufferOffset Assembler::as_##op##_rc(Register ra, Register rs, uint8_t sh, uint8_t mb, uint8_t me) {\
|
|
+ spew(#op ".\t%3s,%3s,%d,%d,%d", ra.name(), rs.name(), sh, mb, me); \
|
|
+ MOZ_ASSERT(sh < 32); \
|
|
+ MOZ_ASSERT(mb < 32); \
|
|
+ return writeInst(PPC_##op | rs.code() << 21 | ra.code() << 16 | sh << 11 | mb << 6 | me << 1 | 1); }
|
|
+
|
|
+#define DEF_MDSFORM(op) \
|
|
+ BufferOffset Assembler::as_##op(Register ra, Register rs, Register rb, uint8_t mb) {\
|
|
+ spew(#op "\t%3s,%3s,%3s,%d", ra.name(), rs.name(), rb.name(), mb); \
|
|
+ return writeInst(PPC_##op | rs.code() << 21 | ra.code() << 16 | rb.code() << 11 | mb << 6); }
|
|
+
|
|
+#define DEF_MDSFORM_RC(op) \
|
|
+ BufferOffset Assembler::as_##op##_rc(Register ra, Register rs, Register rb, uint8_t mb) {\
|
|
+ spew(#op ".\t%3s,%3s,%3s,%d", ra.name(), rs.name(), rb.name(), mb); \
|
|
+ return writeInst(PPC_##op | rs.code() << 21 | ra.code() << 16 | rb.code() << 11 | mb << 6); }
|
|
+
|
|
+/* NOTHING documents these encodings well, not OPPCC, not even the 3.1 ISA book. */
|
|
+#define DEF_MDFORM(op) \
|
|
+ BufferOffset Assembler::as_##op(Register ra, Register rs, uint8_t sh, uint8_t mb) {\
|
|
+ spew(#op "\t%3s,%3s,%d,%d", ra.name(), rs.name(), sh, mb); \
|
|
+ MOZ_ASSERT(sh < 64); MOZ_ASSERT(mb < 64); \
|
|
+ return writeInst(PPC_##op | rs.code() << 21 | ra.code() << 16 | ((sh & 0x1f) << 11) | ((mb & 0x1f) << 6) | (mb & 0x20) | ((sh & 0x20) >> 4)); }
|
|
+
|
|
+#define DEF_MDFORM_RC(op) \
|
|
+ BufferOffset Assembler::as_##op##_rc(Register ra, Register rs, uint8_t sh, uint8_t mb) {\
|
|
+ spew(#op ".\t%3s,%3s,%d,%d", ra.name(), rs.name(), sh, mb); \
|
|
+ MOZ_ASSERT(sh < 64); MOZ_ASSERT(mb < 64); \
|
|
+ return writeInst(PPC_##op | rs.code() << 21 | ra.code() << 16 | ((sh & 0x1f) << 11) | ((mb & 0x1f) << 6) | (mb & 0x20) | ((sh & 0x20) >> 4) | 0x01); }
|
|
+
|
|
+DEF_MFORM(rlwnm)
|
|
+DEF_MFORM_I(rlwinm)
|
|
+DEF_MFORM_I_RC(rlwinm)
|
|
+DEF_MFORM_I(rlwimi)
|
|
+DEF_XFORMS_I(srawi)
|
|
+
|
|
+DEF_MDSFORM(rldcl)
|
|
+//DEF_MDSFORM_RC(rldcl)
|
|
+//DEF_MDSFORM(rldcr)
|
|
+DEF_MDFORM(rldicl)
|
|
+DEF_MDFORM_RC(rldicl)
|
|
+DEF_MDFORM(rldicr)
|
|
+DEF_MDFORM_RC(rldicr)
|
|
+DEF_MDFORM(rldimi)
|
|
+//DEF_MDFORM_RC(rldimi)
|
|
+BufferOffset Assembler::as_sradi(Register rd, Register rs, int sh)
|
|
+{
|
|
+ spew("sradi\t%3s,%3s,%d", rd.name(), rs.name(), sh);
|
|
+ return writeInst(PPC_sradi | rd.code() << 16 | rs.code() << 21 |
|
|
+ (sh & 0x1f) << 11 | (sh & 0x20) >> 4);
|
|
+}
|
|
+
|
|
+#define DEF_ALU2(op) DEF_XFORM(op) DEF_XFORM_RC(op)
|
|
+
|
|
+DEF_ALU2(add)
|
|
+DEF_ALU2(addc)
|
|
+DEF_ALU2(adde)
|
|
+DEF_ALU2(addo)
|
|
+DEF_ALU2(subf)
|
|
+DEF_ALU2(subfc)
|
|
+DEF_ALU2(subfe)
|
|
+DEF_ALU2(subfo)
|
|
+DEF_ALU2(divd)
|
|
+DEF_ALU2(divdo)
|
|
+DEF_ALU2(divdu)
|
|
+DEF_ALU2(divduo)
|
|
+DEF_ALU2(divw)
|
|
+DEF_ALU2(divwo)
|
|
+DEF_ALU2(divwu)
|
|
+DEF_ALU2(divwuo)
|
|
+DEF_ALU2(mulld)
|
|
+DEF_ALU2(mulhd)
|
|
+DEF_ALU2(mulhdu)
|
|
+DEF_ALU2(mulldo)
|
|
+DEF_ALU2(mullw)
|
|
+DEF_ALU2(mulhw)
|
|
+DEF_ALU2(mulhwu)
|
|
+DEF_ALU2(mullwo)
|
|
+DEF_ALU2(eqv) // NB: Implemented differently.
|
|
+#undef DEF_ALU2
|
|
+
|
|
+#define DEF_ALU2_NORC(op) DEF_XFORM(op)
|
|
+DEF_ALU2_NORC(modsd)
|
|
+DEF_ALU2_NORC(modud)
|
|
+DEF_ALU2_NORC(modsw)
|
|
+DEF_ALU2_NORC(moduw)
|
|
+#undef DEF_ALU2_NORC
|
|
+
|
|
+#define DEF_ALUI(op) \
|
|
+ BufferOffset Assembler::as_##op(Register rd, Register ra, int16_t im) { \
|
|
+ spew(#op "\t%3s,%3s,%d", rd.name(), ra.name(), im); \
|
|
+ return writeInst(InstImm(PPC_##op, rd, ra, im).encode()); }\
|
|
+ BufferOffset Assembler::as_##op##_rc(Register rd, Register ra, int16_t im) { \
|
|
+ spew(#op ".\t%3s,%3s,%d", rd.name(), ra.name(), im); \
|
|
+ return writeInst(InstImm(PPC_##op, rd, ra, im).encode() | 0x1); }
|
|
+// mscdfr0
|
|
+BufferOffset Assembler::as_addi(Register rd, Register ra, int16_t im, bool actually_li) {
|
|
+#if DEBUG
|
|
+ if (actually_li) {
|
|
+ spew("li\t%3s,%d", rd.name(), im);
|
|
+ } else {
|
|
+ MOZ_ASSERT(ra != r0); // Because that would be li
|
|
+ spew("addi\t%3s,%3s,%d", rd.name(), ra.name(), im);
|
|
+ }
|
|
+#endif
|
|
+ return writeInst(InstImm(PPC_addi, rd, ra, im).encode());
|
|
+}
|
|
+BufferOffset Assembler::as_addis(Register rd, Register ra, int16_t im, bool actually_lis) {
|
|
+#if DEBUG
|
|
+ if (actually_lis) {
|
|
+ spew("lis\t%3s,%d", rd.name(), im);
|
|
+ } else {
|
|
+ MOZ_ASSERT(ra != r0); // Because that would be lis
|
|
+ spew("addis\t%3s,%3s,%d", rd.name(), ra.name(), im);
|
|
+ }
|
|
+#endif
|
|
+ return writeInst(InstImm(PPC_addis, rd, ra, im).encode());
|
|
+}
|
|
+
|
|
+DEF_ALUI(addic)
|
|
+// NB: mulli is usually strength-reduced, since it can take up to five
|
|
+// cycles in the worst case. See xs_sr_mulli.
|
|
+DEF_ALUI(mulli)
|
|
+DEF_ALUI(subfic)
|
|
+#undef DEF_ALUI
|
|
+
|
|
+#define DEF_ALUE(op) DEF_XFORM2(op) DEF_XFORM2_RC(op)
|
|
+DEF_ALUE(addme)
|
|
+DEF_ALUE(addze)
|
|
+DEF_ALUE(subfze)
|
|
+#undef DEF_ALUE
|
|
+
|
|
+#define DEF_ALUE(op) DEF_XFORM2S(op) DEF_XFORM2S_RC(op)
|
|
+DEF_ALUE(cntlzw) // NB: In this case, rd = ra and ra = rs, but no biggie here.
|
|
+DEF_ALUE(cntlzd) // NB: In this case, rd = ra and ra = rs, but no biggie here.
|
|
+DEF_ALUE(cnttzd) // NB: In this case, rd = ra and ra = rs, but no biggie here.
|
|
+DEF_ALUE(cnttzw) // NB: In this case, rd = ra and ra = rs, but no biggie here.
|
|
+#undef DEF_ALUE
|
|
+
|
|
+DEF_XFORM2S(popcntd)
|
|
+DEF_XFORM2S(popcntw)
|
|
+
|
|
+#define DEF_BITALU2(op) DEF_XFORMS(op) DEF_XFORMS_RC(op)
|
|
+DEF_BITALU2(andc)
|
|
+DEF_BITALU2(nand)
|
|
+DEF_BITALU2(nor)
|
|
+DEF_BITALU2(slw)
|
|
+DEF_BITALU2(srw)
|
|
+DEF_BITALU2(sraw)
|
|
+DEF_BITALU2(sld)
|
|
+DEF_BITALU2(srd)
|
|
+DEF_BITALU2(srad)
|
|
+DEF_BITALU2(and)
|
|
+//DEF_BITALU2(or)
|
|
+ BufferOffset Assembler::as_or(Register rd, Register ra, Register rb) {
|
|
+ spew("or\t%3s,%3s,%3s", rd.name(), ra.name(), rb.name());
|
|
+ MOZ_ASSERT(!(rd == ra && ra == rb));
|
|
+ return writeInst(InstReg(PPC_or, ra, rd, rb).encode()); }
|
|
+
|
|
+ BufferOffset Assembler::as_or_rc(Register rd, Register ra, Register rb) {
|
|
+ spew("or.\t%3s,%3s,%3s", rd.name(), ra.name(), rb.name());
|
|
+ return writeInst(InstReg(PPC_or, ra, rd, rb).encode() | 0x1); }
|
|
+
|
|
+DEF_BITALU2(xor)
|
|
+#undef DEF_BITALU2
|
|
+
|
|
+// No Rc bit for these.
|
|
+#define DEF_BITALUI(op) DEF_DFORMS(op)
|
|
+DEF_BITALUI(ori)
|
|
+DEF_BITALUI(oris)
|
|
+DEF_BITALUI(xori)
|
|
+DEF_BITALUI(xoris)
|
|
+#undef DEF_BITALUI
|
|
+// Implied Rc bit for these.
|
|
+ BufferOffset Assembler::as_andi_rc(Register ra, Register rs, uint16_t im) {
|
|
+ spew("andi.\t%3s,%3s,%d", ra.name(), rs.name(), im);
|
|
+ return writeInst(InstImm(PPC_andi, rs, ra, im).encode()); }
|
|
+
|
|
+ BufferOffset Assembler::as_andis_rc(Register ra, Register rs, uint16_t im) {
|
|
+ spew("andis.\t%3s,%3s,%d", ra.name(), rs.name(), im);
|
|
+ return writeInst(InstImm(PPC_andis, rs, ra, im).encode()); }
|
|
+
|
|
+#define DEF_ALUEXT(op) DEF_XFORM2S(op) DEF_XFORM2S_RC(op)
|
|
+DEF_ALUEXT(extsb)
|
|
+DEF_ALUEXT(extsh)
|
|
+DEF_ALUEXT(extsw)
|
|
+#undef DEF_ALUEXT
|
|
+
|
|
+#define DEF_MEMd(op) DEF_DFORM(op)
|
|
+DEF_MEMd(lbz)
|
|
+DEF_MEMd(lha)
|
|
+DEF_MEMd(lhz)
|
|
+ BufferOffset Assembler::as_lwa(Register ra, Register rs, int16_t im) {
|
|
+ spew("lwa\t%3s,%d(%3s)", ra.name(), im, rs.name());
|
|
+ MOZ_ASSERT(rs != r0);
|
|
+ MOZ_ASSERT(!(im & 0x03));
|
|
+ return writeInst(InstImm(PPC_lwa, ra, rs, im).encode()); }
|
|
+
|
|
+DEF_MEMd(lwz)
|
|
+//DEF_MEMd(ld)
|
|
+// Assert if the two LSBs of ld's immediate are set, since this assembles
|
|
+// to a different instruction. (E.g., if we want ldu, we should ask for it.)
|
|
+ BufferOffset Assembler::as_ld(Register ra, Register rs, int16_t im) {
|
|
+ spew("ld\t%3s,%d(%3s)", ra.name(), im, rs.name());
|
|
+ MOZ_ASSERT(rs != r0);
|
|
+ MOZ_ASSERT(!(im & 0x03));
|
|
+ return writeInst(InstImm(PPC_ld, ra, rs, im).encode()); }
|
|
+
|
|
+DEF_MEMd(stb)
|
|
+DEF_MEMd(stw)
|
|
+DEF_MEMd(stwu)
|
|
+DEF_MEMd(sth)
|
|
+//DEF_MEMd(std)
|
|
+ BufferOffset Assembler::as_std(Register ra, Register rs, int16_t im) {
|
|
+ spew("std\t%3s,%d(%3s)", ra.name(), im, rs.name());
|
|
+ MOZ_ASSERT(rs != r0);
|
|
+ MOZ_ASSERT(!(im & 0x03));
|
|
+ return writeInst(InstImm(PPC_std, ra, rs, im).encode()); }
|
|
+DEF_MEMd(stdu)
|
|
+#undef DEF_MEMd
|
|
+
|
|
+#define DEF_MEMx(op) DEF_XFORM(op)
|
|
+DEF_MEMx(lbzx)
|
|
+DEF_MEMx(lhax)
|
|
+DEF_MEMx(lhzx)
|
|
+DEF_MEMx(lhbrx)
|
|
+DEF_MEMx(lwzx)
|
|
+DEF_MEMx(lwbrx)
|
|
+DEF_MEMx(lwax)
|
|
+DEF_MEMx(lwarx)
|
|
+DEF_MEMx(ldx)
|
|
+DEF_MEMx(ldarx)
|
|
+
|
|
+DEF_MEMx(stbx)
|
|
+DEF_MEMx(stwx)
|
|
+DEF_MEMx(stwux)
|
|
+DEF_MEMx(stwbrx)
|
|
+DEF_MEMx(sthx)
|
|
+DEF_MEMx(sthbrx)
|
|
+DEF_MEMx(stdx)
|
|
+DEF_MEMx(stdcx)
|
|
+DEF_MEMx(stdux)
|
|
+DEF_MEMx(stwcx)
|
|
+#undef DEF_MEMx
|
|
+
|
|
+BufferOffset Assembler::as_isel(Register rt, Register ra, Register rb, uint16_t bc, CRegisterID cr)
|
|
+{
|
|
+ MOZ_ASSERT(ra != r0); // mscdfr0, but see below, because sometimes we want this
|
|
+ return as_isel0(rt, ra, rb, bc, cr);
|
|
+}
|
|
+
|
|
+// This variant allows ra to be r0, because sometimes we feel like a zero.
|
|
+// Sometimes you don't. Almond Joy's got nuts, Mounds don't.
|
|
+BufferOffset Assembler::as_isel0(Register rt, Register ra, Register rb, uint16_t bc, CRegisterID cr)
|
|
+{
|
|
+ spew("isel\t%3s,%3s,%3s,cr%d:0x%02x", rt.name(), ra.name(), rb.name(), cr, bc);
|
|
+ // Only bits that can be directly tested for in the CR are valid.
|
|
+ // The upper nybble of the condition contains the CR bit.
|
|
+ MOZ_ASSERT((bc < 0x40) && ((bc & 0x0f) == 0x0c));
|
|
+ uint16_t nbc = (bc >> 4) + (cr << 2);
|
|
+ return writeInst(PPC_isel | rt.code() << 21 | ra.code() << 16 | rb.code() << 11 | nbc << 6);
|
|
+}
|
|
+
|
|
+// FPR operations and load-stores.
|
|
+BufferOffset Assembler::as_fcmpo(CRegisterID cr, FloatRegister ra, FloatRegister rb)
|
|
+{
|
|
+ spew("fcmpo\t%d,%3s,%3s", cr, ra.name(), rb.name());
|
|
+ return writeInst(PPC_fcmpo | cr << 23 | ra.encoding() << 16 | rb.encoding() << 11);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_fcmpo(FloatRegister ra, FloatRegister rb)
|
|
+{
|
|
+ return as_fcmpo(cr0, ra, rb);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_fcmpu(CRegisterID cr, FloatRegister ra, FloatRegister rb)
|
|
+{
|
|
+ spew("fcmpu\t%d,%3s,%3s", cr, ra.name(), rb.name());
|
|
+ return writeInst(PPC_fcmpu | cr << 23 | ra.encoding() << 16 | rb.encoding() << 11);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_fcmpu(FloatRegister ra, FloatRegister rb)
|
|
+{
|
|
+ return as_fcmpu(cr0, ra, rb);
|
|
+}
|
|
+
|
|
+#define DEF_FPUAC(op) DEF_AFORM_C(op) DEF_AFORM_C_RC(op)
|
|
+DEF_FPUAC(fmul)
|
|
+DEF_FPUAC(fmuls)
|
|
+#undef DEF_FPUAC
|
|
+
|
|
+#define DEF_FPUAB(op) DEF_AFORM_B(op) DEF_AFORM_B_RC(op)
|
|
+DEF_FPUAB(fadd)
|
|
+DEF_FPUAB(fdiv)
|
|
+DEF_FPUAB(fsub)
|
|
+DEF_FPUAB(fadds)
|
|
+DEF_FPUAB(fdivs)
|
|
+DEF_FPUAB(fsubs)
|
|
+DEF_FPUAB(fcpsgn)
|
|
+DEF_FPUAB(fmrgew) /* rc form invalid */
|
|
+#undef DEF_FPUAB
|
|
+
|
|
+#define DEF_FPUDS(op) DEF_XFORM2_F(op) DEF_XFORM2_F_RC(op)
|
|
+DEF_FPUDS(fabs)
|
|
+DEF_FPUDS(fneg)
|
|
+DEF_FPUDS(fmr)
|
|
+DEF_FPUDS(fcfid)
|
|
+DEF_FPUDS(fcfids)
|
|
+DEF_FPUDS(fcfidu)
|
|
+DEF_FPUDS(fcfidus)
|
|
+DEF_FPUDS(fctid)
|
|
+DEF_FPUDS(fctidz)
|
|
+DEF_FPUDS(fctidu)
|
|
+DEF_FPUDS(fctiduz)
|
|
+DEF_FPUDS(fctiw)
|
|
+DEF_FPUDS(fctiwz)
|
|
+DEF_FPUDS(fctiwu)
|
|
+DEF_FPUDS(fctiwuz)
|
|
+DEF_FPUDS(frim)
|
|
+DEF_FPUDS(frin)
|
|
+DEF_FPUDS(frip)
|
|
+DEF_FPUDS(friz)
|
|
+DEF_FPUDS(frsp)
|
|
+DEF_FPUDS(frsqrte)
|
|
+DEF_FPUDS(fsqrt)
|
|
+DEF_FPUDS(fsqrts)
|
|
+#undef DEF_FPUDS
|
|
+
|
|
+// In Ion, the semantics for this macro are now corrected compared to JM/PPCBC.
|
|
+// (See OPPCC p.432, etc.)
|
|
+#define DEF_FPUACB(op) DEF_AFORM(op) DEF_AFORM_RC(op)
|
|
+DEF_FPUACB(fmadd)
|
|
+DEF_FPUACB(fnmsub)
|
|
+DEF_FPUACB(fsel)
|
|
+#undef DEF_FPUACB
|
|
+
|
|
+#define DEF_FMEMd(op) DEF_DFORM_F(op)
|
|
+DEF_FMEMd(lfd)
|
|
+DEF_FMEMd(lfs)
|
|
+DEF_FMEMd(stfd)
|
|
+DEF_FMEMd(stfs)
|
|
+DEF_FMEMd(stfdu)
|
|
+DEF_FMEMd(stfsu)
|
|
+#undef DEF_FMEMd
|
|
+
|
|
+#define DEF_FMEMx(op) \
|
|
+ BufferOffset Assembler::as_##op(FloatRegister rd, Register ra, Register rb) { \
|
|
+ spew(#op "\t%3s,%3s,%3s", rd.name(), ra.name(), rb.name()); \
|
|
+ return writeInst(XForm(PPC_##op, rd, ra, rb, false)); }
|
|
+DEF_FMEMx(lfdx)
|
|
+DEF_FMEMx(lfsx)
|
|
+DEF_FMEMx(lfiwax)
|
|
+DEF_FMEMx(stfiwx)
|
|
+DEF_FMEMx(stfdx)
|
|
+DEF_FMEMx(stfsx)
|
|
+#undef DEF_FMEMx
|
|
+
|
|
+BufferOffset Assembler::as_mtfsb0(uint8_t bt)
|
|
+{
|
|
+ spew("mtfsb0\t%d", bt);
|
|
+ return writeInst(PPC_mtfsb0 | (uint32_t)bt << 21);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_mtfsb1(uint8_t bt)
|
|
+{
|
|
+ spew("mtfsb1\t%d", bt);
|
|
+ return writeInst(PPC_mtfsb1 | (uint32_t)bt << 21);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_mtfsfi(uint8_t fi, uint8_t imm)
|
|
+{
|
|
+ spew("mtfsfi\t%d,%d", fi, imm);
|
|
+ return writeInst(PPC_mtfsfi | fi << 23 | imm << 12);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_mcrf(CRegisterID bt, CRegisterID bs)
|
|
+{
|
|
+ spew("mcrf\t%d,%d", bt, bs);
|
|
+ return writeInst(PPC_mcrf | (uint32_t)bt << 23 | (uint32_t)bs << 18);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_mcrfs(CRegisterID bf, uint8_t bfa)
|
|
+{
|
|
+ spew("mcrfs\t%d,%d", bf, bfa);
|
|
+ return writeInst(PPC_mcrfs | (uint32_t)bf << 23 | (uint32_t)bfa << 18);
|
|
+}
|
|
+
|
|
+// VSX
|
|
+// Currently only supported for FPRs.
|
|
+// No RC forms for these (least significant bit sets vector or FPR).
|
|
+ BufferOffset Assembler::as_mfvsrd(Register ra, FloatRegister xs) {
|
|
+ spew("mfvsrd\t%3s,%3s", ra.name(), xs.name());
|
|
+ return writeInst(XForm(PPC_mfvsrd, xs, ra, r0, false));
|
|
+ }
|
|
+ BufferOffset Assembler::as_mtvsrd(FloatRegister xt, Register ra) {
|
|
+ spew("mtvsrd\t%3s,%3s", xt.name(), ra.name());
|
|
+ // Yes, same operand order (see PowerISA v3.1 page 121)
|
|
+ return writeInst(XForm(PPC_mtvsrd, xt, ra, r0, false));
|
|
+ }
|
|
+ BufferOffset Assembler::as_mtvsrws(FloatRegister xt, Register ra) {
|
|
+ spew("mtvsrws\t%3s,%3s", xt.name(), ra.name());
|
|
+ return writeInst(XForm(PPC_mtvsrws, xt, ra, r0, false));
|
|
+ }
|
|
+ BufferOffset Assembler::as_mtvsrwz(FloatRegister xt, Register ra) {
|
|
+ spew("mtvsrwz\t%3s,%3s", xt.name(), ra.name());
|
|
+ return writeInst(XForm(PPC_mtvsrwz, xt, ra, r0, false));
|
|
+ }
|
|
+ BufferOffset Assembler::as_xxbrd(FloatRegister xt, FloatRegister xb) {
|
|
+ spew("xxbrd\t%3s,%3s", xt.name(), xb.name());
|
|
+ return writeInst(XForm(PPC_xxbrd, xt, f0, xb, false));
|
|
+ }
|
|
+ BufferOffset Assembler::as_xscvdpsp(FloatRegister xt, FloatRegister xb) {
|
|
+ spew("xscvdpsp\t%3s,%3s", xt.name(), xb.name());
|
|
+ return writeInst(XForm(PPC_xscvdpsp, xt, f0, xb, false));
|
|
+ }
|
|
+ BufferOffset Assembler::as_xscvspdp(FloatRegister xt, FloatRegister xb) {
|
|
+ spew("xscvspdp\t%3s,%3s", xt.name(), xb.name());
|
|
+ return writeInst(XForm(PPC_xscvspdp, xt, f0, xb, false));
|
|
+ }
|
|
+ BufferOffset Assembler::as_xscvdpspn(FloatRegister xt, FloatRegister xb) {
|
|
+ spew("xscvdpspn\t%3s,%3s", xt.name(), xb.name());
|
|
+ return writeInst(XForm(PPC_xscvdpspn, xt, f0, xb, false));
|
|
+ }
|
|
+ BufferOffset Assembler::as_xscvspdpn(FloatRegister xt, FloatRegister xb) {
|
|
+ spew("xscvspdpn\t%3s,%3s", xt.name(), xb.name());
|
|
+ return writeInst(XForm(PPC_xscvspdpn, xt, f0, xb, false));
|
|
+ }
|
|
+ BufferOffset Assembler::as_xxlxor(FloatRegister xt, FloatRegister xa, FloatRegister xb) {
|
|
+ spew("xxlxor\t%3s,%3s,%3s", xt.name(), xa.name(), xb.name());
|
|
+ return writeInst(XForm(PPC_xxlxor, xt, xa, xb, false));
|
|
+ }
|
|
+
|
|
+ BufferOffset Assembler::as_addpcis(Register rt, uint16_t im) {
|
|
+ spew("addpcis\t%s,%d", rt.name(), im);
|
|
+ MOZ_ASSERT(im == 0); // not implemented for anything other than lnia
|
|
+
|
|
+ return writeInst(PPC_addpcis | (rt.code() << 21));
|
|
+ }
|
|
+
|
|
+// Conveniences and generally accepted alternate mnemonics.
|
|
+// XXX: change these to xs_
|
|
+BufferOffset Assembler::xs_trap()
|
|
+{
|
|
+ spew("trap @ %08x", currentOffset());
|
|
+ return writeInst(PPC_trap);
|
|
+}
|
|
+// trap with metadata encoded as register
|
|
+BufferOffset Assembler::xs_trap_tagged(TrapTag tag) {
|
|
+ uint32_t tv = PPC_trap | ((uint8_t)tag << 16) | ((uint8_t)tag << 11);
|
|
+ spew("trap @ %08x ; MARK %d %08x", currentOffset(), (uint8_t)tag, tv);
|
|
+ return writeInst(tv);
|
|
+}
|
|
+Assembler::TrapTag InstImm::traptag() {
|
|
+ // Extract a tag from a tagged trap instruction.
|
|
+ uint8_t r = ((data & 0x001f0000) >> 16);
|
|
+ MOZ_ASSERT(isOpcode(PPC_tw)); // not a trap
|
|
+ MOZ_ASSERT(r == ((data & 0x0000f800) >> 11)); // probably not a tagged trap
|
|
+ return (Assembler::TrapTag)(r & 0xfe); // mask bit 0
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::xs_mr(Register rd, Register ra)
|
|
+{
|
|
+ return as_or(rd, ra, ra);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::x_beq(CRegisterID cr, int16_t off, LikelyBit lkb, LinkBit lb)
|
|
+{
|
|
+ return as_bc(off, Equal, cr, lkb, lb);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::x_bne(CRegisterID cr, int16_t off, LikelyBit lkb, LinkBit lb)
|
|
+{
|
|
+ return as_bc(off, NotEqual, cr, lkb, lb);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::xs_bdnz(int16_t off, LikelyBit lkb, LinkBit lb)
|
|
+{
|
|
+ spew("bdnz .+%d", off);
|
|
+ MOZ_ASSERT(!(off & 0x03));
|
|
+ return writeInst(PPC_bc | (0x10 << 21) | (off & 0xfffc) | lkb << 21 | lb);
|
|
+}
|
|
+
|
|
+// Emit specialized bcl form to avoid tainting branch history.
|
|
+// Link bit is implied; this is meaningless without it.
|
|
+BufferOffset Assembler::xs_bcl_always(int16_t off, LikelyBit lkb)
|
|
+{
|
|
+ spew("bcl 20,4*cr7+so,.+%d", off);
|
|
+ MOZ_ASSERT(!(off & 0x03));
|
|
+ return writeInst(PPC_bc | (20 << 21) | (31 << 16) | (off & 0xfffc) | 0x01);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::xs_mtctr(Register ra)
|
|
+{
|
|
+ return as_mtspr(ctr, ra);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::xs_mtlr(Register ra)
|
|
+{
|
|
+ return as_mtspr(lr_spr, ra);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::xs_mflr(Register rd)
|
|
+{
|
|
+ return as_mfspr(rd, lr_spr);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::xs_mtcr(Register rs)
|
|
+{
|
|
+ return as_mtcrf(0xff, rs);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::xs_mfxer(Register ra)
|
|
+{
|
|
+ return as_mfspr(ra, xer);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::xs_mtxer(Register ra)
|
|
+{
|
|
+ return as_mtspr(xer, ra);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::x_bit_value(Register rd, Register rs, unsigned bit)
|
|
+{
|
|
+ return as_rlwinm(rd, rs, bit + 1, 31, 31);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::x_slwi(Register rd, Register rs, int n)
|
|
+{
|
|
+ return as_rlwinm(rd, rs, n, 0, 31 - n);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::x_sldi(Register rd, Register rs, int n)
|
|
+{
|
|
+ return as_rldicr(rd, rs, n, 63 - n);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::x_srwi(Register rd, Register rs, int n)
|
|
+{
|
|
+ return as_rlwinm(rd, rs, 32 - n, n, 31);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::x_srdi(Register rd, Register rs, int n)
|
|
+{
|
|
+ return as_rldicl(rd, rs, 64 - n, n);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::x_subi(Register rd, Register ra, int16_t im)
|
|
+{
|
|
+ return as_addi(rd, ra, -im);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::xs_li(Register rd, int16_t im)
|
|
+{
|
|
+ return as_addi(rd, r0, im, true /* actually_li */);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::xs_lis(Register rd, int16_t im)
|
|
+{
|
|
+ return as_addis(rd, r0, im, true /* actually_lis */);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::x_not(Register rd, Register ra)
|
|
+{
|
|
+ return as_nor(rd, ra, ra);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::xs_sr_mulli(Register rd, Register ra, int16_t im)
|
|
+{
|
|
+ // XXX: expand this
|
|
+ if (im == -1) {
|
|
+ return as_neg(rd, ra);
|
|
+ }
|
|
+ if (im == 0) {
|
|
+ return xs_li(rd, 0);
|
|
+ }
|
|
+ if (im == 1) {
|
|
+ if (ra != rd) {
|
|
+ return xs_mr(rd, ra);
|
|
+ }
|
|
+ return BufferOffset(currentOffset());
|
|
+ }
|
|
+ if (im == 2) {
|
|
+ return as_add(rd, ra, ra);
|
|
+ }
|
|
+ if (im == 3 && rd != ra) {
|
|
+ as_add(rd, ra, ra);
|
|
+ return as_add(rd, rd, ra);
|
|
+ }
|
|
+ // XXX: convert 2^x to bit shift
|
|
+
|
|
+ return as_mulli(rd, ra, im);
|
|
+}
|
|
+
|
|
+// Traps
|
|
+BufferOffset Assembler::as_tw(uint8_t to, Register ra, Register rb)
|
|
+{
|
|
+ return writeInst(PPC_tw | (uint32_t)to << 21 | ra.code() << 16 |
|
|
+ rb.code() << 11);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_twi(uint8_t to, Register ra, int16_t si)
|
|
+{
|
|
+ return writeInst(PPC_twi | (uint32_t)to << 21 | ra.code() << 16 | si);
|
|
+}
|
|
+
|
|
+BufferOffset Assembler::as_stop()
|
|
+{
|
|
+ spew("stop!!!\n");
|
|
+ return writeInst(PPC_stop);
|
|
+}
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/Assembler-ppc64.h
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/Assembler-ppc64.h Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,1847 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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_ppc_Assembler_ppc_h
|
|
+#define jit_ppc_Assembler_ppc_h
|
|
+
|
|
+/* Mostly derived from TenFourFox IonPower (r.i.p.). */
|
|
+
|
|
+#include "mozilla/ArrayUtils.h"
|
|
+#include "mozilla/Attributes.h"
|
|
+#include "mozilla/MathAlgorithms.h"
|
|
+
|
|
+#include "jit/CompactBuffer.h"
|
|
+#include "jit/JitCode.h"
|
|
+#include "jit/JitSpewer.h"
|
|
+#include "jit/ppc64/Architecture-ppc64.h"
|
|
+#include "jit/shared/Assembler-shared.h"
|
|
+#include "jit/shared/IonAssemblerBuffer.h"
|
|
+
|
|
+#if DEBUG
|
|
+#define ispew(x) JitSpew(JitSpew_Codegen, "== " x " ==")
|
|
+#else
|
|
+#define ispew(x) ;
|
|
+#endif
|
|
+
|
|
+namespace js {
|
|
+namespace jit {
|
|
+
|
|
+static constexpr Register r0{ Registers::r0 };
|
|
+static constexpr Register r1{ Registers::r1 };
|
|
+static constexpr Register sp{ Registers::r1 };
|
|
+static constexpr Register r2{ Registers::r2 };
|
|
+static constexpr Register r3{ Registers::r3 };
|
|
+static constexpr Register r4{ Registers::r4 };
|
|
+static constexpr Register r5{ Registers::r5 };
|
|
+static constexpr Register r6{ Registers::r6 };
|
|
+static constexpr Register r7{ Registers::r7 };
|
|
+static constexpr Register r8{ Registers::r8 };
|
|
+static constexpr Register r9{ Registers::r9 };
|
|
+static constexpr Register r10{ Registers::r10 };
|
|
+static constexpr Register r11{ Registers::r11 };
|
|
+static constexpr Register r12{ Registers::r12 };
|
|
+static constexpr Register r13{ Registers::r13 };
|
|
+static constexpr Register r14{ Registers::r14 };
|
|
+static constexpr Register r15{ Registers::r15 };
|
|
+static constexpr Register r16{ Registers::r16 };
|
|
+static constexpr Register r17{ Registers::r17 };
|
|
+static constexpr Register r18{ Registers::r18 };
|
|
+static constexpr Register r19{ Registers::r19 };
|
|
+static constexpr Register r20{ Registers::r20 };
|
|
+static constexpr Register r21{ Registers::r21 };
|
|
+static constexpr Register r22{ Registers::r22 };
|
|
+static constexpr Register r23{ Registers::r23 };
|
|
+static constexpr Register r24{ Registers::r24 };
|
|
+static constexpr Register r25{ Registers::r25 };
|
|
+static constexpr Register r26{ Registers::r26 };
|
|
+static constexpr Register r27{ Registers::r27 };
|
|
+static constexpr Register r28{ Registers::r28 };
|
|
+static constexpr Register r29{ Registers::r29 };
|
|
+static constexpr Register r30{ Registers::r30 };
|
|
+static constexpr Register r31{ Registers::r31 };
|
|
+
|
|
+static constexpr FloatRegister f0{ FloatRegisters::f0, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f1{ FloatRegisters::f1, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f2{ FloatRegisters::f2, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f3{ FloatRegisters::f3, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f4{ FloatRegisters::f4, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f5{ FloatRegisters::f5, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f6{ FloatRegisters::f6, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f7{ FloatRegisters::f7, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f8{ FloatRegisters::f8, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f9{ FloatRegisters::f9, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f10{ FloatRegisters::f10, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f11{ FloatRegisters::f11, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f12{ FloatRegisters::f12, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f13{ FloatRegisters::f13, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f14{ FloatRegisters::f14, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f15{ FloatRegisters::f15, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f16{ FloatRegisters::f16, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f17{ FloatRegisters::f17, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f18{ FloatRegisters::f18, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f19{ FloatRegisters::f19, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f20{ FloatRegisters::f20, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f21{ FloatRegisters::f21, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f22{ FloatRegisters::f22, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f23{ FloatRegisters::f23, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f24{ FloatRegisters::f24, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f25{ FloatRegisters::f25, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f26{ FloatRegisters::f26, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f27{ FloatRegisters::f27, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f28{ FloatRegisters::f28, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f29{ FloatRegisters::f29, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f30{ FloatRegisters::f30, FloatRegisters::Double };
|
|
+static constexpr FloatRegister f31{ FloatRegisters::f31, FloatRegisters::Double };
|
|
+// The rest of the FPRs are the business of the allocator, not the assembler.
|
|
+// SPRs and CRs are defined in their respective enums (see Architecture-ppc.h).
|
|
+
|
|
+static constexpr Register OsrFrameReg = r6;
|
|
+static constexpr Register ArgumentsRectifierReg = r19;
|
|
+static constexpr Register CallTempReg0 = r8;
|
|
+static constexpr Register CallTempReg1 = r9;
|
|
+static constexpr Register CallTempReg2 = r10;
|
|
+static constexpr Register CallTempReg3 = r7;
|
|
+static constexpr Register CallTempReg4 = r5; // Bad things! Try not to use these!
|
|
+static constexpr Register CallTempReg5 = r6;
|
|
+
|
|
+static constexpr Register InterpreterPCReg = r17;
|
|
+
|
|
+// irregexp
|
|
+static constexpr Register IntArgReg0 = r3;
|
|
+static constexpr Register IntArgReg1 = r4;
|
|
+static constexpr Register IntArgReg2 = r5;
|
|
+static constexpr Register IntArgReg3 = r6;
|
|
+static constexpr Register IntArgReg4 = r7;
|
|
+static constexpr Register IntArgReg5 = r8;
|
|
+static constexpr Register IntArgReg6 = r9;
|
|
+static constexpr Register IntArgReg7 = r10;
|
|
+
|
|
+static constexpr Register GlobalReg = r23; // used by AsmJS. Allocatable, but non-volatile. Must not clash with wasm.
|
|
+static constexpr Register HeapReg = r24; // Ditto.
|
|
+
|
|
+// These are defined, but not actually used, at least by us (see GetTempRegForIntArg).
|
|
+static constexpr Register CallTempNonArgRegs[] = { r10, r9, r8, r7 };
|
|
+static const uint32_t NumCallTempNonArgRegs = mozilla::ArrayLength(CallTempNonArgRegs);
|
|
+
|
|
+class ABIArgGenerator
|
|
+{
|
|
+ uint32_t stackOffset_;
|
|
+ uint32_t usedGPRs_;
|
|
+ uint32_t usedFPRs_;
|
|
+ ABIArg current_;
|
|
+
|
|
+ public:
|
|
+ ABIArgGenerator();
|
|
+ ABIArg next(MIRType argType);
|
|
+ ABIArg ¤t() { return current_; }
|
|
+
|
|
+ uint32_t stackBytesConsumedSoFar() const { return stackOffset_; }
|
|
+ void increaseStackOffset(uint32_t bytes) { stackOffset_ += bytes; }
|
|
+};
|
|
+
|
|
+static constexpr Register ABINonArgReg0 = r19;
|
|
+static constexpr Register ABINonArgReg1 = r20;
|
|
+static constexpr Register ABINonArgReg2 = r21;
|
|
+static constexpr Register ABINonArgReg3 = r22;
|
|
+// These can be non-volatile; they are only used by Wasm, and only after
|
|
+// all registers have been spilled. These must not be argregs as they may
|
|
+// be used after all argregs are exhausted.
|
|
+static constexpr Register ABINonArgReturnReg0 = r29;
|
|
+static constexpr Register ABINonArgReturnReg1 = r30;
|
|
+static constexpr Register ABINonArgReturnVolatileReg = r11;
|
|
+static constexpr Register ABINonVolatileReg = r14;
|
|
+
|
|
+static constexpr Register PreBarrierReg = r4;
|
|
+
|
|
+static constexpr Register InvalidReg{ Registers::invalid_reg };
|
|
+static constexpr FloatRegister InvalidFloatReg;
|
|
+
|
|
+static constexpr Register StackPointer = sp;
|
|
+static constexpr Register FramePointer = r31;
|
|
+
|
|
+static constexpr Register ScratchRegister = r0;
|
|
+static constexpr Register SecondScratchReg = r12;
|
|
+static constexpr Register ThirdScratchReg = r11; // EMERGENCY! RESCUE r11!
|
|
+
|
|
+// All return registers must be allocatable.
|
|
+static constexpr Register JSReturnReg_Type = r6;
|
|
+static constexpr Register JSReturnReg_Data = r5;
|
|
+static constexpr Register JSReturnReg = r4;
|
|
+static constexpr Register ReturnReg = r3;
|
|
+static constexpr Register64 ReturnReg64{ReturnReg};
|
|
+static constexpr FloatRegister ReturnFloat32Reg = {FloatRegisters::f1,
|
|
+ FloatRegisters::Single};
|
|
+static constexpr FloatRegister ReturnDoubleReg = {FloatRegisters::f1,
|
|
+ FloatRegisters::Double};
|
|
+static constexpr FloatRegister ABINonArgDoubleReg = {FloatRegisters::f14,
|
|
+ FloatRegisters::Double};
|
|
+static constexpr ValueOperand JSReturnOperand = ValueOperand(JSReturnReg);
|
|
+
|
|
+// Registers used by RegExpMatcher and RegExpExecMatch stubs (do not use
|
|
+// JSReturnOperand).
|
|
+static constexpr Register RegExpMatcherRegExpReg = CallTempReg0;
|
|
+static constexpr Register RegExpMatcherStringReg = CallTempReg1;
|
|
+static constexpr Register RegExpMatcherLastIndexReg = CallTempReg2;
|
|
+
|
|
+// Registers used by RegExpExecTest stub (do not use ReturnReg).
|
|
+static constexpr Register RegExpExecTestRegExpReg = CallTempReg0;
|
|
+static constexpr Register RegExpExecTestStringReg = CallTempReg1;
|
|
+
|
|
+// Registers used by RegExpSearcher stub (do not use ReturnReg).
|
|
+static constexpr Register RegExpSearcherRegExpReg = CallTempReg0;
|
|
+static constexpr Register RegExpSearcherStringReg = CallTempReg1;
|
|
+static constexpr Register RegExpSearcherLastIndexReg = CallTempReg2;
|
|
+
|
|
+// TLS pointer argument register for WebAssembly functions. This must not alias
|
|
+// any other register used for passing function arguments or return values.
|
|
+// Preserved by WebAssembly functions.
|
|
+static constexpr Register InstanceReg = r18;
|
|
+
|
|
+// Registers used for wasm table calls. These registers must be disjoint
|
|
+// from the ABI argument registers, WasmTlsReg and each other.
|
|
+static constexpr Register WasmTableCallScratchReg0 = ABINonArgReg0;
|
|
+static constexpr Register WasmTableCallScratchReg1 = ABINonArgReg1;
|
|
+static constexpr Register WasmTableCallSigReg = ABINonArgReg2;
|
|
+static constexpr Register WasmTableCallIndexReg = ABINonArgReg3;
|
|
+
|
|
+// Registers used for ref calls.
|
|
+static constexpr Register WasmCallRefCallScratchReg0 = ABINonArgReg0;
|
|
+static constexpr Register WasmCallRefCallScratchReg1 = ABINonArgReg1;
|
|
+static constexpr Register WasmCallRefReg = ABINonArgReg3;
|
|
+
|
|
+// Register used as a scratch along the return path in the fast js -> wasm stub
|
|
+// code. This must not overlap ReturnReg, JSReturnOperand, or WasmTlsReg. It
|
|
+// must be a volatile register.
|
|
+static constexpr Register WasmJitEntryReturnScratch = r10;
|
|
+
|
|
+static constexpr uint32_t WasmCheckedCallEntryOffset = 0u;
|
|
+static constexpr uint32_t WasmCheckedTailEntryOffset = 32u; // damn mtspr
|
|
+
|
|
+// Good grief. Must FPRs be vector registers on every architecture?
|
|
+// I guess we could only support SIMD on processors with VSX.
|
|
+static constexpr FloatRegister ReturnSimdReg = InvalidFloatReg;
|
|
+static constexpr FloatRegister ReturnSimd128Reg = InvalidFloatReg;
|
|
+static constexpr FloatRegister ReturnInt32x4Reg = InvalidFloatReg;
|
|
+static constexpr FloatRegister ReturnFloat32x4Reg = InvalidFloatReg;
|
|
+static constexpr FloatRegister ScratchSimdReg = InvalidFloatReg;
|
|
+static constexpr FloatRegister ScratchSimd128Reg = InvalidFloatReg;
|
|
+
|
|
+static constexpr FloatRegister ScratchFloat32Reg = {FloatRegisters::f0,
|
|
+ FloatRegisters::Single};
|
|
+static constexpr FloatRegister ScratchDoubleReg = {FloatRegisters::f0,
|
|
+ FloatRegisters::Double};
|
|
+
|
|
+struct ScratchFloat32Scope : public AutoFloatRegisterScope {
|
|
+ explicit ScratchFloat32Scope(MacroAssembler& masm)
|
|
+ : AutoFloatRegisterScope(masm, ScratchFloat32Reg) {}
|
|
+};
|
|
+struct ScratchDoubleScope : public AutoFloatRegisterScope {
|
|
+ explicit ScratchDoubleScope(MacroAssembler& masm)
|
|
+ : AutoFloatRegisterScope(masm, ScratchDoubleReg) {}
|
|
+};
|
|
+
|
|
+// A bias applied to the GlobalReg to allow the use of instructions with small
|
|
+// negative immediate offsets which doubles the range of global data that can be
|
|
+// accessed with a single instruction. (XXX)
|
|
+static const int32_t AsmJSGlobalRegBias = 32768;
|
|
+
|
|
+// Registers used in the GenerateFFIIonExit Enable Activation block. (Mirror MIPS.)
|
|
+static constexpr Register AsmJSIonExitRegCallee = r7;
|
|
+static constexpr Register AsmJSIonExitRegE0 = r3;
|
|
+static constexpr Register AsmJSIonExitRegE1 = r4;
|
|
+static constexpr Register AsmJSIonExitRegE2 = r5;
|
|
+static constexpr Register AsmJSIonExitRegE3 = r6;
|
|
+
|
|
+// Registers used in the GenerateFFIIonExit Disable Activation block.
|
|
+static constexpr Register AsmJSIonExitRegReturnData = JSReturnReg_Data;
|
|
+static constexpr Register AsmJSIonExitRegReturnType = JSReturnReg_Type;
|
|
+static constexpr Register AsmJSIonExitRegD0 = r3;
|
|
+static constexpr Register AsmJSIonExitRegD1 = r4;
|
|
+static constexpr Register AsmJSIonExitRegD2 = r7;
|
|
+
|
|
+static const uint32_t ABIStackAlignment = 16;
|
|
+static const uint32_t CodeAlignment = 16;
|
|
+// Ion code only. The 8-alignment is necessary because frames always have the return address
|
|
+// at the top of the stack and are not padded (see the common frame layout in JitFrames.h).
|
|
+static const uint32_t StackAlignment = 8;
|
|
+static const uint32_t JitStackAlignment = 16;
|
|
+static const uint32_t JitStackValueAlignment = 2;
|
|
+
|
|
+// Helper classes for ScratchRegister usage. Asserts that only one piece
|
|
+// of code thinks it has exclusive ownership of each scratch register.
|
|
+struct ScratchRegisterScope : public AutoRegisterScope {
|
|
+ explicit ScratchRegisterScope(MacroAssembler& masm)
|
|
+ : AutoRegisterScope(masm, ScratchRegister) {}
|
|
+};
|
|
+struct SecondScratchRegisterScope : public AutoRegisterScope {
|
|
+ explicit SecondScratchRegisterScope(MacroAssembler& masm)
|
|
+ : AutoRegisterScope(masm, SecondScratchReg) {}
|
|
+};
|
|
+
|
|
+// Future.
|
|
+static constexpr bool SupportsSimd = false;
|
|
+static constexpr uint32_t SimdStackAlignment = 16;
|
|
+static constexpr uint32_t SimdMemoryAlignment = 16;
|
|
+static constexpr uint32_t AsmJSStackAlignment = 16;
|
|
+
|
|
+static constexpr uint32_t WasmStackAlignment = SimdStackAlignment;
|
|
+
|
|
+static const uint32_t WasmTrapInstructionLength = 4;
|
|
+
|
|
+static const Scale ScalePointer = TimesEight;
|
|
+
|
|
+enum PPCOpcodes {
|
|
+ // Some we don't use yet (but we will).
|
|
+ PPC_add = 0x7C000214, // add
|
|
+ PPC_addc = 0x7C000014, // add carrying
|
|
+ PPC_adde = 0x7C000114, // add extended
|
|
+ PPC_addi = 0x38000000, // add immediate
|
|
+ PPC_addic = 0x30000000, // add immediate carrying
|
|
+ PPC_addis = 0x3C000000, // add immediate shifted
|
|
+ PPC_addme = 0x7C0001D4, // add -1 extended
|
|
+ PPC_addo = 0x7C000614, // add & OE=1 (can set OV)
|
|
+ PPC_addpcis = 0x4C000004, // load next instruction address into register
|
|
+ PPC_addze = 0x7C000194, // add zero extended
|
|
+ PPC_and = 0x7C000038, // and
|
|
+ PPC_andc = 0x7C000078, // and with compliment
|
|
+ PPC_andi = 0x70000000, // and immediate
|
|
+ PPC_andis = 0x74000000, // and immediate shifted
|
|
+ PPC_b = 0x48000000, // branch
|
|
+ PPC_bc = 0x40000000, // branch conditional
|
|
+ PPC_bctr = 0x4E800420, // branch to CTR (+/- LR)
|
|
+ PPC_bcctr = 0x4C000420, // branch conditional to count register
|
|
+ PPC_blr = 0x4E800020, // branch to link register
|
|
+ PPC_cmpd = 0x7C200000, // compare
|
|
+ PPC_cmpdi = 0x2C200000, // compare immediate
|
|
+ PPC_cmpld = 0x7C200040, // compare logical
|
|
+ PPC_cmpldi = 0x28200000, // compare logical immediate
|
|
+ PPC_cmpw = 0x7C000000, // compare
|
|
+ PPC_cmpwi = 0x2C000000, // compare immediate
|
|
+ PPC_cmplw = 0x7C000040, // compare logical
|
|
+ PPC_cmplwi = 0x28000000, // compare logical immediate
|
|
+ PPC_cntlzd = 0x7C000074, // count leading zeroes
|
|
+ PPC_cntlzw = 0x7C000034, // count leading zeroes
|
|
+ PPC_cnttzd = 0x7C000474, // count leading zeroes
|
|
+ PPC_cnttzw = 0x7C000434, // count leading zeroes
|
|
+ PPC_crand = 0x4C000202, // condition register and
|
|
+ PPC_crandc = 0x4C000102, // condition register and-with-complement
|
|
+ PPC_cror = 0x4C000382, // condition register or
|
|
+ PPC_crorc = 0x4C000342, // condition register or-with-complement
|
|
+ PPC_crxor = 0x4C000182, // condition register xor
|
|
+ PPC_divd = 0x7C0003D2, // integer divide
|
|
+ PPC_divdo = 0x7C0007D2, // integer divide & OE=1 (can set OV)
|
|
+ PPC_divdu = 0x7C000392, // integer divide unsigned
|
|
+ PPC_divduo = 0x7C000792, // integer divide unsigned & OE=1 (can set OV)
|
|
+ PPC_divw = 0x7C0003D6, // integer divide
|
|
+ PPC_divwo = 0x7C0007D6, // integer divide & OE=1 (can set OV)
|
|
+ PPC_divwu = 0x7C000396, // integer divide unsigned
|
|
+ PPC_divwuo = 0x7C000796, // integer divide unsigned & OE=1 (can set OV)
|
|
+ PPC_eieio = 0x7C0006AC, // enforce in-order execution of I/O
|
|
+ PPC_eqv = 0x7C000238, // equivalence operator
|
|
+ PPC_extsb = 0x7C000774, // extend sign byte
|
|
+ PPC_extsh = 0x7C000734, // extend sign halfword
|
|
+ PPC_extsw = 0x7C0007B4, // extend sign word
|
|
+ PPC_fabs = 0xFC000210, // floating absolute value (double precision)
|
|
+ PPC_fadd = 0xFC00002A, // floating add (double precision)
|
|
+ PPC_fadds = 0xEC00002A, // floating add (single precision)
|
|
+ PPC_fcpsgn = 0xFC000010, // floating copy sign
|
|
+ PPC_fcfid = 0xFC00069C, // floating convert from integer doubleword
|
|
+ PPC_fcfids = 0xEC00069C, // floating convert from integer doubleword SP
|
|
+ PPC_fcfidu = 0xFC00079C, // floating convert from integer doubleword US
|
|
+ PPC_fcfidus = 0xEC00079C, // floating convert from integer DW (SP+US)
|
|
+ PPC_fcmpo = 0xFC000040, // floating compare unordered
|
|
+ PPC_fcmpu = 0xFC000000, // floating compare unordered
|
|
+ PPC_fctid = 0xFC00065C, // floating convert to integer (to -Inf)
|
|
+ PPC_fctidu = 0xFC00075C, // floating convert to integer doubleword unsigned
|
|
+ PPC_fctidz = 0xFC00065E, // floating convert to integer DW signed (to zero)
|
|
+ PPC_fctiduz = 0xFC00075E, // floating convert to integer USDW (to zero)
|
|
+ PPC_fctiw = 0xFC00001C, // floating convert to integer (to -Inf)
|
|
+ PPC_fctiwu = 0xFC00011C, // floating convert to integer (to -Inf)
|
|
+ PPC_fctiwuz = 0xFC00011E, // floating convert to integer (to zero)
|
|
+ PPC_fctiwz = 0xFC00001E, // floating convert to integer (to zero)
|
|
+ PPC_fdiv = 0xFC000024, // floating divide (double precision)
|
|
+ PPC_fdivs = 0xEC000024, // floating divide (single precision)
|
|
+ PPC_fmr = 0xFC000090, // floating move register
|
|
+ PPC_fmrgew = 0xFC00078C, // floating merge even word
|
|
+ PPC_fmul = 0xFC000032, // floating multiply (double precision)
|
|
+ PPC_fmuls = 0xEC000032, // floating multiply (single precision)
|
|
+ PPC_fneg = 0xFC000050, // floating negate
|
|
+ PPC_frim = 0xFC0003d0, // floating round to integer minus
|
|
+ PPC_frin = 0xFC000310, // floating round to integer nearest
|
|
+ PPC_frip = 0xFC000390, // floating round to integer plus
|
|
+ PPC_friz = 0xFC000350, // floating round to integer toward zero
|
|
+ PPC_frsp = 0xFC000018, // convert to single precision
|
|
+ PPC_fsel = 0xFC00002E, // floating point select
|
|
+ PPC_fsub = 0xFC000028, // floating subtract (double precision)
|
|
+ PPC_fsubs = 0xEC000028, // floating subtract (single precision)
|
|
+ PPC_fsqrt = 0xFC00002C, // floating square root (double)
|
|
+ PPC_fsqrts = 0xEC00002C, // floating square root (double)
|
|
+ PPC_frsqrte = 0xFC000034, // floating reciprocal square root estimate
|
|
+ PPC_fnmsub = 0xFC00003C, // floating fused negative multiply-subtract
|
|
+ PPC_fmadd = 0xFC00003A, // floating fused multiply-add
|
|
+ PPC_isel = 0x7C00001E, // integer select
|
|
+ PPC_isync = 0x4C00012C, // instruction synchronize
|
|
+ PPC_lbz = 0x88000000, // load byte and zero
|
|
+ PPC_lbzx = 0x7C0000AE, // load byte and zero indexed
|
|
+ PPC_ld = 0xE8000000, // load doubleword
|
|
+ PPC_ldarx = 0x7C0000A8, // load doubleword indexed
|
|
+ PPC_ldx = 0x7C00002A, // load doubleword indexed
|
|
+ PPC_lfd = 0xC8000000, // load floating point double
|
|
+ PPC_lfdx = 0x7C0004AE, // load floating-point double indexed
|
|
+ PPC_lfiwax = 0x7C0006AE, // load floating-point as integer word algebraic indexed
|
|
+ PPC_lfiwzx = 0x7C0006EE, // load floating-point as integer word algebraic indexed
|
|
+ PPC_lfs = 0xC0000000, // load single precision float
|
|
+ PPC_lfsx = 0x7C00042E, // load single precision float indexed
|
|
+ PPC_lha = 0xA8000000, // load halfword algebraic
|
|
+ PPC_lhax = 0x7C0002AE, // load halfword algebraic indexed
|
|
+ PPC_lhz = 0xA0000000, // load halfword and zero
|
|
+ PPC_lhzx = 0x7C00022E, // load halfword and zero indexed
|
|
+ PPC_lhbrx = 0x7C00062C, // load hw and zero indexed (byte swapped)
|
|
+ PPC_lwa = 0xE8000002, // load word algebraic
|
|
+ PPC_lwax = 0x7C0002AA, // load word algebraic indexed
|
|
+ PPC_lwarx = 0x7c000028, // load word and reserve indexed
|
|
+ PPC_lwz = 0x80000000, // load word and zero
|
|
+ PPC_lwzx = 0x7C00002E, // load word and zero indexed
|
|
+ PPC_lwbrx = 0x7C00042C, // load word and zero indexed (byte swapped)
|
|
+ PPC_mcrxrx = 0x7C000480, // move XER[OV, OV32, CA, CA32] to CR[0-3]
|
|
+ PPC_mcrf = 0x4C000000, // move CR[0-3] to CR[0-3]
|
|
+ PPC_mcrfs = 0xFC000080, // move FPSCR fields to CR
|
|
+ PPC_mfcr = 0x7C000026, // move from condition register
|
|
+ PPC_mfocrf = 0x7C100120, // move from one condition register field
|
|
+ PPC_mffs = 0xFC00048E, // move from fpscr to fpr
|
|
+ PPC_mfspr = 0x7C0002A6, // move from spr (special purpose register)
|
|
+ PPC_mfvsrd = 0x7C000066, // move from VSR doubleword (used for FPR)
|
|
+ PPC_modsd = 0x7C000612, // integer remainder 64-bit (signed)
|
|
+ PPC_modud = 0x7c000212, // integer remainder 64-bit (unsigned)
|
|
+ PPC_modsw = 0x7C000616, // integer remainder (signed)
|
|
+ PPC_moduw = 0x7c000216, // integer remainder (unsigned)
|
|
+ PPC_mtcrf = 0x7C000120, // move to condition register field
|
|
+ PPC_mtfsb0 = 0xFC00008C, // move zero bit into FPSCR
|
|
+ PPC_mtfsb1 = 0xFC00004C, // move one bit into FPSCR
|
|
+ PPC_mtfsfi = 0xFC00010C, // move 4-bit immediate into FPSCR field
|
|
+ PPC_mtvsrd = 0x7C000166, // move to VSR doubleword (used for FPR)
|
|
+ PPC_mtvsrws = 0x7C000326, // move to VSR word and splat (used for FPR)
|
|
+ PPC_mtvsrwz = 0x7C0001E6, // move to VSR word and zero (used for FPR)
|
|
+ PPC_mtspr = 0x7C0003A6, // move to spr
|
|
+ PPC_mulhd = 0x7C000092, // multiply high signed doubleword
|
|
+ PPC_mulhdu = 0x7C000012, // multiply high signed doubleword
|
|
+ PPC_mulhw = 0x7C000096, // multiply high signed
|
|
+ PPC_mulhwu = 0x7C000016, // multiply high unsigned
|
|
+ PPC_mulli = 0x1C000000, // multiply low immediate
|
|
+ PPC_mulld = 0x7C0001D2, // multiply low doubleword
|
|
+ PPC_mulldo = 0x7C0005D2, // multiply low doubleword
|
|
+ PPC_mullw = 0x7C0001D6, // multiply low word
|
|
+ PPC_mullwo = 0x7C0005D6, // multiply low word with overflow
|
|
+ PPC_nand = 0x7C0003B8, // nand
|
|
+ PPC_neg = 0x7C0000D0, // negate
|
|
+ PPC_nego = 0x7C0004D0, // negate & OE=1 (can set OV)
|
|
+ PPC_nor = 0x7C0000F8, // nor
|
|
+ PPC_or = 0x7C000378, // or
|
|
+ PPC_ori = 0x60000000, // or immediate
|
|
+ PPC_oris = 0x64000000, // or immediate shifted
|
|
+ PPC_popcntb = 0x7C0000F4, // population count doubleword
|
|
+ PPC_popcntd = 0x7C0003F4, // population count doubleword
|
|
+ PPC_popcntw = 0x7C0002F4, // population count doubleword
|
|
+ PPC_rldcl = 0x78000010, // rotate left doubleword then clear left
|
|
+ PPC_rldicl = 0x78000000, // rotate left doubleword immediate then clear left
|
|
+ PPC_rldcr = 0x78000012, // rotate left doubleword then clear right
|
|
+ PPC_rldicr = 0x78000004, // rotate left doubleword immediate then clear right
|
|
+ PPC_rldimi = 0x7800000C, // rotate left doubleword immediate then mask insert
|
|
+ PPC_rlwimi = 0x50000000, // rotate left word imm then mask insert
|
|
+ PPC_rlwinm = 0x54000000, // rotate left word imm then and with mask
|
|
+ PPC_rlwnm = 0x5C000000, // rotate left word then AND with mask
|
|
+ PPC_sld = 0x7C000036, // shift left doubleword
|
|
+ PPC_slw = 0x7C000030, // shift left word
|
|
+ PPC_srad = 0x7C000634, // shift right algebraic doubleword (sign ext)
|
|
+ PPC_sradi = 0x7C000674, // shift right algebraic doubleword immediate
|
|
+ PPC_sraw = 0x7C000630, // shift right algebraic word (sign ext)
|
|
+ PPC_srawi = 0x7C000670, // shift right algebraic word immediate
|
|
+ PPC_srd = 0x7C000436, // shift right doubleword (zero ext)
|
|
+ PPC_srw = 0x7C000430, // shift right word (zero ext)
|
|
+ PPC_stb = 0x98000000, // store byte
|
|
+ PPC_stbx = 0x7C0001AE, // store byte indexed
|
|
+ PPC_std = 0xF8000000, // store doubleword
|
|
+ PPC_stdcx = 0x7C0001AD, // store doubleword conditional indexed
|
|
+ PPC_stdu = 0xF8000001, // store doubleword with update
|
|
+ PPC_stdux = 0x7C00016A, // store doubleword with update indexed
|
|
+ PPC_stdx = 0x7C00012A, // store doubleword indexed
|
|
+ PPC_stfd = 0xD8000000, // store floating-point double
|
|
+ PPC_stfdu = 0xDC000000, // store floating-point double with update
|
|
+ PPC_stfdx = 0x7C0005AE, // store floating-point double indexed
|
|
+ PPC_stfiwx = 0x7C0007AE, // Store floating-point as integer word indexed
|
|
+ PPC_stfs = 0xD0000000, // store floating-point single
|
|
+ PPC_stfsu = 0xD4000000, // store floating-point single
|
|
+ PPC_stfsx = 0x7C00052E, // store floating-point single indexed
|
|
+ PPC_sth = 0xB0000000, // store halfword
|
|
+ PPC_sthx = 0x7C00032E, // store halfword indexed
|
|
+ PPC_sthbrx = 0x7C00072C, // store halfword indexed (byte swapped)
|
|
+ PPC_stop = 0x4C0002E4, // wasm-specific trap word (see note)
|
|
+ PPC_stw = 0x90000000, // store word
|
|
+ PPC_stwu = 0x94000000, // store word with update
|
|
+ PPC_stwux = 0x7C00016E, // store word with update indexed
|
|
+ PPC_stwx = 0x7C00012E, // store word indexed
|
|
+ PPC_stwbrx = 0x7C00052C, // store word indexed (byte swapped)
|
|
+ PPC_stwcx = 0x7C00012D, // store word indexed
|
|
+ PPC_subf = 0x7C000050, // subtract from
|
|
+ PPC_subfc = 0x7C000010, // subtract from with carry
|
|
+ PPC_subfe = 0x7C000110, // subtract from extended
|
|
+ PPC_subfic = 0x20000000, // subtract from immediate
|
|
+ PPC_subfze = 0x7C000190, // subtract from zero extended
|
|
+ PPC_subfo = 0x7C000450, // subtract from with overflow
|
|
+ PPC_sync = 0x7C0004AC, // sync
|
|
+#if defined(__APPLE__) || defined(__linux__)
|
|
+ PPC_trap = 0x7FE00008, // trap word (extended from tw 31,r0,r0)
|
|
+#elif defined(__FreeBSD__)
|
|
+ PPC_trap = 0x7C810808, // trap word (tweq r1, r1)
|
|
+#else
|
|
+#error Specify the trap word for your PPC operating system
|
|
+#endif
|
|
+ PPC_tw = 0x7C000008, // trap word immediate
|
|
+ PPC_twi = 0x0C000000, // trap word immediate
|
|
+ PPC_xor = 0x7C000278, // xor
|
|
+ PPC_xori = 0x68000000, // xor immediate
|
|
+ PPC_xoris = 0x6C000000, // xor immediate shifted
|
|
+ PPC_xscvdpsp= 0xF0000424, // VSX scalar convert double to single (for FPR)
|
|
+ PPC_xscvdpspn=0xF000042C, // VSX scalar convert double to single sNaN-pres
|
|
+ PPC_xscvspdp= 0xF0000524, // VSX scalar convert single to double (for FPR)
|
|
+ PPC_xscvspdpn=0xF000052C, // VSX scalar convert single to double sNaN-pres
|
|
+ PPC_xxbrd = 0xF017076C, // VSX byte-reverse doubleword
|
|
+ PPC_xxlxor = 0xF00004D0, // VSX logical XOR (I love this mnemonic)
|
|
+
|
|
+ // simplified mnemonics
|
|
+ PPC_mr = PPC_or,
|
|
+ PPC_not = PPC_nor,
|
|
+ PPC_nop = PPC_ori,
|
|
+ PPC_lwsync = PPC_sync | (1 << 21),
|
|
+
|
|
+ PPC_MAJOR_OPCODE_MASK = 0xFC000000 // AND with this to get some idea of the opcode
|
|
+};
|
|
+
|
|
+class Instruction;
|
|
+class InstImm;
|
|
+class MacroAssemblerPPC;
|
|
+class Operand;
|
|
+
|
|
+// A BOffImm16 is a 16 bit (signed or unsigned) immediate that is used for branches.
|
|
+class BOffImm16
|
|
+{
|
|
+ int32_t data;
|
|
+
|
|
+ public:
|
|
+ uint32_t encode() {
|
|
+ MOZ_ASSERT(!isInvalid());
|
|
+ return (uint32_t)data & 0xFFFC;
|
|
+ }
|
|
+ int32_t decode() {
|
|
+ MOZ_ASSERT(!isInvalid());
|
|
+ return data;
|
|
+ }
|
|
+
|
|
+ explicit BOffImm16(int offset)
|
|
+ {
|
|
+ MOZ_ASSERT((offset & 0x3) == 0);
|
|
+ MOZ_ASSERT(IsInRange(offset) || IsInSignedRange(offset));
|
|
+ data = offset;
|
|
+ }
|
|
+ static bool IsInRange(int offset) {
|
|
+ if (offset > 65535)
|
|
+ return false;
|
|
+ if (offset < 0)
|
|
+ return false;
|
|
+ return true;
|
|
+ }
|
|
+ static bool IsInSignedRange(int offset) {
|
|
+ if (offset > 32767)
|
|
+ return false;
|
|
+ if (offset < -32767)
|
|
+ return false;
|
|
+ return true;
|
|
+ }
|
|
+ static const int32_t INVALID = 0x00020000;
|
|
+ BOffImm16()
|
|
+ : data(INVALID)
|
|
+ { }
|
|
+
|
|
+ bool isInvalid() {
|
|
+ return data == INVALID;
|
|
+ }
|
|
+ Instruction *getDest(Instruction *src);
|
|
+
|
|
+ BOffImm16(InstImm inst);
|
|
+};
|
|
+
|
|
+// A JOffImm26 is a 26 bit signed immediate that is used for unconditional jumps.
|
|
+class JOffImm26
|
|
+{
|
|
+ int32_t data;
|
|
+
|
|
+ public:
|
|
+ uint32_t encode() {
|
|
+ MOZ_ASSERT(!isInvalid());
|
|
+ return (uint32_t)data & 0x03FFFFFC;
|
|
+ }
|
|
+ int32_t decode() {
|
|
+ MOZ_ASSERT(!isInvalid());
|
|
+ return data;
|
|
+ }
|
|
+
|
|
+ explicit JOffImm26(int offset)
|
|
+ {
|
|
+ MOZ_ASSERT((offset & 0x3) == 0);
|
|
+ MOZ_ASSERT(IsInRange(offset));
|
|
+ data = offset;
|
|
+ }
|
|
+ static bool IsInRange(int offset) {
|
|
+ if (offset < -33554431)
|
|
+ return false;
|
|
+ if (offset > 33554431)
|
|
+ return false;
|
|
+ return true;
|
|
+ }
|
|
+ static const int32_t INVALID = 0x20000000;
|
|
+ JOffImm26()
|
|
+ : data(INVALID)
|
|
+ { }
|
|
+
|
|
+ bool isInvalid() {
|
|
+ return data == INVALID;
|
|
+ }
|
|
+ Instruction *getDest(Instruction *src);
|
|
+
|
|
+};
|
|
+
|
|
+class Imm16
|
|
+{
|
|
+ int32_t value;
|
|
+
|
|
+ public:
|
|
+ Imm16();
|
|
+ Imm16(uint32_t imm)
|
|
+ : value(imm)
|
|
+ {
|
|
+ }
|
|
+ uint32_t encode() {
|
|
+ return (uint32_t)value & 0xffff;
|
|
+ }
|
|
+ int32_t decodeSigned() {
|
|
+ return value;
|
|
+ }
|
|
+ uint32_t decodeUnsigned() {
|
|
+ return value;
|
|
+ }
|
|
+ static bool IsInSignedRange(int32_t imm) {
|
|
+ return imm >= INT16_MIN && imm <= INT16_MAX;
|
|
+ }
|
|
+ static bool IsInUnsignedRange(uint32_t imm) {
|
|
+ return imm <= UINT16_MAX ;
|
|
+ }
|
|
+ static Imm16 Lower (Imm32 imm) {
|
|
+ return Imm16(imm.value & 0xffff);
|
|
+ }
|
|
+ static Imm16 Upper (Imm32 imm) {
|
|
+ return Imm16((imm.value >> 16) & 0xffff);
|
|
+ }
|
|
+};
|
|
+
|
|
+class Imm8
|
|
+{
|
|
+ uint8_t value;
|
|
+
|
|
+ public:
|
|
+ Imm8();
|
|
+ Imm8(uint32_t imm) : value(imm) {}
|
|
+ uint32_t encode(uint32_t shift) { return value << shift; }
|
|
+ int32_t decodeSigned() { return value; }
|
|
+ uint32_t decodeUnsigned() { return value; }
|
|
+ static bool IsInSignedRange(int32_t imm) {
|
|
+ return imm >= INT8_MIN && imm <= INT8_MAX;
|
|
+ }
|
|
+ static bool IsInUnsignedRange(uint32_t imm) { return imm <= UINT8_MAX; }
|
|
+ static Imm8 Lower(Imm16 imm) { return Imm8(imm.decodeSigned() & 0xff); }
|
|
+ static Imm8 Upper(Imm16 imm) {
|
|
+ return Imm8((imm.decodeSigned() >> 8) & 0xff);
|
|
+ }
|
|
+};
|
|
+
|
|
+class Operand
|
|
+{
|
|
+ public:
|
|
+ enum Tag {
|
|
+ REG,
|
|
+ FREG,
|
|
+ MEM
|
|
+ };
|
|
+
|
|
+ private:
|
|
+ Tag tag : 3;
|
|
+ uint32_t reg : 5; // XXX. This really should be Register::Code, but then float regs ...
|
|
+ int32_t offset;
|
|
+
|
|
+ public:
|
|
+ Operand (Register reg_)
|
|
+ : tag(REG), reg(reg_.code())
|
|
+ { }
|
|
+
|
|
+ Operand (FloatRegister freg)
|
|
+ : tag(FREG), reg(freg.code())
|
|
+ { }
|
|
+
|
|
+ Operand (Register base, Imm32 off)
|
|
+ : tag(MEM), reg(base.code()), offset(off.value)
|
|
+ { }
|
|
+
|
|
+ Operand (Register base, int32_t off)
|
|
+ : tag(MEM), reg(base.code()), offset(off)
|
|
+ { }
|
|
+
|
|
+ Operand (const Address &addr)
|
|
+ : tag(MEM), reg(addr.base.code()), offset(addr.offset)
|
|
+ { }
|
|
+
|
|
+ Tag getTag() const {
|
|
+ return tag;
|
|
+ }
|
|
+
|
|
+ Register toReg() const {
|
|
+ MOZ_ASSERT(tag == REG);
|
|
+ return Register::FromCode((Register::Code)reg);
|
|
+ }
|
|
+
|
|
+ FloatRegister toFReg() const {
|
|
+ MOZ_ASSERT(tag == FREG);
|
|
+ return FloatRegister::FromCode((FloatRegister::Code)reg);
|
|
+ }
|
|
+
|
|
+ void toAddr(Register *r, Imm32 *dest) const {
|
|
+ MOZ_ASSERT(tag == MEM);
|
|
+ *r = Register::FromCode((Register::Code)reg);
|
|
+ *dest = Imm32(offset);
|
|
+ }
|
|
+ Address toAddress() const {
|
|
+ MOZ_ASSERT(tag == MEM);
|
|
+ return Address(Register::FromCode((Register::Code)reg), offset);
|
|
+ }
|
|
+ int32_t disp() const {
|
|
+ MOZ_ASSERT(tag == MEM);
|
|
+ return offset;
|
|
+ }
|
|
+
|
|
+ int32_t base() const {
|
|
+ MOZ_ASSERT(tag == MEM);
|
|
+ return reg;
|
|
+ }
|
|
+ Register baseReg() const {
|
|
+ MOZ_ASSERT(tag == MEM);
|
|
+ return Register::FromCode((Register::Code)reg);
|
|
+ }
|
|
+};
|
|
+
|
|
+inline Imm32
|
|
+Imm64::firstHalf() const
|
|
+{
|
|
+ return hi(); // ENDIAN!
|
|
+}
|
|
+
|
|
+inline Imm32
|
|
+Imm64::secondHalf() const
|
|
+{
|
|
+ return low(); // ENDIAN!
|
|
+}
|
|
+
|
|
+class Assembler;
|
|
+typedef js::jit::AssemblerBuffer<1024, Instruction> PPCBuffer;
|
|
+
|
|
+class PPCBufferWithExecutableCopy : public PPCBuffer
|
|
+{
|
|
+ static const int SliceSize = 1024;
|
|
+ public:
|
|
+ void executableCopy(uint8_t* buffer) {
|
|
+ if (this->oom())
|
|
+ return;
|
|
+
|
|
+ for (Slice* cur = head; cur != nullptr; cur = cur->getNext()) {
|
|
+ memcpy(buffer, &cur->instructions, cur->length());
|
|
+ buffer += cur->length();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ bool appendRawCode(const uint8_t* code, size_t numBytes) {
|
|
+ if (this->oom()) {
|
|
+ return false;
|
|
+ }
|
|
+ while (numBytes > SliceSize) {
|
|
+ this->putBytes(SliceSize, code);
|
|
+ numBytes -= SliceSize;
|
|
+ code += SliceSize;
|
|
+ }
|
|
+ this->putBytes(numBytes, code);
|
|
+ return !this->oom();
|
|
+ }
|
|
+};
|
|
+
|
|
+class Assembler : public AssemblerShared
|
|
+{
|
|
+ public:
|
|
+ enum TrapTag { // FreeBSD and others may use r1 in their trap word, so don't allow bit 0 or > 15.
|
|
+ BTag = 2,
|
|
+ BCTag = 4,
|
|
+ CallTag = 6,
|
|
+ DebugTag0 = 10,
|
|
+ DebugTag1 = 12,
|
|
+ DebugTag2 = 14
|
|
+ };
|
|
+
|
|
+ enum BranchBits {
|
|
+ BranchOnClear = 0x04,
|
|
+ BranchOnSet = 0x0c,
|
|
+ BranchOptionMask = 0x0f,
|
|
+ BranchOptionInvert = 0x08 // XOR with this to invert the sense of a Condition
|
|
+ };
|
|
+
|
|
+ enum Condition {
|
|
+ // Bit flag for unsigned comparisons (remember that you have to
|
|
+ // choose the type of comparison at the compare step, not the
|
|
+ // branch). We just mask this bit off, but the MacroAsssembler
|
|
+ // may use it as a flag. This is a synthetic code.
|
|
+ ConditionUnsigned = 0x100, // Computation only
|
|
+ ConditionUnsignedHandled = 0x2ff, // Mask off bit 8 but not 9 or 0-7
|
|
+
|
|
+ // Bit flag for zero-relative Conditions. These are treated as
|
|
+ // equivalent conditions relative to 0, but the MacroAssembler and
|
|
+ // CodeGenerator may use this bit to reason about the intent of
|
|
+ // generated instructions. This is a synthetic code.
|
|
+ ConditionZero = 0x400, // Computation only
|
|
+
|
|
+ // Bit flag for XER-only codes. We need to have XER in the CR using
|
|
+ // mcrxrx or an equivalent first, but we don't need to check any CR
|
|
+ // bits otherwise. This is a synthetic code. We use the 32-bit flags.
|
|
+ ConditionOnlyXER = 0x200, // Computation only
|
|
+ ConditionXERCA = 0x23c, // CA32 same as SO bit
|
|
+ ConditionXERNCA = 0x234,
|
|
+ ConditionXEROV = 0x21c, // OV32 same as GT bit
|
|
+
|
|
+ // These are off pp370-1 in OPPCC. The top nybble is the offset
|
|
+ // to the CR field (the x in BIF*4+x), and the bottom is the BO.
|
|
+ // Synthetic condition flags sit in the MSB.
|
|
+ Equal = 0x2c,
|
|
+ NotEqual = 0x24,
|
|
+ GreaterThan = 0x1c,
|
|
+ GreaterThanOrEqual = 0x04,
|
|
+ LessThan = 0x0c,
|
|
+ LessThanOrEqual = 0x14,
|
|
+
|
|
+ Above = GreaterThan | ConditionUnsigned,
|
|
+ AboveOrEqual = GreaterThanOrEqual | ConditionUnsigned,
|
|
+ Below = LessThan | ConditionUnsigned,
|
|
+ BelowOrEqual = LessThanOrEqual | ConditionUnsigned,
|
|
+
|
|
+ Signed = LessThan | ConditionZero,
|
|
+ // Don't mention negative zero to me. Don't wanna hear it. Nope.
|
|
+ NotSigned = GreaterThanOrEqual | ConditionZero,
|
|
+ Zero = Equal | ConditionZero,
|
|
+ NonZero = NotEqual | ConditionZero,
|
|
+
|
|
+ // Hard hints that we need an unsigned compare.
|
|
+ BitEqual = Equal | ConditionUnsigned,
|
|
+ BitNotEqual = NotEqual | ConditionUnsigned,
|
|
+
|
|
+ Overflow = ConditionXEROV,
|
|
+ CarrySet = ConditionXERCA,
|
|
+ CarryClear = ConditionXERNCA,
|
|
+
|
|
+ Always = 0x1f,
|
|
+
|
|
+ // This is specific to the SO bits in the CR, not the general overflow
|
|
+ // condition in the way Ion conceives of it.
|
|
+ SOBit = 0x3c,
|
|
+ NSOBit = 0x34
|
|
+ };
|
|
+
|
|
+ enum DoubleCondition {
|
|
+ DoubleConditionUnordered = 0x100, // Computation only. This is also synthetic.
|
|
+
|
|
+ // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN.
|
|
+ DoubleOrdered = 0x34,
|
|
+ DoubleEqual = 0x2c,
|
|
+ DoubleNotEqual = 0x24,
|
|
+ DoubleGreaterThan = 0x1c,
|
|
+ DoubleGreaterThanOrEqual = 0x04,
|
|
+ DoubleLessThan = 0x0c,
|
|
+ DoubleLessThanOrEqual = 0x14,
|
|
+ // If either operand is NaN, these conditions always evaluate to true.
|
|
+ // Except for DoubleUnordered, synthetic condition flags sit in the MSB
|
|
+ // and are masked off by us but may be used by the MacroAssembler.
|
|
+ DoubleUnordered = 0x3c,
|
|
+ DoubleEqualOrUnordered = DoubleEqual | DoubleConditionUnordered,
|
|
+ DoubleNotEqualOrUnordered = DoubleNotEqual | DoubleConditionUnordered,
|
|
+ DoubleGreaterThanOrUnordered = DoubleGreaterThan | DoubleConditionUnordered,
|
|
+ DoubleGreaterThanOrEqualOrUnordered = DoubleGreaterThanOrEqual | DoubleConditionUnordered,
|
|
+ DoubleLessThanOrUnordered = DoubleLessThan | DoubleConditionUnordered,
|
|
+ DoubleLessThanOrEqualOrUnordered = DoubleLessThanOrEqual | DoubleConditionUnordered,
|
|
+ };
|
|
+
|
|
+ enum JumpOrCall { BranchIsJump, BranchIsCall };
|
|
+
|
|
+ enum LinkBit {
|
|
+ DontLinkB = 0,
|
|
+ LinkB = 1,
|
|
+ };
|
|
+
|
|
+ enum LikelyBit {
|
|
+ NotLikelyB = 0,
|
|
+ LikelyB = 1,
|
|
+ };
|
|
+
|
|
+ enum BranchAddressType {
|
|
+ RelativeBranch = 0,
|
|
+ AbsoluteBranch = 2,
|
|
+ };
|
|
+
|
|
+ BufferOffset nextOffset() {
|
|
+ return m_buffer.nextOffset();
|
|
+ }
|
|
+
|
|
+ enum FloatFormat { SingleFloat, DoubleFloat };
|
|
+ enum FloatTestKind { TestForTrue, TestForFalse };
|
|
+
|
|
+ protected:
|
|
+ Instruction * editSrc (BufferOffset bo) {
|
|
+ return m_buffer.getInst(bo);
|
|
+ }
|
|
+ public:
|
|
+ uint32_t actualOffset(uint32_t) const;
|
|
+ uint32_t actualIndex(uint32_t) const;
|
|
+ static uint8_t *PatchableJumpAddress(JitCode *code, uint32_t index);
|
|
+ static uint64_t ExtractLoad64Value(Instruction *inst);
|
|
+ static void UpdateLoad64Value(Instruction *inst0, uint64_t value);
|
|
+ static void WriteLoad64Instructions(Instruction* inst0, Register reg,
|
|
+ uint64_t value);
|
|
+ protected:
|
|
+
|
|
+ // structure for fixing up pc-relative loads/jumps when a the machine code
|
|
+ // gets moved (executable copy, gc, etc.)
|
|
+ struct RelativePatch
|
|
+ {
|
|
+ // the offset within the code buffer where the value is loaded that
|
|
+ // we want to fix-up
|
|
+ BufferOffset offset;
|
|
+ void *target;
|
|
+ RelocationKind kind;
|
|
+
|
|
+ RelativePatch(BufferOffset offset, void *target, RelocationKind kind)
|
|
+ : offset(offset),
|
|
+ target(target),
|
|
+ kind(kind)
|
|
+ { }
|
|
+ };
|
|
+
|
|
+ //js::Vector<CodeLabel, 0, SystemAllocPolicy> codeLabels_;
|
|
+ js::Vector<RelativePatch, 8, SystemAllocPolicy> jumps_;
|
|
+ //js::Vector<uint32_t, 8, SystemAllocPolicy> longJumps_;
|
|
+
|
|
+ CompactBufferWriter jumpRelocations_;
|
|
+ CompactBufferWriter dataRelocations_;
|
|
+ CompactBufferWriter relocations_;
|
|
+ CompactBufferWriter preBarriers_;
|
|
+
|
|
+ PPCBufferWithExecutableCopy m_buffer;
|
|
+
|
|
+ private:
|
|
+ char const * nGPR(Register rreg)
|
|
+ {
|
|
+ uint32_t reg = rreg.code();
|
|
+ MOZ_ASSERT(reg <= 31);
|
|
+ //MOZ_ASSERT(reg >= 0);
|
|
+ static char const *names[] = {
|
|
+ "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7",
|
|
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
|
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
|
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"
|
|
+ };
|
|
+ return names[reg];
|
|
+ }
|
|
+
|
|
+ char const * nFPR(FloatRegister freg)
|
|
+ {
|
|
+ uint32_t reg = freg.code();
|
|
+ MOZ_ASSERT(reg <= 31);
|
|
+ //MOZ_ASSERT(reg >= 0);
|
|
+ static char const *names[] = {
|
|
+ "f0", "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"
|
|
+ };
|
|
+ return names[reg];
|
|
+ }
|
|
+
|
|
+ char const * nCR(CRegisterID reg)
|
|
+ {
|
|
+ MOZ_ASSERT(reg <= 7);
|
|
+ MOZ_ASSERT(reg >= 0);
|
|
+ static char const *names[] = {
|
|
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7"
|
|
+ };
|
|
+ return names[reg];
|
|
+ }
|
|
+
|
|
+ char const * nSPR(SPRegisterID reg)
|
|
+ {
|
|
+ // XXX: we don't handle VRSAVE with this, but we don't use it yet.
|
|
+ MOZ_ASSERT(reg >= 1);
|
|
+ MOZ_ASSERT(reg <= 9);
|
|
+ static char const *names[] = {
|
|
+ "", "xer", "", "", "", "", "", "", "lr", "ctr"
|
|
+ };
|
|
+ return names[reg];
|
|
+ }
|
|
+
|
|
+ public: // used by MacroAssembler
|
|
+ // Which absolute bit number does a condition register + Condition pair
|
|
+ // refer to?
|
|
+ static uint8_t crBit(CRegisterID cr, Condition cond)
|
|
+ {
|
|
+ return (cr << 2) + ((cond & 0xf0) >> 4);
|
|
+ }
|
|
+
|
|
+ static uint8_t crBit(CRegisterID cr, DoubleCondition cond)
|
|
+ {
|
|
+ return (cr << 2) + ((cond & 0xf0) >> 4);
|
|
+ }
|
|
+
|
|
+ public:
|
|
+ Assembler()
|
|
+ : m_buffer(),
|
|
+ isFinished(false)
|
|
+ { }
|
|
+
|
|
+ void setUnlimitedBuffer() { m_buffer.setUnlimited(); }
|
|
+ static Condition InvertCondition(Condition cond);
|
|
+ static DoubleCondition InvertCondition(DoubleCondition cond);
|
|
+
|
|
+ // MacroAssemblers hold onto gcthings, so they are traced by the GC.
|
|
+ void trace(JSTracer *trc);
|
|
+ void writeRelocation(BufferOffset src) {
|
|
+ jumpRelocations_.writeUnsigned(src.getOffset());
|
|
+ }
|
|
+
|
|
+#if(1)
|
|
+ // As opposed to the x86/x64 version, the data relocation must be executed
|
|
+ // beforehand to recover the pointer, not after.
|
|
+ void writeDataRelocation(ImmGCPtr ptr) {
|
|
+ if (ptr.value) {
|
|
+ if (gc::IsInsideNursery(ptr.value))
|
|
+ embedsNurseryPointers_ = true;
|
|
+ dataRelocations_.writeUnsigned(nextOffset().getOffset());
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ void writePrebarrierOffset(CodeOffset label) {
|
|
+ preBarriers_.writeUnsigned(label.offset());
|
|
+ }
|
|
+
|
|
+ public:
|
|
+ static uintptr_t GetPointer(uint8_t *);
|
|
+
|
|
+ bool oom() const;
|
|
+
|
|
+ void setPrinter(Sprinter *sp) {
|
|
+ }
|
|
+
|
|
+ static const Register getStackPointer() {
|
|
+ // This is stupid.
|
|
+ return StackPointer;
|
|
+ }
|
|
+
|
|
+ private:
|
|
+ bool isFinished;
|
|
+ public:
|
|
+#if defined(DEBUG)
|
|
+ void spew_with_address(const char *fmt, uint32_t ins, ...);
|
|
+#endif
|
|
+ void finish();
|
|
+ bool appendRawCode(const uint8_t* code, size_t numBytes);
|
|
+ void executableCopy(void *buffer);
|
|
+ void copyJumpRelocationTable(uint8_t *dest);
|
|
+ void copyDataRelocationTable(uint8_t *dest);
|
|
+ void copyPreBarrierTable(uint8_t *dest);
|
|
+
|
|
+/*
|
|
+ size_t numCodeLabels() const {
|
|
+ return codeLabels_.length();
|
|
+ }
|
|
+ CodeLabel codeLabel(size_t i) {
|
|
+ return codeLabels_[i];
|
|
+ }
|
|
+*/
|
|
+
|
|
+ // Size of the instruction stream, in bytes.
|
|
+ size_t size() const;
|
|
+ // Size of the jump relocation table, in bytes.
|
|
+ size_t jumpRelocationTableBytes() const;
|
|
+ size_t dataRelocationTableBytes() const;
|
|
+ size_t preBarrierTableBytes() const;
|
|
+
|
|
+ // Size of the data table, in bytes.
|
|
+ size_t bytesNeeded() const;
|
|
+
|
|
+ // Write a blob of binary into the instruction stream *or*
|
|
+ // into a destination address. If dest is nullptr (the default), then the
|
|
+ // instruction gets written into the instruction stream. If dest is not null
|
|
+ // it is interpreted as a pointer to the location that we want the
|
|
+ // instruction to be written.
|
|
+ BufferOffset writeInst(uint32_t x, uint32_t *dest = nullptr);
|
|
+ // A static variant for the cases where we don't want to have an assembler
|
|
+ // object at all. Normally, you would use the dummy (nullptr) object.
|
|
+ static void WriteInstStatic(uint32_t x, uint32_t *dest);
|
|
+
|
|
+ public:
|
|
+ BufferOffset align(int alignment, bool useTrap = false);
|
|
+
|
|
+ BufferOffset as_nop();
|
|
+ BufferOffset as_eieio();
|
|
+ BufferOffset as_isync();
|
|
+ BufferOffset xs_lwsync();
|
|
+ BufferOffset as_sync();
|
|
+
|
|
+ // Branch and jump instructions.
|
|
+ uint16_t computeConditionCode(Condition op, CRegisterID cr = cr0);
|
|
+ uint16_t computeConditionCode(DoubleCondition cond, CRegisterID cr = cr0);
|
|
+ BufferOffset as_b(JOffImm26 off, BranchAddressType bat = RelativeBranch, LinkBit lb = DontLinkB);
|
|
+ BufferOffset as_b(int32_t off, BranchAddressType bat = RelativeBranch, LinkBit lb = DontLinkB); // stubs into the above
|
|
+ BufferOffset as_blr(LinkBit lb = DontLinkB);
|
|
+ BufferOffset as_bctr(LinkBit lb = DontLinkB);
|
|
+
|
|
+ // Conditional branches.
|
|
+ BufferOffset as_bc(BOffImm16 off, Condition cond, CRegisterID cr = cr0, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
|
+ BufferOffset as_bc(int16_t off, Condition cond, CRegisterID cr = cr0, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
|
+ BufferOffset as_bc(BOffImm16 off, DoubleCondition cond, CRegisterID = cr0, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
|
+ BufferOffset as_bc(int16_t off, DoubleCondition cond, CRegisterID = cr0, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
|
+ BufferOffset as_bcctr(Condition cond, CRegisterID cr = cr0, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
|
+ BufferOffset as_bcctr(DoubleCondition cond, CRegisterID cr = cr0, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
|
+
|
|
+ BufferOffset as_bc(int16_t off, uint16_t op, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
|
+ BufferOffset as_bcctr(uint16_t op, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
|
+
|
|
+
|
|
+ // SPR operations.
|
|
+ BufferOffset as_mtspr(SPRegisterID spr, Register ra);
|
|
+ BufferOffset as_mfspr(Register rd, SPRegisterID spr);
|
|
+
|
|
+ // CR operations.
|
|
+#define DEF_CRCR(op) BufferOffset as_##op(uint8_t t, uint8_t a, uint8_t b);
|
|
+
|
|
+ DEF_CRCR(crand)
|
|
+ DEF_CRCR(crandc)
|
|
+ DEF_CRCR(cror)
|
|
+ DEF_CRCR(crorc)
|
|
+ DEF_CRCR(crxor)
|
|
+#undef DEF_CRCR
|
|
+ BufferOffset as_mtcrf(uint32_t mask, Register rs);
|
|
+ BufferOffset as_mfcr(Register rd);
|
|
+ BufferOffset as_mfocrf(Register rd, CRegisterID crfs);
|
|
+ BufferOffset as_mcrxrx(CRegisterID crt);
|
|
+
|
|
+ // GPR operations and load-stores.
|
|
+ BufferOffset as_neg(Register rd, Register rs);
|
|
+ BufferOffset as_nego(Register rd, Register rs);
|
|
+
|
|
+ BufferOffset as_cmpd(CRegisterID cr, Register ra, Register rb);
|
|
+ BufferOffset as_cmpdi(CRegisterID cr, Register ra, int16_t im);
|
|
+ BufferOffset as_cmpld(CRegisterID cr, Register ra, Register rb);
|
|
+ BufferOffset as_cmpldi(CRegisterID cr, Register ra, int16_t im);
|
|
+ BufferOffset as_cmpw(CRegisterID cr, Register ra, Register rb);
|
|
+ BufferOffset as_cmpwi(CRegisterID cr, Register ra, int16_t im);
|
|
+ BufferOffset as_cmplw(CRegisterID cr, Register ra, Register rb);
|
|
+ BufferOffset as_cmplwi(CRegisterID cr, Register ra, int16_t im);
|
|
+ BufferOffset as_cmpd(Register ra, Register rb); // all implied cr0
|
|
+ BufferOffset as_cmpdi(Register ra, int16_t im);
|
|
+ BufferOffset as_cmpld(Register ra, Register rb);
|
|
+ BufferOffset as_cmpldi(Register ra, int16_t im);
|
|
+ BufferOffset as_cmpw(Register ra, Register rb);
|
|
+ BufferOffset as_cmpwi(Register ra, int16_t im);
|
|
+ BufferOffset as_cmplw(Register ra, Register rb);
|
|
+ BufferOffset as_cmplwi(Register ra, int16_t im);
|
|
+
|
|
+ BufferOffset as_srawi(Register id, Register rs, uint8_t n);
|
|
+
|
|
+ BufferOffset as_rldcl(Register ra, Register rs, Register rb, uint8_t mb);
|
|
+ BufferOffset as_rldcl_rc(Register ra, Register rs, Register rb, uint8_t mb);
|
|
+ BufferOffset as_rldicl(Register ra, Register rs, uint8_t sh, uint8_t mb);
|
|
+ BufferOffset as_rldicl_rc(Register ra, Register rs, uint8_t sh, uint8_t mb);
|
|
+ BufferOffset as_rldicr(Register ra, Register rs, uint8_t sh, uint8_t mb);
|
|
+ BufferOffset as_rldicr_rc(Register ra, Register rs, uint8_t sh, uint8_t mb);
|
|
+ BufferOffset as_rlwinm(Register rd, Register rs, uint8_t sh, uint8_t mb, uint8_t me);
|
|
+ BufferOffset as_rlwinm_rc(Register rd, Register rs, uint8_t sh, uint8_t mb, uint8_t me);
|
|
+ BufferOffset as_rlwimi(Register rd, Register rs, uint8_t sh, uint8_t mb, uint8_t me); // cracked on G5
|
|
+ BufferOffset as_rldimi(Register rd, Register rs, uint8_t sh, uint8_t mb);
|
|
+ BufferOffset as_rlwnm(Register rd, Register rs, Register rb, uint8_t mb, uint8_t me);
|
|
+ BufferOffset as_sradi(Register rd, Register rs, int n);
|
|
+
|
|
+#define DEF_ALU2(op) BufferOffset as_##op(Register rd, Register ra, Register rb); \
|
|
+ BufferOffset as_##op##_rc(Register rd, Register ra, Register rb);
|
|
+ DEF_ALU2(add)
|
|
+ DEF_ALU2(addc)
|
|
+ DEF_ALU2(adde)
|
|
+ DEF_ALU2(addo)
|
|
+ DEF_ALU2(subf)
|
|
+ DEF_ALU2(subfc)
|
|
+ DEF_ALU2(subfe)
|
|
+ DEF_ALU2(subfo)
|
|
+ DEF_ALU2(divd)
|
|
+ DEF_ALU2(divdo)
|
|
+ DEF_ALU2(divdu)
|
|
+ DEF_ALU2(divduo)
|
|
+ DEF_ALU2(divw)
|
|
+ DEF_ALU2(divwo)
|
|
+ DEF_ALU2(divwu)
|
|
+ DEF_ALU2(divwuo)
|
|
+ DEF_ALU2(mulld)
|
|
+ DEF_ALU2(mulhd)
|
|
+ DEF_ALU2(mulhdu)
|
|
+ DEF_ALU2(mulldo)
|
|
+ DEF_ALU2(mullw)
|
|
+ DEF_ALU2(mulhw)
|
|
+ DEF_ALU2(mulhwu)
|
|
+ DEF_ALU2(mullwo)
|
|
+ DEF_ALU2(eqv) // NB: Implemented differently.
|
|
+#undef DEF_ALU2
|
|
+
|
|
+#define DEF_ALU2_NORC(op) BufferOffset as_##op(Register rt, Register ra, Register rb);
|
|
+ DEF_ALU2_NORC(modsd)
|
|
+ DEF_ALU2_NORC(modud)
|
|
+ DEF_ALU2_NORC(modsw)
|
|
+ DEF_ALU2_NORC(moduw)
|
|
+#undef DEF_ALU2_NORC
|
|
+
|
|
+// Special handling due to mscdfr0 (no _rc)
|
|
+BufferOffset as_addi(Register rd, Register ra, int16_t im, bool actually_li = false);
|
|
+BufferOffset as_addis(Register rd, Register ra, int16_t im, bool actually_lis = false);
|
|
+
|
|
+#define DEF_ALUI(op) BufferOffset as_##op(Register rd, Register ra, int16_t im); \
|
|
+ BufferOffset as_##op##_rc(Register rd, Register ra, int16_t im);
|
|
+ DEF_ALUI(addic)
|
|
+ // NB: mulli is usually strength-reduced, since it can take up to five
|
|
+ // cycles in the worst case. See xs_sr_mulli.
|
|
+ DEF_ALUI(mulli)
|
|
+ DEF_ALUI(subfic)
|
|
+#undef DEF_ALUI
|
|
+
|
|
+#define DEF_ALUE(op) BufferOffset as_##op(Register rd, Register ra); \
|
|
+ BufferOffset as_##op##_rc(Register rd, Register ra);
|
|
+ DEF_ALUE(addme)
|
|
+ DEF_ALUE(addze)
|
|
+ DEF_ALUE(subfze)
|
|
+ DEF_ALUE(cntlzw) // NB: In this case, rd = ra and ra = rs, but no biggie here.
|
|
+ DEF_ALUE(cntlzd) // NB: In this case, rd = ra and ra = rs, but no biggie here.
|
|
+ DEF_ALUE(cnttzd) // NB: In this case, rd = ra and ra = rs, but no biggie here.
|
|
+ DEF_ALUE(cnttzw) // NB: In this case, rd = ra and ra = rs, but no biggie here.
|
|
+
|
|
+ BufferOffset as_popcntd(Register ra, Register rs);
|
|
+ BufferOffset as_popcntw(Register ra, Register rs);
|
|
+#undef DEF_ALUE
|
|
+
|
|
+#define DEF_BITALU2(op) BufferOffset as_##op(Register rd, Register rs, Register rb); \
|
|
+ BufferOffset as_##op##_rc(Register rd, Register rs, Register rb);
|
|
+ DEF_BITALU2(andc)
|
|
+ DEF_BITALU2(nand)
|
|
+ DEF_BITALU2(nor)
|
|
+ DEF_BITALU2(slw)
|
|
+ DEF_BITALU2(srw)
|
|
+ DEF_BITALU2(sraw)
|
|
+ DEF_BITALU2(sld)
|
|
+ DEF_BITALU2(srd)
|
|
+ DEF_BITALU2(srad)
|
|
+ DEF_BITALU2(and) // NB: See terminal _ constants above. This will have and_ and and__rc.
|
|
+ DEF_BITALU2(or)
|
|
+ DEF_BITALU2(xor)
|
|
+#undef DEF_BITALU2
|
|
+
|
|
+#define DEF_BITALUI(op) BufferOffset as_##op(Register rd, Register ra, uint16_t im);
|
|
+ // There is no Rc form for these instructions.
|
|
+ DEF_BITALUI(ori)
|
|
+ DEF_BITALUI(oris)
|
|
+ DEF_BITALUI(xori)
|
|
+ DEF_BITALUI(xoris)
|
|
+ // There is no Rc-less version of andi/andis.
|
|
+ DEF_BITALUI(andi_rc)
|
|
+ DEF_BITALUI(andis_rc)
|
|
+#undef DEF_BITALUI
|
|
+
|
|
+#define DEF_ALUEXT(op) BufferOffset as_##op(Register rd, Register rs); \
|
|
+ BufferOffset as_##op##_rc(Register rd, Register rs);
|
|
+ DEF_ALUEXT(extsb)
|
|
+ DEF_ALUEXT(extsh)
|
|
+ DEF_ALUEXT(extsw)
|
|
+#undef DEF_ALUEXT
|
|
+
|
|
+#define DEF_MEMd(op) BufferOffset as_##op(Register rd, Register rb, int16_t off);
|
|
+ DEF_MEMd(lbz)
|
|
+ DEF_MEMd(lha)
|
|
+ DEF_MEMd(lhz)
|
|
+ DEF_MEMd(lwa)
|
|
+ DEF_MEMd(lwz)
|
|
+ DEF_MEMd(ld)
|
|
+
|
|
+ DEF_MEMd(stb)
|
|
+ DEF_MEMd(stw)
|
|
+ DEF_MEMd(stwu)
|
|
+ DEF_MEMd(sth)
|
|
+ DEF_MEMd(std)
|
|
+ DEF_MEMd(stdu)
|
|
+#undef DEF_MEMd
|
|
+
|
|
+#define DEF_MEMx(op) BufferOffset as_##op(Register rd, Register ra, Register rb);
|
|
+ DEF_MEMx(lbzx)
|
|
+ DEF_MEMx(lhax)
|
|
+ DEF_MEMx(lhzx)
|
|
+ DEF_MEMx(lhbrx)
|
|
+ DEF_MEMx(lwzx)
|
|
+ DEF_MEMx(lwbrx)
|
|
+ DEF_MEMx(lwax)
|
|
+ DEF_MEMx(lwarx)
|
|
+ DEF_MEMx(ldx)
|
|
+ DEF_MEMx(ldarx)
|
|
+
|
|
+ DEF_MEMx(stbx)
|
|
+ DEF_MEMx(stwx)
|
|
+ DEF_MEMx(stwux)
|
|
+ DEF_MEMx(stwbrx)
|
|
+ DEF_MEMx(sthx)
|
|
+ DEF_MEMx(sthbrx)
|
|
+ DEF_MEMx(stdx)
|
|
+ DEF_MEMx(stdcx)
|
|
+ DEF_MEMx(stdux)
|
|
+ DEF_MEMx(stwcx)
|
|
+#undef DEF_MEMx
|
|
+
|
|
+ BufferOffset as_isel(Register rt, Register ra, Register rb, uint16_t rc, CRegisterID cr = cr0);
|
|
+ BufferOffset as_isel0(Register rt, Register ra, Register rb, uint16_t rc, CRegisterID cr = cr0);
|
|
+
|
|
+ // FPR operations and load-stores.
|
|
+ BufferOffset as_fcmpo(CRegisterID cr, FloatRegister ra, FloatRegister rb);
|
|
+ BufferOffset as_fcmpo(FloatRegister ra, FloatRegister rb); // implied cr0
|
|
+ BufferOffset as_fcmpu(CRegisterID cr, FloatRegister ra, FloatRegister rb);
|
|
+ BufferOffset as_fcmpu(FloatRegister ra, FloatRegister rb); // implied cr0
|
|
+#define DEF_FPUAC(op) BufferOffset as_##op(FloatRegister rd, FloatRegister ra, FloatRegister rc); \
|
|
+ BufferOffset as_##op##_rc(FloatRegister rd, FloatRegister ra, FloatRegister rc);
|
|
+ DEF_FPUAC(fmul)
|
|
+ DEF_FPUAC(fmuls)
|
|
+#undef DEF_FPUAC
|
|
+
|
|
+#define DEF_FPUAB(op) BufferOffset as_##op(FloatRegister rd, FloatRegister ra, FloatRegister rc); \
|
|
+ BufferOffset as_##op##_rc(FloatRegister rd, FloatRegister ra, FloatRegister rc);
|
|
+ DEF_FPUAB(fadd)
|
|
+ DEF_FPUAB(fdiv)
|
|
+ DEF_FPUAB(fsub)
|
|
+ DEF_FPUAB(fadds)
|
|
+ DEF_FPUAB(fdivs)
|
|
+ DEF_FPUAB(fsubs)
|
|
+ DEF_FPUAB(fcpsgn)
|
|
+ DEF_FPUAB(fmrgew)
|
|
+#undef DEF_FPUAB
|
|
+
|
|
+#define DEF_FPUDS(op) BufferOffset as_##op(FloatRegister rd, FloatRegister rs); \
|
|
+ BufferOffset as_##op##_rc(FloatRegister rd, FloatRegister rs);
|
|
+ DEF_FPUDS(fabs)
|
|
+ DEF_FPUDS(fneg)
|
|
+ DEF_FPUDS(fmr)
|
|
+ DEF_FPUDS(fcfid)
|
|
+ DEF_FPUDS(fcfids)
|
|
+ DEF_FPUDS(fcfidu)
|
|
+ DEF_FPUDS(fcfidus)
|
|
+ DEF_FPUDS(fctid)
|
|
+ DEF_FPUDS(fctidz)
|
|
+ DEF_FPUDS(fctidu)
|
|
+ DEF_FPUDS(fctiduz)
|
|
+ DEF_FPUDS(fctiw)
|
|
+ DEF_FPUDS(fctiwz)
|
|
+ DEF_FPUDS(fctiwu)
|
|
+ DEF_FPUDS(fctiwuz)
|
|
+ DEF_FPUDS(frim)
|
|
+ DEF_FPUDS(frin)
|
|
+ DEF_FPUDS(frip)
|
|
+ DEF_FPUDS(friz)
|
|
+ DEF_FPUDS(frsp)
|
|
+ DEF_FPUDS(frsqrte)
|
|
+ DEF_FPUDS(fsqrt)
|
|
+ DEF_FPUDS(fsqrts)
|
|
+#undef DEF_FPUDS
|
|
+
|
|
+// In Ion, the semantics for this macro are now corrected compared to JM/PPCBC.
|
|
+// (See OPPCC p.432, etc.)
|
|
+#define DEF_FPUABC(op) BufferOffset as_##op(FloatRegister rd, FloatRegister ra, FloatRegister rc, FloatRegister rb); \
|
|
+ BufferOffset as_##op##_rc(FloatRegister rd, FloatRegister ra, FloatRegister rc, FloatRegister rb);
|
|
+ DEF_FPUABC(fmadd)
|
|
+ DEF_FPUABC(fnmsub)
|
|
+ DEF_FPUABC(fsel)
|
|
+#undef DEF_FPUABC
|
|
+
|
|
+#define DEF_FMEMd(op) BufferOffset as_##op(FloatRegister rd, Register rb, int16_t off);
|
|
+ DEF_FMEMd(lfd)
|
|
+ DEF_FMEMd(lfs)
|
|
+ DEF_FMEMd(stfd)
|
|
+ DEF_FMEMd(stfs)
|
|
+ DEF_FMEMd(stfdu)
|
|
+ DEF_FMEMd(stfsu)
|
|
+#undef DEF_FMEMd
|
|
+
|
|
+#define DEF_FMEMx(op) BufferOffset as_##op(FloatRegister rd, Register ra, Register rb);
|
|
+ DEF_FMEMx(lfdx)
|
|
+ DEF_FMEMx(lfsx)
|
|
+ DEF_FMEMx(lfiwax)
|
|
+ DEF_FMEMx(stfiwx)
|
|
+ DEF_FMEMx(stfdx)
|
|
+ DEF_FMEMx(stfsx)
|
|
+#undef DEF_FMEMx
|
|
+
|
|
+// convert SPRid to 10-bit split encoding (OPPCC appendix A, p.514)
|
|
+#define PPC_SPR(x) (((int)x>>5) | ((int)x & 31)<<5)
|
|
+
|
|
+ BufferOffset as_mtfsb0(uint8_t bt);
|
|
+ BufferOffset as_mtfsb1(uint8_t bt);
|
|
+ BufferOffset as_mtfsfi(uint8_t fi, uint8_t imm);
|
|
+ BufferOffset as_mcrf(CRegisterID bt, CRegisterID bs);
|
|
+ BufferOffset as_mcrfs(CRegisterID bf, uint8_t bfa);
|
|
+
|
|
+// VSX
|
|
+// Currently supported only for FPRs.
|
|
+ BufferOffset as_mfvsrd(Register ra, FloatRegister xs);
|
|
+ BufferOffset as_mtvsrd(FloatRegister xs, Register ra);
|
|
+ BufferOffset as_mtvsrwz(FloatRegister xs, Register ra);
|
|
+ BufferOffset as_mtvsrws(FloatRegister xs, Register ra);
|
|
+ BufferOffset as_xxbrd(FloatRegister xt, FloatRegister xb);
|
|
+ BufferOffset as_xscvdpsp(FloatRegister xt, FloatRegister xb);
|
|
+ BufferOffset as_xscvspdp(FloatRegister xt, FloatRegister xb);
|
|
+ BufferOffset as_xscvdpspn(FloatRegister xt, FloatRegister xb);
|
|
+ BufferOffset as_xscvspdpn(FloatRegister xt, FloatRegister xb);
|
|
+ BufferOffset as_xxlxor(FloatRegister xt, FloatRegister xa, FloatRegister xb);
|
|
+
|
|
+ BufferOffset as_addpcis(Register rt, uint16_t im = 0);
|
|
+
|
|
+ // Conveniences and generally accepted alternate mnemonics.
|
|
+// XXX: change these to xs_ and remove ones we don't actually use
|
|
+ BufferOffset xs_trap();
|
|
+ BufferOffset xs_trap_tagged(TrapTag tag); // Codegen for marking traps in output.
|
|
+ BufferOffset xs_mr(Register rd, Register ra);
|
|
+ BufferOffset xs_bcl_always(int16_t off, LikelyBit lkb = NotLikelyB);
|
|
+ BufferOffset x_beq(CRegisterID cr, int16_t off, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
|
+ BufferOffset x_bne(CRegisterID cr, int16_t off, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
|
+ BufferOffset xs_bdnz(int16_t off, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
|
+ BufferOffset xs_mtctr(Register ra);
|
|
+ BufferOffset xs_mtlr(Register ra);
|
|
+ BufferOffset xs_mflr(Register rd);
|
|
+ BufferOffset xs_mtcr(Register rs);
|
|
+ BufferOffset xs_mfxer(Register ra);
|
|
+ BufferOffset xs_mtxer(Register ra);
|
|
+ BufferOffset x_insertbits0_15(Register rd, Register rs);
|
|
+ BufferOffset x_bit_value(Register rd, Register rs, unsigned bit);
|
|
+ BufferOffset x_slwi(Register rd, Register rs, int n);
|
|
+ BufferOffset x_sldi(Register rd, Register rs, int n);
|
|
+ BufferOffset x_srwi(Register rd, Register rs, int n);
|
|
+ BufferOffset x_srdi(Register rd, Register rs, int n);
|
|
+ BufferOffset x_subi(Register rd, Register ra, int16_t im);
|
|
+ BufferOffset xs_sr_mulli(Register rd, Register ra, int16_t im);
|
|
+
|
|
+ BufferOffset x_not(Register rd, Register ra);
|
|
+
|
|
+ // Large loads.
|
|
+ BufferOffset xs_li(Register rd, int16_t im);
|
|
+ BufferOffset xs_lis(Register rd, int16_t im);
|
|
+
|
|
+ // Traps
|
|
+ BufferOffset as_tw(uint8_t to, Register ra, Register rb);
|
|
+ BufferOffset as_twi(uint8_t to, Register ra, int16_t si);
|
|
+ BufferOffset as_stop();
|
|
+
|
|
+ // Label operations.
|
|
+ void bind(InstImm* inst, uintptr_t branch, uintptr_t target, bool bound = false);
|
|
+ void bind(Label* label) { bind(label, nextOffset()); }
|
|
+ void bind(Label* label, BufferOffset boff);
|
|
+ void bind(CodeLabel *label) { label->target()->bind(currentOffset()); }
|
|
+ uint32_t currentOffset() {
|
|
+ return nextOffset().getOffset();
|
|
+ }
|
|
+ void retarget(Label *label, Label *target);
|
|
+ static void Bind(uint8_t *rawCode, const CodeLabel& label);
|
|
+
|
|
+/* XXX: Remove bindS*, those are TenFourFox-specific and we aren't going to implement them */
|
|
+ // Fast fixed branches.
|
|
+#define SHORT_LABEL(w) BufferOffset w = nextOffset()
|
|
+#define SHORT_LABEL_MASM(w) BufferOffset w = masm.nextOffset()
|
|
+ // Binds instruction i to offset s as a fixed short branch.
|
|
+ void bindS(BufferOffset s, BufferOffset i);
|
|
+ void bindSS(BufferOffset i); // s is implied as the current offset.
|
|
+
|
|
+ // See Bind.
|
|
+ size_t labelOffsetToPatchOffset(size_t offset) {
|
|
+ return actualOffset(offset);
|
|
+ }
|
|
+
|
|
+ void call(Label *label);
|
|
+ void call(void *target);
|
|
+
|
|
+ static void TraceJumpRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader);
|
|
+ static void TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader);
|
|
+ /*
|
|
+ static void FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader& reader,
|
|
+ const ObjectVector& nurseryObjects);
|
|
+ */
|
|
+ void assertNoGCThings() const {
|
|
+#ifdef DEBUG
|
|
+ MOZ_ASSERT(dataRelocations_.length() == 0);
|
|
+/* XXX: This can only be uint32.
|
|
+ for (auto& j : longJumps_) {
|
|
+ MOZ_ASSERT(j.kind == RelocationKind::HARDCODED);
|
|
+ }
|
|
+*/
|
|
+ for (auto& j : jumps_) {
|
|
+ MOZ_ASSERT(j.kind == RelocationKind::HARDCODED);
|
|
+ }
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ static bool SupportsFloatingPoint() {
|
|
+ // BaselineInterpreter is not enabled unless this is true, and nothing
|
|
+ // else is enabled if BaselineInterpreter isn't enabled, which yields
|
|
+ // a convenient way to disable the JIT at runtime if needed. (We also
|
|
+ // added code to disable irregexp JIT gated here as well.) Currently
|
|
+ // the JIT is only supported on ISA 3.0 and up (POWER9 and up) in
|
|
+ // little-endian mode. You can decide the manner in which you want the
|
|
+ // JIT handled:
|
|
+#if(0)
|
|
+ // Manually: unless you turn the JIT off, it's on. If you're writing
|
|
+ // support for a presently unsupported Power ISA CPU or big-endian
|
|
+ // mode, then change if(0) to if(1) and grab onto your ankles.
|
|
+ return true;
|
|
+#elif defined(__POWER9_VECTOR__) && defined(__LITTLE_ENDIAN__)
|
|
+ // Statically at compile time: if gcc is given --mcpu=power9, and it
|
|
+ // was compiled on a little-endian host, then the resulting binary will
|
|
+ // only work properly on ISA 3.0+ and thus the JIT must work too. All
|
|
+ // ISA checks become static as well, so it's also slightly faster.
|
|
+ return true;
|
|
+#elif defined(__LITTLE_ENDIAN__)
|
|
+ // Dynamically at runtime: ask the operating system. If AltiVec, VSX
|
|
+ // and VSX3 are all supported, then this must be a supported CPU. When
|
|
+ // additional CPU support is added, adjust the check here.
|
|
+ return HasPPCISA3();
|
|
+#else
|
|
+ // Otherwise, statically disable on all unsupported or big-endian CPUs.
|
|
+ return false;
|
|
+#endif
|
|
+ }
|
|
+ static bool SupportsSimd() { return false; } // todo
|
|
+
|
|
+ // Technically unaligned integer loads are supported in hardware, but
|
|
+ // there is a non-zero penalty (though small on many implementations),
|
|
+ // and certain unlikely edge cases can potentially fault to the operating
|
|
+ // system.
|
|
+ static bool SupportsUnalignedAccesses() { return true; }
|
|
+ static bool SupportsFastUnalignedAccesses() { return false; }
|
|
+
|
|
+ // However, 64-bit FP loads invariably fault to the operating system
|
|
+ // when unaligned, so we really want to avoid that.
|
|
+ static bool SupportsFastUnalignedFPAccesses() { return false; }
|
|
+
|
|
+ static bool HasRoundInstruction(RoundingMode mode) {
|
|
+ switch (mode) {
|
|
+ case RoundingMode::Up:
|
|
+ case RoundingMode::Down:
|
|
+ case RoundingMode::TowardsZero:
|
|
+ return true;
|
|
+
|
|
+ // See note in MacroAssembler::nearbyIntDouble.
|
|
+ case RoundingMode::NearestTiesToEven:
|
|
+ return false;
|
|
+
|
|
+ // fall through
|
|
+ }
|
|
+ MOZ_CRASH("unexpected mode");
|
|
+ }
|
|
+
|
|
+ protected:
|
|
+ void bind(Instruction *inst, uint32_t branch, uint32_t target);
|
|
+ void addPendingJump(BufferOffset src, ImmPtr target, RelocationKind kind) {
|
|
+ enoughMemory_ &= jumps_.append(RelativePatch(src, target.value, kind));
|
|
+ if (kind == RelocationKind::JITCODE)
|
|
+ writeRelocation(src);
|
|
+ }
|
|
+
|
|
+ void addLongJump(BufferOffset src, BufferOffset dst) {
|
|
+ //enoughMemory_ &= longJumps_.append(src.getOffset());
|
|
+ CodeLabel cl;
|
|
+ cl.patchAt()->bind(src.getOffset());
|
|
+ cl.target()->bind(dst.getOffset());
|
|
+ cl.setLinkMode(CodeLabel::JumpImmediate);
|
|
+ addCodeLabel(std::move(cl));
|
|
+ }
|
|
+
|
|
+ public:
|
|
+ /*
|
|
+ size_t numLongJumps() const {
|
|
+ return longJumps_.length();
|
|
+ }
|
|
+ uint32_t longJump(size_t i) {
|
|
+ return longJumps_[i];
|
|
+ }
|
|
+ */
|
|
+
|
|
+ void comment(const char *msg) {
|
|
+ }
|
|
+ // Copy the assembly code to the given buffer, and perform any pending
|
|
+ // relocations relying on the target address.
|
|
+ void executableCopy(uint8_t *buffer);
|
|
+
|
|
+ void flushBuffer() {
|
|
+ }
|
|
+
|
|
+ BufferOffset haltingAlign(int alignment);
|
|
+ BufferOffset nopAlign(int alignment);
|
|
+
|
|
+ static uint32_t PatchWrite_NearCallSize();
|
|
+ static uint32_t NopSize() { return 4; }
|
|
+
|
|
+ static uint32_t ExtractLisOriValue(Instruction *inst0, Instruction *inst1);
|
|
+ static void UpdateLisOriValue(Instruction *inst0, Instruction *inst1, uint32_t value);
|
|
+ static void WriteLisOriInstructions(Instruction *inst, Instruction *inst1,
|
|
+ Register reg, uint32_t value);
|
|
+
|
|
+ static void PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall);
|
|
+ static void PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue,
|
|
+ PatchedImmPtr expectedValue);
|
|
+ static void PatchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue,
|
|
+ ImmPtr expectedValue);
|
|
+ static void PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm);
|
|
+
|
|
+ static void PatchInstructionImmediate(uint8_t *code, PatchedImmPtr imm);
|
|
+
|
|
+ static uint8_t *NextInstruction(uint8_t *instruction, uint32_t *count = nullptr);
|
|
+
|
|
+ static void ToggleToJmp(CodeLocationLabel inst_);
|
|
+ static void ToggleToCmp(CodeLocationLabel inst_);
|
|
+
|
|
+ static void ToggleCall(CodeLocationLabel inst_, bool enabled);
|
|
+
|
|
+ static void UpdateBoundsCheck(uint32_t logHeapSize, Instruction *inst);
|
|
+ void processCodeLabels(uint8_t *rawCode);
|
|
+ static int32_t ExtractCodeLabelOffset(uint8_t *code);
|
|
+
|
|
+ void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end,
|
|
+ const Disassembler::HeapAccess& heapAccess)
|
|
+ {
|
|
+ // Implement this if we implement a disassembler.
|
|
+ }
|
|
+
|
|
+ bool swapBuffer(wasm::Bytes& bytes);
|
|
+ bool reserve(size_t size) { return !oom(); }
|
|
+}; // Assembler
|
|
+
|
|
+static const uint32_t OpcodeShift = 26;
|
|
+static const uint32_t OpcodeBits = 6;
|
|
+
|
|
+class Instruction
|
|
+{
|
|
+ protected:
|
|
+ uint32_t data;
|
|
+
|
|
+ public:
|
|
+ Instruction (uint32_t data_) : data(data_) { }
|
|
+ Instruction (PPCOpcodes op_) : data((uint32_t)op_) { }
|
|
+
|
|
+ uint32_t encode() const {
|
|
+ return data;
|
|
+ }
|
|
+
|
|
+ // Quickies
|
|
+ void makeOp_nop() {
|
|
+ data = PPC_nop;
|
|
+ }
|
|
+ void makeOp_mtctr(Register r) {
|
|
+ data = PPC_mtspr | ((uint32_t)r.code())<<21 | PPC_SPR(ctr)<<11 ;
|
|
+ }
|
|
+ void makeOp_bctr(Assembler::LinkBit l = Assembler::DontLinkB) {
|
|
+ data = PPC_bctr | l;
|
|
+ }
|
|
+
|
|
+ void setData(uint32_t data) {
|
|
+ this->data = data;
|
|
+ }
|
|
+
|
|
+ const Instruction & operator=(const Instruction &src) {
|
|
+ data = src.data;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ uint32_t extractOpcode() {
|
|
+ return (data & PPC_MAJOR_OPCODE_MASK); // ">> 26"
|
|
+ }
|
|
+ bool isOpcode(uint32_t op) {
|
|
+ return (extractOpcode() == (op & PPC_MAJOR_OPCODE_MASK));
|
|
+ }
|
|
+ void assertOpcode(uint32_t op) {
|
|
+ MOZ_ASSERT(isOpcode(op));
|
|
+ }
|
|
+
|
|
+ // Get the next instruction in the instruction stream.
|
|
+ // This will do neat things like ignore constant pools and their guards,
|
|
+ // if we ever implement those again.
|
|
+ Instruction *next() { return this + 1; };
|
|
+
|
|
+ // Sometimes the backend wants a uint32_t (or a pointer to it) rather than
|
|
+ // an instruction. raw() just coerces this into a pointer to a uint32_t.
|
|
+ const uint32_t *raw() const { return &data; }
|
|
+ uint32_t size() const { return 4; }
|
|
+}; // Instruction
|
|
+
|
|
+class InstReg : public Instruction
|
|
+{
|
|
+ // Valid for reg/reg/reg instructions only.
|
|
+ // XXX: Assert that at some point.
|
|
+ public:
|
|
+ InstReg (PPCOpcodes op) : Instruction(op) { }
|
|
+ InstReg (PPCOpcodes op, Register rd, Register ra, Register rb)
|
|
+ : Instruction(op | ((uint32_t)rd.code() << 21) |
|
|
+ ((uint32_t)ra.code() << 16) | (uint32_t)rb.code() << 11) {}
|
|
+
|
|
+
|
|
+ void setBOffImm16(BOffImm16 off) {
|
|
+ data = (data & 0xFFFF0000) | off.encode();
|
|
+ }
|
|
+ void setImm16(Imm16 off) {
|
|
+ data = (data & 0xFFFF0000) | off.encode();
|
|
+ }
|
|
+ uint32_t extractImm16Value() {
|
|
+ return (data & 0x0000FFFF);
|
|
+ }
|
|
+ void setTargetReg(Register rt) {
|
|
+ data = (data & 0xFC1FFFFF) | ((uint32_t)rt.code() << 21);
|
|
+ }
|
|
+ void setUpperReg(Register ru) {
|
|
+ // Mask out upper register field and put in the new one.
|
|
+ // For addi/addis, this is the DESTINATION.
|
|
+ // For ori/oris, this is the SOURCE.
|
|
+ // For bc, this is BO.
|
|
+ data = (data & 0xFFE0FFFF) | ((uint32_t)ru.code() << 16);
|
|
+ }
|
|
+ void setLowerReg(Register rl) {
|
|
+ // Mask out lower register field and put in the new one.
|
|
+ // For addi/addis, this is the SOURCE. (For li/lis, this should be ZERO.)
|
|
+ // For ori/oris, this is the DESTINATION.
|
|
+ // For bc, this is BI.
|
|
+ data = (data & 0xFFFF07FF) | ((uint32_t)rl.code() << 11);
|
|
+ }
|
|
+}; // InstReg
|
|
+
|
|
+class InstImm : public Instruction
|
|
+{
|
|
+ // Valid for reg/reg/imm instructions only and bc.
|
|
+ // XXX: Assert that at some point.
|
|
+ public:
|
|
+ InstImm (PPCOpcodes op) : Instruction(op) { }
|
|
+ InstImm (PPCOpcodes op, Register ra, Register rs, Imm16 im)
|
|
+ : Instruction(op | ((uint32_t)ra.code() << 21) |
|
|
+ ((uint32_t)rs.code() << 16) | im.encode()) {}
|
|
+
|
|
+ void setBOffImm16(BOffImm16 off) {
|
|
+ data = (data & 0xFFFF0000) | off.encode();
|
|
+ }
|
|
+ void setImm16(Imm16 off) {
|
|
+ data = (data & 0xFFFF0000) | off.encode();
|
|
+ }
|
|
+ uint32_t extractImm16Value() {
|
|
+ return (data & 0x0000FFFF);
|
|
+ }
|
|
+ void setUpperReg(Register ru) {
|
|
+ // Mask out upper register field and put in the new one.
|
|
+ // For addi/addis, this is the DESTINATION.
|
|
+ // For ori/oris, this is the SOURCE.
|
|
+ // For bc, this is BO.
|
|
+ data = (data & 0xFC1FFFFF) | ((uint32_t)ru.code() << 21);
|
|
+ }
|
|
+ void setLowerReg(Register rl) {
|
|
+ // Mask out lower register field and put in the new one.
|
|
+ // For addi/addis, this is the SOURCE. (For li/lis, this should be ZERO.)
|
|
+ // For ori/oris, this is the DESTINATION.
|
|
+ // For bc, this is BI.
|
|
+ data = (data & 0xFFE0FFFF) | ((uint32_t)rl.code() << 16);
|
|
+ }
|
|
+ Assembler::TrapTag traptag();
|
|
+}; // InstImm
|
|
+
|
|
+// If this assert is not satisfied, we can't use Instruction to patch in-place.
|
|
+static_assert(sizeof(Instruction) == 4, "sizeof(Instruction) must be 32-bit word.");
|
|
+
|
|
+static const uint32_t NumIntArgRegs = 8;
|
|
+
|
|
+static inline bool
|
|
+GetIntArgReg(uint32_t usedArgSlots, Register *out)
|
|
+{
|
|
+ if (usedArgSlots < NumIntArgRegs) {
|
|
+ // Argregs start at r3!
|
|
+ *out = Register::FromCode((Register::Code)(3 + usedArgSlots));
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+// Get a register in which we plan to put a quantity that will be used as an
|
|
+// integer argument. Because we have so many argument registers on PowerPC, this
|
|
+// essentially stubs into GetIntArgReg because failure is incredibly improbable.
|
|
+static inline bool
|
|
+GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register *out)
|
|
+{
|
|
+ // NB: this implementation can't properly determine yet which regs are used if there are
|
|
+ // float arguments.
|
|
+ MOZ_ASSERT(usedFloatArgs == 0);
|
|
+
|
|
+ if (GetIntArgReg(usedIntArgs, out))
|
|
+ return true;
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static inline uint32_t
|
|
+GetArgStackDisp(uint32_t usedArgSlots)
|
|
+{
|
|
+#if(0)
|
|
+ // NYI. This situation should never occur.
|
|
+ MOZ_ASSERT(usedArgSlots >= NumIntArgRegs);
|
|
+ return usedArgSlots * sizeof(intptr_t);
|
|
+#else
|
|
+ MOZ_CRASH("unexpected spill to stack");
|
|
+ return 0;
|
|
+#endif
|
|
+}
|
|
+
|
|
+inline bool IsUnaligned(const wasm::MemoryAccessDesc& access) {
|
|
+ if (!access.align()) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return access.align() < access.byteSize();
|
|
+}
|
|
+
|
|
+} // namespace jit
|
|
+} // namespace js
|
|
+
|
|
+// Convenience macros from JM/PPCBC.
|
|
+
|
|
+// whether a (Trusted)Imm32 fits in an unsigned immediate value
|
|
+#define PPC_IMM_OK_U(x) (MOZ_LIKELY(((x).m_value & 0xffff0000) == 0))
|
|
+
|
|
+// whether a (Trusted)Imm32 fits in a signed immediate value
|
|
+#define PPC_IMM_OK_S(x) (MOZ_LIKELY(((x).m_value & 0xffff8000) == 0 || \
|
|
+ ((x).m_value & 0xffff8000) == 0xffff8000))
|
|
+
|
|
+// whether the offset part of an Address fits in a (signed) immediate value
|
|
+#define PPC_OFFS_OK(x) (MOZ_LIKELY(((x).offset & 0xffff8000) == 0 || \
|
|
+ ((x).offset & 0xffff8000) == 0xffff8000))
|
|
+
|
|
+// same test, but checking a bit ahead (for multiple loads)
|
|
+#define PPC_OFFS_INCR_OK(x, incr) (MOZ_LIKELY((((x).offset + incr) & 0xffff8000) == 0 || \
|
|
+ (((x).offset + incr) & 0xffff8000) == 0xffff8000))
|
|
+
|
|
+#endif /* jit_ppc_Assembler_ppc_h */
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/AtomicOperations-ppc64.h
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/AtomicOperations-ppc64.h Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,200 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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/. */
|
|
+
|
|
+/* For documentation, see jit/AtomicOperations.h */
|
|
+
|
|
+#ifndef jit_ppc64le_AtomicOperations_ppc64le_h
|
|
+#define jit_ppc64le_AtomicOperations_ppc64le_h
|
|
+
|
|
+#include "mozilla/Assertions.h"
|
|
+#include "mozilla/Types.h"
|
|
+
|
|
+#include "builtin/AtomicsObject.h"
|
|
+#include "vm/ArrayBufferObject.h"
|
|
+
|
|
+#if !defined(__clang__) && !defined(__GNUC__)
|
|
+# error "This file only for gcc-compatible compilers"
|
|
+#endif
|
|
+
|
|
+inline bool
|
|
+js::jit::AtomicOperations::hasAtomic8()
|
|
+{
|
|
+ return true; // XXX?
|
|
+}
|
|
+
|
|
+inline bool
|
|
+js::jit::AtomicOperations::isLockfree8()
|
|
+{
|
|
+ MOZ_ASSERT(__atomic_always_lock_free(sizeof(int8_t), 0));
|
|
+ MOZ_ASSERT(__atomic_always_lock_free(sizeof(int16_t), 0));
|
|
+ MOZ_ASSERT(__atomic_always_lock_free(sizeof(int32_t), 0));
|
|
+ MOZ_ASSERT(__atomic_always_lock_free(sizeof(int64_t), 0));
|
|
+ return true;
|
|
+}
|
|
+
|
|
+inline void
|
|
+js::jit::AtomicOperations::fenceSeqCst()
|
|
+{
|
|
+ __atomic_thread_fence(__ATOMIC_SEQ_CST);
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+inline T
|
|
+js::jit::AtomicOperations::loadSeqCst(T* addr)
|
|
+{
|
|
+ static_assert(sizeof(T) <= sizeof(void*), "atomics supported up to pointer size only");
|
|
+ T v;
|
|
+ __atomic_load(addr, &v, __ATOMIC_SEQ_CST);
|
|
+ return v;
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+inline void
|
|
+js::jit::AtomicOperations::storeSeqCst(T* addr, T val)
|
|
+{
|
|
+ static_assert(sizeof(T) <= sizeof(void*), "atomics supported up to pointer size only");
|
|
+ __atomic_store(addr, &val, __ATOMIC_SEQ_CST);
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+inline T
|
|
+js::jit::AtomicOperations::compareExchangeSeqCst(T* addr, T oldval, T newval)
|
|
+{
|
|
+ static_assert(sizeof(T) <= sizeof(void*), "atomics supported up to pointer size only");
|
|
+ __atomic_compare_exchange(addr, &oldval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
|
+ return oldval;
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+inline T
|
|
+js::jit::AtomicOperations::fetchAddSeqCst(T* addr, T val)
|
|
+{
|
|
+ static_assert(sizeof(T) <= sizeof(void*), "atomics supported up to pointer size only");
|
|
+ return __atomic_fetch_add(addr, val, __ATOMIC_SEQ_CST);
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+inline T
|
|
+js::jit::AtomicOperations::fetchSubSeqCst(T* addr, T val)
|
|
+{
|
|
+ static_assert(sizeof(T) <= sizeof(void*), "atomics supported up to pointer size only");
|
|
+ return __atomic_fetch_sub(addr, val, __ATOMIC_SEQ_CST);
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+inline T
|
|
+js::jit::AtomicOperations::fetchAndSeqCst(T* addr, T val)
|
|
+{
|
|
+ static_assert(sizeof(T) <= sizeof(void*), "atomics supported up to pointer size only");
|
|
+ return __atomic_fetch_and(addr, val, __ATOMIC_SEQ_CST);
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+inline T
|
|
+js::jit::AtomicOperations::fetchOrSeqCst(T* addr, T val)
|
|
+{
|
|
+ static_assert(sizeof(T) <= sizeof(void*), "atomics supported up to pointer size only");
|
|
+ return __atomic_fetch_or(addr, val, __ATOMIC_SEQ_CST);
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+inline T
|
|
+js::jit::AtomicOperations::fetchXorSeqCst(T* addr, T val)
|
|
+{
|
|
+ static_assert(sizeof(T) <= sizeof(void*), "atomics supported up to pointer size only");
|
|
+ return __atomic_fetch_xor(addr, val, __ATOMIC_SEQ_CST);
|
|
+
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+inline T
|
|
+js::jit::AtomicOperations::loadSafeWhenRacy(T* addr)
|
|
+{
|
|
+ static_assert(sizeof(T) <= sizeof(void*), "atomics supported up to pointer size only");
|
|
+ T v;
|
|
+ __atomic_load(addr, &v, __ATOMIC_RELAXED);
|
|
+ return v;
|
|
+}
|
|
+
|
|
+template<>
|
|
+inline uint8_clamped
|
|
+js::jit::AtomicOperations::loadSafeWhenRacy(uint8_clamped* addr)
|
|
+{
|
|
+ uint8_t v;
|
|
+ __atomic_load(&addr->val, &v, __ATOMIC_RELAXED);
|
|
+ return uint8_clamped(v);
|
|
+}
|
|
+
|
|
+template<>
|
|
+inline float
|
|
+js::jit::AtomicOperations::loadSafeWhenRacy(float* addr)
|
|
+{
|
|
+ return *addr;
|
|
+}
|
|
+
|
|
+template<>
|
|
+inline double
|
|
+js::jit::AtomicOperations::loadSafeWhenRacy(double* addr)
|
|
+{
|
|
+ return *addr;
|
|
+}
|
|
+
|
|
+} }
|
|
+
|
|
+template<typename T>
|
|
+inline void
|
|
+js::jit::AtomicOperations::storeSafeWhenRacy(T* addr, T val)
|
|
+{
|
|
+ static_assert(sizeof(T) <= sizeof(void*), "atomics supported up to pointer size only");
|
|
+ __atomic_store(addr, &val, __ATOMIC_RELAXED);
|
|
+}
|
|
+
|
|
+template<>
|
|
+inline void
|
|
+js::jit::AtomicOperations::storeSafeWhenRacy(uint8_clamped* addr, uint8_clamped val)
|
|
+{
|
|
+ __atomic_store(&addr->val, &val.val, __ATOMIC_RELAXED);
|
|
+}
|
|
+
|
|
+template<>
|
|
+inline void
|
|
+js::jit::AtomicOperations::storeSafeWhenRacy(float* addr, float val)
|
|
+{
|
|
+ *addr = val;
|
|
+}
|
|
+
|
|
+template<>
|
|
+inline void
|
|
+js::jit::AtomicOperations::storeSafeWhenRacy(double* addr, double val)
|
|
+{
|
|
+ *addr = val;
|
|
+}
|
|
+
|
|
+inline void
|
|
+js::jit::AtomicOperations::memcpySafeWhenRacy(void* dest, const void* src, size_t nbytes)
|
|
+{
|
|
+ MOZ_ASSERT(!((char*)dest <= (char*)src && (char*)src < (char*)dest+nbytes));
|
|
+ MOZ_ASSERT(!((char*)src <= (char*)dest && (char*)dest < (char*)src+nbytes));
|
|
+ ::memcpy(dest, src, nbytes);
|
|
+}
|
|
+
|
|
+inline void
|
|
+js::jit::AtomicOperations::memmoveSafeWhenRacy(void* dest, const void* src, size_t nbytes)
|
|
+{
|
|
+ ::memmove(dest, src, nbytes);
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+inline T
|
|
+js::jit::AtomicOperations::exchangeSeqCst(T* addr, T val)
|
|
+{
|
|
+ static_assert(sizeof(T) <= sizeof(void*), "atomics supported up to pointer size only");
|
|
+ T v;
|
|
+ __atomic_exchange(addr, &val, &v, __ATOMIC_SEQ_CST);
|
|
+ return v;
|
|
+}
|
|
+
|
|
+#endif // jit_ppc64le_AtomicOperations_ppc64le_h
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/CodeGenerator-ppc64.cpp
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/CodeGenerator-ppc64.cpp Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,3596 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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/. */
|
|
+
|
|
+/* Code generator for little-endian 64-bit PowerPC compliant to Power ISA v3.0B. */
|
|
+
|
|
+#include "jit/ppc64/CodeGenerator-ppc64.h"
|
|
+
|
|
+#include "mozilla/DebugOnly.h"
|
|
+#include "mozilla/MathAlgorithms.h"
|
|
+
|
|
+#include "jsnum.h"
|
|
+
|
|
+#include "jit/CodeGenerator.h"
|
|
+#include "jit/JitFrames.h"
|
|
+//#include "jit/JitRealm.h"
|
|
+#include "jit/JitRuntime.h"
|
|
+#include "jit/MIR.h"
|
|
+#include "jit/MIRGraph.h"
|
|
+#include "js/Conversions.h"
|
|
+#include "vm/JSContext.h"
|
|
+#include "vm/Realm.h"
|
|
+#include "vm/Shape.h"
|
|
+
|
|
+#include "jit/MacroAssembler-inl.h"
|
|
+#include "jit/shared/CodeGenerator-shared-inl.h"
|
|
+#include "vm/JSScript-inl.h"
|
|
+
|
|
+using namespace js;
|
|
+using namespace js::jit;
|
|
+
|
|
+using mozilla::DebugOnly;
|
|
+using mozilla::FloorLog2;
|
|
+using mozilla::NegativeInfinity;
|
|
+using JS::GenericNaN;
|
|
+using JS::ToInt32;
|
|
+
|
|
+#if DEBUG
|
|
+
|
|
+/* Useful class to print visual guard blocks. */
|
|
+class AutoDeBlock
|
|
+{
|
|
+ private:
|
|
+ const char *blockname;
|
|
+
|
|
+ public:
|
|
+ AutoDeBlock(const char *name) {
|
|
+ blockname = name;
|
|
+ JitSpew(JitSpew_Codegen, "[[ CGPPC64: %s", blockname);
|
|
+ }
|
|
+
|
|
+ ~AutoDeBlock() {
|
|
+ JitSpew(JitSpew_Codegen, " CGPPC64: %s ]]", blockname);
|
|
+ }
|
|
+};
|
|
+#undef ADBlock
|
|
+#define ADBlock() AutoDeBlock _adbx(__PRETTY_FUNCTION__)
|
|
+
|
|
+#else
|
|
+
|
|
+/* Useful macro to completely elide visual guard blocks. */
|
|
+#define ADBlock() ;
|
|
+
|
|
+#endif
|
|
+
|
|
+void OutOfLineBailout::accept(CodeGeneratorPPC64* codegen) {
|
|
+ codegen->visitOutOfLineBailout(this);
|
|
+}
|
|
+
|
|
+CodeGeneratorPPC64::CodeGeneratorPPC64(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm)
|
|
+ : CodeGeneratorShared(gen, graph, masm)
|
|
+{
|
|
+}
|
|
+
|
|
+ValueOperand
|
|
+CodeGeneratorPPC64::ToValue(LInstruction* ins, size_t pos)
|
|
+{
|
|
+ return ValueOperand(ToRegister(ins->getOperand(pos)));
|
|
+}
|
|
+
|
|
+ValueOperand
|
|
+CodeGeneratorPPC64::ToTempValue(LInstruction* ins, size_t pos)
|
|
+{
|
|
+ return ValueOperand(ToRegister(ins->getTemp(pos)));
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitBox(LBox* box)
|
|
+{
|
|
+ ADBlock();
|
|
+ const LAllocation* in = box->getOperand(0);
|
|
+ const LDefinition* result = box->getDef(0);
|
|
+
|
|
+ if (IsFloatingPointType(box->type())) {
|
|
+ FloatRegister reg = ToFloatRegister(in);
|
|
+ if (box->type() == MIRType::Float32) {
|
|
+ masm.convertFloat32ToDouble(reg, ScratchDoubleReg);
|
|
+ reg = ScratchDoubleReg;
|
|
+ }
|
|
+ masm.moveFromDouble(reg, ToRegister(result));
|
|
+ } else {
|
|
+ masm.boxValue(ValueTypeFromMIRType(box->type()), ToRegister(in), ToRegister(result));
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitUnbox(LUnbox* unbox)
|
|
+{
|
|
+ ADBlock();
|
|
+ MUnbox* mir = unbox->mir();
|
|
+ Register result = ToRegister(unbox->output());
|
|
+
|
|
+ if (mir->fallible()) {
|
|
+ const ValueOperand value = ToValue(unbox, LUnbox::Input);
|
|
+ Label bail;
|
|
+ switch (mir->type()) {
|
|
+ case MIRType::Int32:
|
|
+ masm.fallibleUnboxInt32(value, result, &bail);
|
|
+ break;
|
|
+ case MIRType::Boolean:
|
|
+ masm.fallibleUnboxBoolean(value, result, &bail);
|
|
+ break;
|
|
+ case MIRType::Object:
|
|
+ masm.fallibleUnboxObject(value, result, &bail);
|
|
+ break;
|
|
+ case MIRType::String:
|
|
+ masm.fallibleUnboxString(value, result, &bail);
|
|
+ break;
|
|
+ case MIRType::Symbol:
|
|
+ masm.fallibleUnboxSymbol(value, result, &bail);
|
|
+ break;
|
|
+ case MIRType::BigInt:
|
|
+ masm.fallibleUnboxBigInt(value, result, &bail);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("Given MIRType cannot be unboxed.");
|
|
+ }
|
|
+ bailoutFrom(&bail, unbox->snapshot());
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ LAllocation* input = unbox->getOperand(LUnbox::Input);
|
|
+ if (input->isRegister()) {
|
|
+ Register inputReg = ToRegister(input);
|
|
+ switch (mir->type()) {
|
|
+ case MIRType::Int32:
|
|
+ masm.unboxInt32(inputReg, result);
|
|
+ break;
|
|
+ case MIRType::Boolean:
|
|
+ masm.unboxBoolean(inputReg, result);
|
|
+ break;
|
|
+ case MIRType::Object:
|
|
+ masm.unboxObject(inputReg, result);
|
|
+ break;
|
|
+ case MIRType::String:
|
|
+ masm.unboxString(inputReg, result);
|
|
+ break;
|
|
+ case MIRType::Symbol:
|
|
+ masm.unboxSymbol(inputReg, result);
|
|
+ break;
|
|
+ case MIRType::BigInt:
|
|
+ masm.unboxBigInt(inputReg, result);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("Given MIRType cannot be unboxed.");
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Address inputAddr = ToAddress(input);
|
|
+ switch (mir->type()) {
|
|
+ case MIRType::Int32:
|
|
+ masm.unboxInt32(inputAddr, result);
|
|
+ break;
|
|
+ case MIRType::Boolean:
|
|
+ masm.unboxBoolean(inputAddr, result);
|
|
+ break;
|
|
+ case MIRType::Object:
|
|
+ masm.unboxObject(inputAddr, result);
|
|
+ break;
|
|
+ case MIRType::String:
|
|
+ masm.unboxString(inputAddr, result);
|
|
+ break;
|
|
+ case MIRType::Symbol:
|
|
+ masm.unboxSymbol(inputAddr, result);
|
|
+ break;
|
|
+ case MIRType::BigInt:
|
|
+ masm.unboxBigInt(inputAddr, result);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("Given MIRType cannot be unboxed.");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGeneratorPPC64::splitTagForTest(const ValueOperand& value, ScratchTagScope& tag)
|
|
+{
|
|
+ masm.splitTag(value.valueReg(), tag);
|
|
+}
|
|
+
|
|
+// XXX: needs move or rewrite
|
|
+#if(0)
|
|
+void
|
|
+CodeGenerator::visitCompareB(LCompareB* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ MCompare* mir = lir->mir();
|
|
+
|
|
+ const ValueOperand lhs = ToValue(lir, LCompareB::Lhs);
|
|
+ const LAllocation* rhs = lir->rhs();
|
|
+ const Register output = ToRegister(lir->output());
|
|
+
|
|
+ MOZ_ASSERT(mir->jsop() == JSOp::StrictEq || mir->jsop() == JSOp::StrictNe);
|
|
+ Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
|
|
+
|
|
+ // Load boxed boolean in ScratchRegister.
|
|
+ if (rhs->isConstant())
|
|
+ masm.moveValue(rhs->toConstant()->toJSValue(), ValueOperand(ScratchRegister));
|
|
+ else
|
|
+ masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchRegister);
|
|
+
|
|
+ // Perform the comparison.
|
|
+ masm.cmpPtrSet(cond, lhs.valueReg(), ScratchRegister, output);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitCompareBAndBranch(LCompareBAndBranch* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ MCompare* mir = lir->cmpMir();
|
|
+ const ValueOperand lhs = ToValue(lir, LCompareBAndBranch::Lhs);
|
|
+ const LAllocation* rhs = lir->rhs();
|
|
+
|
|
+ MOZ_ASSERT(mir->jsop() == JSOp::StrictEq || mir->jsop() == JSOp::StrictNe);
|
|
+
|
|
+ // Load boxed boolean in ScratchRegister.
|
|
+ if (rhs->isConstant())
|
|
+ masm.moveValue(rhs->toConstant()->toJSValue(), ValueOperand(ScratchRegister));
|
|
+ else
|
|
+ masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchRegister);
|
|
+
|
|
+ // Perform the comparison.
|
|
+ Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
|
|
+ emitBranch(lhs.valueReg(), ScratchRegister, cond, lir->ifTrue(), lir->ifFalse());
|
|
+}
|
|
+#endif
|
|
+
|
|
+void
|
|
+CodeGenerator::visitCompareI64(LCompareI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ MCompare* mir = lir->mir();
|
|
+ MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
|
|
+ mir->compareType() == MCompare::Compare_UInt64);
|
|
+
|
|
+ const LInt64Allocation lhs = lir->getInt64Operand(LCompareI64::Lhs);
|
|
+ const LInt64Allocation rhs = lir->getInt64Operand(LCompareI64::Rhs);
|
|
+ Register lhsReg = ToRegister64(lhs).reg;
|
|
+ Register output = ToRegister(lir->output());
|
|
+ Register rhsReg;
|
|
+
|
|
+ if (IsConstant(rhs)) {
|
|
+ rhsReg = ScratchRegister;
|
|
+ masm.ma_li(rhsReg, ImmWord(ToInt64(rhs)));
|
|
+ } else {
|
|
+ rhsReg = ToRegister64(rhs).reg;
|
|
+ }
|
|
+
|
|
+ bool isSigned = mir->compareType() == MCompare::Compare_Int64;
|
|
+ masm.cmpPtrSet(JSOpToCondition(lir->jsop(), isSigned), lhsReg, rhsReg, output);
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmExtendU32Index(LWasmExtendU32Index* lir) {
|
|
+ // Generates no code on this platform because the input is assumed to have
|
|
+ // canonical form.
|
|
+ Register output = ToRegister(lir->output());
|
|
+ MOZ_ASSERT(ToRegister(lir->input()) == output);
|
|
+ masm.debugAssertCanonicalInt32(output);
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmWrapU32Index(LWasmWrapU32Index* lir) {
|
|
+ // Generates no code on this platform because the input is assumed to have
|
|
+ // canonical form.
|
|
+ Register output = ToRegister(lir->output());
|
|
+ MOZ_ASSERT(ToRegister(lir->input()) == output);
|
|
+ masm.debugAssertCanonicalInt32(output);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitCompareI64AndBranch(LCompareI64AndBranch* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ MCompare* mir = lir->cmpMir();
|
|
+ MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
|
|
+ mir->compareType() == MCompare::Compare_UInt64);
|
|
+
|
|
+ const LInt64Allocation lhs = lir->getInt64Operand(LCompareI64::Lhs);
|
|
+ const LInt64Allocation rhs = lir->getInt64Operand(LCompareI64::Rhs);
|
|
+ Register lhsReg = ToRegister64(lhs).reg;
|
|
+ Register rhsReg;
|
|
+
|
|
+ if (IsConstant(rhs)) {
|
|
+ rhsReg = ScratchRegister;
|
|
+ masm.ma_li(rhsReg, ImmWord(ToInt64(rhs)));
|
|
+ } else {
|
|
+ rhsReg = ToRegister64(rhs).reg;
|
|
+ }
|
|
+
|
|
+ bool isSigned = mir->compareType() == MCompare::Compare_Int64;
|
|
+ Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned);
|
|
+ emitBranch(lhsReg, rhsReg, cond, lir->ifTrue(), lir->ifFalse());
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitDivOrModI64(LDivOrModI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ // divdo will automatically set the Overflow bit if INT64_MIN/-1 is
|
|
+ // performed, or if we divide by zero, so we just bailout based on that.
|
|
+
|
|
+ Label noOverflow, done;
|
|
+ Register lhs = ToRegister(lir->lhs());
|
|
+ Register rhs = ToRegister(lir->rhs());
|
|
+ Register output = ToRegister(lir->output());
|
|
+
|
|
+ // DIVIDE! AND! CONQUER!
|
|
+ if (lir->snapshot()) {
|
|
+ // If Overflow is set, we must bail out. But only do this if
|
|
+ // there is a snapshot, because if there isn't, Ion already knows
|
|
+ // this operation is infallible (and we can avoid checking
|
|
+ // for overflow, which is slightly faster).
|
|
+ masm.ma_li(output, 0L);
|
|
+ masm.xs_mtxer(output); // whack XER[SO]
|
|
+ masm.as_divdo_rc(output, lhs, rhs); // T = A / B
|
|
+
|
|
+ // Did we trap?
|
|
+ masm.ma_bc(Assembler::NSOBit, &noOverflow, ShortJump);
|
|
+ // Yes. Determine how.
|
|
+ MOZ_ASSERT(lir->canBeNegativeOverflow() || lir->canBeDivideByZero());
|
|
+ if (lir->canBeNegativeOverflow()) {
|
|
+ if (lir->canBeDivideByZero()) {
|
|
+ Label notNegOne;
|
|
+
|
|
+ masm.branchPtr(Assembler::NotEqual, rhs, ImmWord(-1), ¬NegOne);
|
|
+ // If rhs was -1, then the offending operation must have been
|
|
+ // INT64_MIN/-1.
|
|
+ if (lir->mir()->isMod())
|
|
+ masm.move32(Imm32(0), output);
|
|
+ else
|
|
+ masm.wasmTrap(wasm::Trap::IntegerOverflow, lir->bytecodeOffset());
|
|
+ masm.ma_b(&done, ShortJump);
|
|
+ masm.bind(¬NegOne);
|
|
+ } else {
|
|
+ // Just trap, it's the only possible triggering condition.
|
|
+ masm.wasmTrap(wasm::Trap::IntegerOverflow, lir->bytecodeOffset());
|
|
+ }
|
|
+ }
|
|
+ if (lir->canBeDivideByZero()) {
|
|
+ // Must have been divide by zero.
|
|
+ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, lir->bytecodeOffset());
|
|
+ }
|
|
+ } else {
|
|
+ masm.as_divd(output, lhs, rhs);
|
|
+ }
|
|
+
|
|
+ masm.bind(&noOverflow);
|
|
+ if (lir->mir()->isMod()) {
|
|
+// XXX: convert to modsd/modud
|
|
+ // Recover the remainder.
|
|
+ // We don't need to check overflow; we know it can't.
|
|
+ masm.as_mulld(ScratchRegister, output, rhs);
|
|
+ masm.as_subf(output, ScratchRegister, lhs); // T = B - A
|
|
+ }
|
|
+
|
|
+ masm.bind(&done);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitUDivOrModI64(LUDivOrModI64* lir)
|
|
+{
|
|
+ // Simpler, because no -1 check.
|
|
+ ADBlock();
|
|
+ Register lhs = ToRegister(lir->lhs());
|
|
+ Register rhs = ToRegister(lir->rhs());
|
|
+ Register output = ToRegister(lir->output());
|
|
+ LSnapshot* snap = lir->snapshot();
|
|
+ Label noOverflow;
|
|
+
|
|
+ if (snap) {
|
|
+ masm.ma_li(output, 0L);
|
|
+ masm.xs_mtxer(output); // whack XER[SO]
|
|
+ masm.as_divduo_rc(output, lhs, rhs); // T = A / B
|
|
+
|
|
+ // Did we trap?
|
|
+ masm.ma_bc(Assembler::NSOBit, &noOverflow, ShortJump);
|
|
+ // Yes. Only one way that can happen here.
|
|
+ MOZ_ASSERT(lir->canBeDivideByZero());
|
|
+ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, lir->bytecodeOffset());
|
|
+ }
|
|
+ masm.bind(&noOverflow);
|
|
+
|
|
+ if (lir->mir()->isMod()) {
|
|
+ if (HasPPCISA3()) {
|
|
+ masm.as_modud(output, lhs, rhs);
|
|
+ } else {
|
|
+ // Recover the remainder manually.
|
|
+ // We don't need to check overflow; we know it can't.
|
|
+ // Reuse the overflow check result if available.
|
|
+ if (!snap) {
|
|
+ masm.as_divdu(output, lhs, rhs);
|
|
+ }
|
|
+ masm.as_mulld(ScratchRegister, output, rhs);
|
|
+ masm.as_subf(output, ScratchRegister, lhs); // T = B - A
|
|
+ }
|
|
+ } else {
|
|
+ // Reuse the overflow check result if available.
|
|
+ if (!snap) {
|
|
+ masm.as_divdu(output, lhs, rhs);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void CodeGeneratorPPC64::emitBigIntDiv(LBigIntDiv* ins, Register dividend,
|
|
+ Register divisor, Register output,
|
|
+ Label* fail)
|
|
+{
|
|
+ // Callers handle division by zero and integer overflow.
|
|
+
|
|
+ masm.as_divd(/* result= */ dividend, dividend, divisor);
|
|
+
|
|
+ // Create and return the result.
|
|
+ masm.newGCBigInt(output, divisor, initialBigIntHeap(), fail);
|
|
+ masm.initializeBigInt(output, dividend);
|
|
+}
|
|
+
|
|
+void CodeGeneratorPPC64::emitBigIntMod(LBigIntMod* ins, Register dividend,
|
|
+ Register divisor, Register output,
|
|
+ Label* fail)
|
|
+{
|
|
+ // Callers handle division by zero and integer overflow.
|
|
+
|
|
+// XXX: use modsd, etc. on POWER9
|
|
+ // Signed division.
|
|
+ masm.as_divd(output, dividend, divisor);
|
|
+
|
|
+ // Compute the remainder: output = dividend - (output * divisor).
|
|
+ masm.as_mulld(output, output, divisor);
|
|
+ masm.as_subf(dividend, output, dividend);
|
|
+
|
|
+ // Create and return the result.
|
|
+ masm.newGCBigInt(output, divisor, initialBigIntHeap(), fail);
|
|
+ masm.initializeBigInt(output, dividend);
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+void
|
|
+CodeGeneratorPPC64::emitWasmLoadI64(T* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ const MWasmLoad* mir = lir->mir();
|
|
+
|
|
+ Register ptrScratch = InvalidReg;
|
|
+ if(!lir->ptrCopy()->isBogusTemp()){
|
|
+ ptrScratch = ToRegister(lir->ptrCopy());
|
|
+ }
|
|
+
|
|
+ if (IsUnaligned(mir->access())) {
|
|
+ masm.wasmUnalignedLoadI64(mir->access(), HeapReg, ToRegister(lir->ptr()),
|
|
+ ptrScratch, ToOutRegister64(lir), ToRegister(lir->getTemp(1)));
|
|
+ } else {
|
|
+ masm.wasmLoadI64(mir->access(), HeapReg, ToRegister(lir->ptr()), ptrScratch,
|
|
+ ToOutRegister64(lir));
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmLoadI64(LWasmLoadI64* lir)
|
|
+{
|
|
+ emitWasmLoadI64(lir);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmUnalignedLoadI64(LWasmUnalignedLoadI64* lir)
|
|
+{
|
|
+ emitWasmLoadI64(lir);
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+void
|
|
+CodeGeneratorPPC64::emitWasmStoreI64(T* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ const MWasmStore* mir = lir->mir();
|
|
+
|
|
+ Register ptrScratch = InvalidReg;
|
|
+ if(!lir->ptrCopy()->isBogusTemp()){
|
|
+ ptrScratch = ToRegister(lir->ptrCopy());
|
|
+ }
|
|
+
|
|
+ if (IsUnaligned(mir->access())) {
|
|
+ masm.wasmUnalignedStoreI64(mir->access(), ToRegister64(lir->value()), HeapReg,
|
|
+ ToRegister(lir->ptr()), ptrScratch, ToRegister(lir->getTemp(1)));
|
|
+ } else {
|
|
+ masm.wasmStoreI64(mir->access(), ToRegister64(lir->value()), HeapReg,
|
|
+ ToRegister(lir->ptr()), ptrScratch);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmStoreI64(LWasmStoreI64* lir)
|
|
+{
|
|
+ emitWasmStoreI64(lir);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmUnalignedStoreI64(LWasmUnalignedStoreI64* lir)
|
|
+{
|
|
+ emitWasmStoreI64(lir);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmSelectI64(LWasmSelectI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(lir->mir()->type() == MIRType::Int64);
|
|
+
|
|
+ Register cond = ToRegister(lir->condExpr());
|
|
+ const LInt64Allocation falseExpr = lir->falseExpr();
|
|
+
|
|
+ Register64 out = ToOutRegister64(lir);
|
|
+ MOZ_ASSERT(ToRegister64(lir->trueExpr()) == out, "true expr is reused for input");
|
|
+
|
|
+ if (falseExpr.value().isRegister()) {
|
|
+ masm.as_cmpdi(cond, 0);
|
|
+ __asm__("trap\n"); // XXX: fix condition below
|
|
+ masm.as_isel(out.reg, out.reg, ToRegister(falseExpr.value()), 2); // CR0[EQ]
|
|
+ } else {
|
|
+ Label done;
|
|
+ masm.ma_bc(cond, cond, &done, Assembler::NonZero, ShortJump);
|
|
+ masm.loadPtr(ToAddress(falseExpr.value()), out.reg);
|
|
+ masm.bind(&done);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmReinterpretFromI64(LWasmReinterpretFromI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(lir->mir()->type() == MIRType::Double);
|
|
+ MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Int64);
|
|
+
|
|
+ if (HasPPCISA3()) {
|
|
+ masm.as_mtvsrd(ToFloatRegister(lir->output()), ToRegister(lir->input()));
|
|
+ } else {
|
|
+ // Alternative, if we're not assuming POWER8.
|
|
+ masm.as_stdu(ToRegister(lir->input()), StackPointer, -8);
|
|
+ masm.as_lfd(ToFloatRegister(lir->output()), StackPointer, 0);
|
|
+ masm.as_addi(StackPointer, StackPointer, 8);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmReinterpretToI64(LWasmReinterpretToI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(lir->mir()->type() == MIRType::Int64);
|
|
+ MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Double);
|
|
+
|
|
+ if (HasPPCISA3()) {
|
|
+ masm.as_mfvsrd(ToRegister(lir->output()), ToFloatRegister(lir->input()));
|
|
+ } else { // Sigh.
|
|
+ masm.as_stfdu(ToFloatRegister(lir->input()), StackPointer, -8);
|
|
+ masm.as_ld(ToRegister(lir->output()), StackPointer, 0);
|
|
+ masm.as_addi(StackPointer, StackPointer, 8);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitExtendInt32ToInt64(LExtendInt32ToInt64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ const LAllocation* input = lir->getOperand(0);
|
|
+ Register output = ToRegister(lir->output());
|
|
+ Register inpreg = ToRegister(input);
|
|
+
|
|
+ // If the value is unsigned, then we just copy the input
|
|
+ // register; it automatically is promoted to 64 bits.
|
|
+ // If the value is signed, then sign extend it.
|
|
+ if (lir->mir()->isUnsigned()) {
|
|
+ if (inpreg != output)
|
|
+ masm.as_or(output, inpreg, inpreg);
|
|
+ } else
|
|
+ masm.as_extsw(output, inpreg);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWrapInt64ToInt32(LWrapInt64ToInt32* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ const LAllocation* input = lir->getOperand(0);
|
|
+ Register output = ToRegister(lir->output());
|
|
+
|
|
+ if (lir->mir()->bottomHalf()) {
|
|
+ if (input->isMemory())
|
|
+ masm.load32(ToAddress(input), output); // endian!
|
|
+ else
|
|
+ masm.as_srawi(output, ToRegister(input), 0); // sign-extends
|
|
+ } else {
|
|
+ MOZ_CRASH("Not implemented.");
|
|
+ // but it would be rldicl rA,rS,32,32 (don't tell anyone, k?)
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSignExtendInt64(LSignExtendInt64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register64 input = ToRegister64(lir->getInt64Operand(0));
|
|
+ Register64 output = ToOutRegister64(lir);
|
|
+ switch (lir->mode()) {
|
|
+ case MSignExtendInt64::Byte:
|
|
+ masm.move32To64SignExtend(input.reg, output);
|
|
+ masm.move8SignExtend(output.reg, output.reg);
|
|
+ break;
|
|
+ case MSignExtendInt64::Half:
|
|
+ masm.move32To64SignExtend(input.reg, output);
|
|
+ masm.move16SignExtend(output.reg, output.reg);
|
|
+ break;
|
|
+ case MSignExtendInt64::Word:
|
|
+ masm.move32To64SignExtend(input.reg, output);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitClzI64(LClzI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register64 input = ToRegister64(lir->getInt64Operand(0));
|
|
+ Register64 output = ToOutRegister64(lir);
|
|
+ masm.as_cntlzd(output.reg, input.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitCtzI64(LCtzI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register64 input = ToRegister64(lir->getInt64Operand(0));
|
|
+ Register64 output = ToOutRegister64(lir);
|
|
+ // Damn, it's good to be a POWER9 gangsta. When I was a boy,
|
|
+ // we had to count our trailing zeroes on my G5 walking
|
|
+ // uphill in the snow BOTH WAYS. We even have that fancy
|
|
+ // population count instruction now. You kids have it so good.
|
|
+// XXX: POWER9
|
|
+ masm.as_cnttzd(output.reg, input.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitNotI64(LNotI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register64 input = ToRegister64(lir->getInt64Operand(0));
|
|
+ Register output = ToRegister(lir->output());
|
|
+
|
|
+// XXX: isel would be better
|
|
+ masm.ma_cmp_set(output, input.reg, Imm64(0), Assembler::Equal);
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitBitNotI64(LBitNotI64* ins) {
|
|
+ ADBlock();
|
|
+ const LAllocation* input = ins->getOperand(0);
|
|
+ MOZ_ASSERT(!input->isConstant());
|
|
+ Register inputReg = ToRegister(input);
|
|
+ MOZ_ASSERT(inputReg == ToRegister(ins->output())); // MIPS. Do we truly care?
|
|
+ masm.ma_not(inputReg, inputReg);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister input = ToFloatRegister(lir->input());
|
|
+ Register64 output = ToOutRegister64(lir);
|
|
+
|
|
+ MWasmTruncateToInt64* mir = lir->mir();
|
|
+ MIRType fromType = mir->input()->type();
|
|
+
|
|
+ MOZ_ASSERT(fromType == MIRType::Double || fromType == MIRType::Float32);
|
|
+
|
|
+ auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input, output);
|
|
+ addOutOfLineCode(ool, mir);
|
|
+
|
|
+ Label* oolEntry = ool->entry();
|
|
+ Label* oolRejoin = ool->rejoin();
|
|
+ bool isSaturating = mir->isSaturating();
|
|
+
|
|
+ if (fromType == MIRType::Double) {
|
|
+ if (mir->isUnsigned())
|
|
+ masm.wasmTruncateDoubleToUInt64(input, output, isSaturating, oolEntry, oolRejoin,
|
|
+ InvalidFloatReg);
|
|
+ else
|
|
+ masm.wasmTruncateDoubleToInt64(input, output, isSaturating, oolEntry, oolRejoin,
|
|
+ InvalidFloatReg);
|
|
+ } else {
|
|
+ if (mir->isUnsigned())
|
|
+ masm.wasmTruncateFloat32ToUInt64(input, output, isSaturating, oolEntry, oolRejoin,
|
|
+ InvalidFloatReg);
|
|
+ else
|
|
+ masm.wasmTruncateFloat32ToInt64(input, output, isSaturating, oolEntry, oolRejoin,
|
|
+ InvalidFloatReg);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register64 input = ToRegister64(lir->getInt64Operand(0));
|
|
+ FloatRegister output = ToFloatRegister(lir->output());
|
|
+
|
|
+ MIRType outputType = lir->mir()->type();
|
|
+ MOZ_ASSERT(outputType == MIRType::Double || outputType == MIRType::Float32);
|
|
+
|
|
+ if (outputType == MIRType::Double) {
|
|
+ if (lir->mir()->isUnsigned())
|
|
+ masm.convertUInt64ToDouble(input, output, Register::Invalid());
|
|
+ else
|
|
+ masm.convertInt64ToDouble(input, output);
|
|
+ } else {
|
|
+ if (lir->mir()->isUnsigned())
|
|
+ masm.convertUInt64ToFloat32(input, output, Register::Invalid());
|
|
+ else
|
|
+ masm.convertInt64ToFloat32(input, output);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitTestI64AndBranch(LTestI64AndBranch* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register64 input = ToRegister64(lir->getInt64Operand(0));
|
|
+ MBasicBlock* ifTrue = lir->ifTrue();
|
|
+ MBasicBlock* ifFalse = lir->ifFalse();
|
|
+
|
|
+ emitBranch(input.reg, Imm32(0), Assembler::NonZero, ifTrue, ifFalse);
|
|
+}
|
|
+
|
|
+Operand
|
|
+CodeGeneratorPPC64::ToOperand(const LAllocation& a)
|
|
+{
|
|
+ if (a.isGeneralReg()) {
|
|
+ return Operand(a.toGeneralReg()->reg());
|
|
+ }
|
|
+ if (a.isFloatReg()) {
|
|
+ return Operand(a.toFloatReg()->reg());
|
|
+ }
|
|
+ return Operand(ToAddress(a));
|
|
+}
|
|
+
|
|
+Operand
|
|
+CodeGeneratorPPC64::ToOperand(const LAllocation* a)
|
|
+{
|
|
+ return ToOperand(*a);
|
|
+}
|
|
+
|
|
+Operand
|
|
+CodeGeneratorPPC64::ToOperand(const LDefinition* def)
|
|
+{
|
|
+ return ToOperand(def->output());
|
|
+}
|
|
+
|
|
+#ifdef JS_PUNBOX64
|
|
+Operand
|
|
+CodeGeneratorPPC64::ToOperandOrRegister64(const LInt64Allocation input)
|
|
+{
|
|
+ return ToOperand(input.value());
|
|
+}
|
|
+#else
|
|
+Register64
|
|
+CodeGeneratorPPC64::ToOperandOrRegister64(const LInt64Allocation input)
|
|
+{
|
|
+ return ToRegister64(input);
|
|
+}
|
|
+#endif
|
|
+
|
|
+void
|
|
+CodeGeneratorPPC64::branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs,
|
|
+ MBasicBlock* mir, Assembler::DoubleCondition cond)
|
|
+{
|
|
+ // Skip past trivial blocks.
|
|
+ Label* label = skipTrivialBlocks(mir)->lir()->label();
|
|
+ if (fmt == Assembler::DoubleFloat)
|
|
+ masm.branchDouble(cond, lhs, rhs, label);
|
|
+ else
|
|
+ masm.branchFloat(cond, lhs, rhs, label);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitTestIAndBranch(LTestIAndBranch* test)
|
|
+{
|
|
+ ADBlock();
|
|
+ const LAllocation* opd = test->getOperand(0);
|
|
+ MBasicBlock* ifTrue = test->ifTrue();
|
|
+ MBasicBlock* ifFalse = test->ifFalse();
|
|
+
|
|
+ emitBranch(ToRegister(opd), Imm32(0), Assembler::NonZero, ifTrue, ifFalse);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitCompare(LCompare* comp)
|
|
+{
|
|
+ ADBlock();
|
|
+ MCompare* mir = comp->mir();
|
|
+ Assembler::Condition cond = JSOpToCondition(mir->compareType(), comp->jsop());
|
|
+ const LAllocation* left = comp->getOperand(0);
|
|
+ const LAllocation* right = comp->getOperand(1);
|
|
+ const LDefinition* def = comp->getDef(0);
|
|
+
|
|
+ if (mir->compareType() == MCompare::Compare_Object ||
|
|
+ mir->compareType() == MCompare::Compare_Symbol ||
|
|
+ mir->compareType() == MCompare::Compare_UIntPtr) {
|
|
+ if (right->isConstant()) {
|
|
+ MOZ_ASSERT(mir->compareType() == MCompare::Compare_UIntPtr);
|
|
+ // Force 64-bit compare.
|
|
+ masm.ma_cmp_set(ToRegister(def), ToRegister(left), Imm64(ToInt32(right)),
|
|
+ cond, /* useCmpw */ false);
|
|
+ } else if (right->isGeneralReg()) {
|
|
+ masm.cmpPtrSet(cond, ToRegister(left), ToRegister(right),
|
|
+ ToRegister(def));
|
|
+ } else {
|
|
+ masm.cmpPtrSet(cond, ToRegister(left), ToAddress(right), ToRegister(def));
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (right->isConstant()) {
|
|
+ masm.cmp32Set(cond, ToRegister(left), Imm32(ToInt32(right)),
|
|
+ ToRegister(def));
|
|
+ } else if (right->isGeneralReg()) {
|
|
+ masm.cmp32Set(cond, ToRegister(left), ToRegister(right), ToRegister(def));
|
|
+ } else {
|
|
+ masm.cmp32Set(cond, ToRegister(left), ToAddress(right), ToRegister(def));
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitCompareAndBranch(LCompareAndBranch* comp)
|
|
+{
|
|
+ ADBlock();
|
|
+ MCompare* mir = comp->cmpMir();
|
|
+ Assembler::Condition cond = JSOpToCondition(mir->compareType(), comp->jsop());
|
|
+
|
|
+ if (mir->compareType() == MCompare::Compare_Object ||
|
|
+ mir->compareType() == MCompare::Compare_Symbol ||
|
|
+ mir->compareType() == MCompare::Compare_UIntPtr) {
|
|
+ if (comp->right()->isConstant()) {
|
|
+ MOZ_ASSERT(mir->compareType() == MCompare::Compare_UIntPtr);
|
|
+ emitBranch(ToRegister(comp->left()), ImmWord(ToInt32(comp->right())), cond,
|
|
+ comp->ifTrue(), comp->ifFalse());
|
|
+ } else if (comp->right()->isGeneralReg()) {
|
|
+ emitBranch(ToRegister(comp->left()), ToRegister(comp->right()), cond,
|
|
+ comp->ifTrue(), comp->ifFalse());
|
|
+ } else {
|
|
+ masm.loadPtr(ToAddress(comp->right()), ScratchRegister);
|
|
+ emitBranch(ToRegister(comp->left()), ScratchRegister, cond,
|
|
+ comp->ifTrue(), comp->ifFalse());
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (comp->right()->isConstant()) {
|
|
+ emitBranch(ToRegister(comp->left()), Imm32(ToInt32(comp->right())), cond,
|
|
+ comp->ifTrue(), comp->ifFalse());
|
|
+ } else if (comp->right()->isGeneralReg()) {
|
|
+ emitBranch(ToRegister(comp->left()), ToRegister(comp->right()), cond,
|
|
+ comp->ifTrue(), comp->ifFalse());
|
|
+ } else {
|
|
+ masm.load32(ToAddress(comp->right()), ScratchRegister);
|
|
+ emitBranch(ToRegister(comp->left()), ScratchRegister, cond, comp->ifTrue(),
|
|
+ comp->ifFalse());
|
|
+ }
|
|
+
|
|
+}
|
|
+
|
|
+bool
|
|
+CodeGeneratorPPC64::generateOutOfLineCode()
|
|
+{
|
|
+ ADBlock();
|
|
+ if (!CodeGeneratorShared::generateOutOfLineCode())
|
|
+ return false;
|
|
+
|
|
+ if (deoptLabel_.used()) {
|
|
+ // All non-table-based bailouts will go here.
|
|
+ masm.bind(&deoptLabel_);
|
|
+
|
|
+ // Push the frame size, so the handler can recover the IonScript.
|
|
+ // Frame size is stored in LR and pushed by GenerateBailoutThunk.
|
|
+ // We have to use LR because generateBailoutTable will implicitly do
|
|
+ // the same.
|
|
+ masm.ma_li(ScratchRegister, frameSize());
|
|
+ masm.xs_mtlr(ScratchRegister);
|
|
+
|
|
+ TrampolinePtr handler = gen->jitRuntime()->getGenericBailoutHandler();
|
|
+ masm.jump(handler);
|
|
+ }
|
|
+
|
|
+ return !masm.oom();
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGeneratorPPC64::bailoutFrom(Label* label, LSnapshot* snapshot)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ MOZ_ASSERT_IF(!masm.oom(), label->used());
|
|
+ MOZ_ASSERT_IF(!masm.oom(), !label->bound());
|
|
+
|
|
+ encode(snapshot);
|
|
+
|
|
+ InlineScriptTree* tree = snapshot->mir()->block()->trackedTree();
|
|
+ OutOfLineBailout* ool = new(alloc()) OutOfLineBailout(snapshot, masm.framePushed());
|
|
+ addOutOfLineCode(ool, new(alloc()) BytecodeSite(tree, tree->script()->code()));
|
|
+
|
|
+ masm.retarget(label, ool->entry());
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGeneratorPPC64::bailout(LSnapshot* snapshot)
|
|
+{
|
|
+ Label label;
|
|
+ masm.jump(&label);
|
|
+ bailoutFrom(&label, snapshot);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitMinMaxD(LMinMaxD* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister first = ToFloatRegister(ins->first());
|
|
+ FloatRegister second = ToFloatRegister(ins->second());
|
|
+
|
|
+ MOZ_ASSERT(first == ToFloatRegister(ins->output()));
|
|
+
|
|
+ if (ins->mir()->isMax())
|
|
+ masm.maxDouble(second, first, true);
|
|
+ else
|
|
+ masm.minDouble(second, first, true);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitMinMaxF(LMinMaxF* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister first = ToFloatRegister(ins->first());
|
|
+ FloatRegister second = ToFloatRegister(ins->second());
|
|
+
|
|
+ MOZ_ASSERT(first == ToFloatRegister(ins->output()));
|
|
+
|
|
+ if (ins->mir()->isMax())
|
|
+ masm.maxFloat32(second, first, true);
|
|
+ else
|
|
+ masm.minFloat32(second, first, true);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitAddI(LAddI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ const LAllocation* lhs = ins->getOperand(0);
|
|
+ const LAllocation* rhs = ins->getOperand(1);
|
|
+ const LDefinition* dest = ins->getDef(0);
|
|
+
|
|
+ MOZ_ASSERT(rhs->isConstant() || rhs->isGeneralReg());
|
|
+
|
|
+ // If there is no snapshot, we don't need to check for overflow.
|
|
+ if (!ins->snapshot()) {
|
|
+ if (rhs->isConstant())
|
|
+ masm.ma_add(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)));
|
|
+ else
|
|
+ masm.as_add(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Label overflow;
|
|
+ if (rhs->isConstant())
|
|
+ masm.ma_addTestOverflow(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)), &overflow);
|
|
+ else
|
|
+ masm.ma_addTestOverflow(ToRegister(dest), ToRegister(lhs), ToRegister(rhs), &overflow);
|
|
+
|
|
+ bailoutFrom(&overflow, ins->snapshot());
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitAddI64(LAddI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ const LInt64Allocation lhs = lir->getInt64Operand(LAddI64::Lhs);
|
|
+ const LInt64Allocation rhs = lir->getInt64Operand(LAddI64::Rhs);
|
|
+
|
|
+ MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs));
|
|
+
|
|
+ if (IsConstant(rhs)) {
|
|
+ masm.add64(Imm64(ToInt64(rhs)), ToRegister64(lhs));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ masm.add64(ToOperandOrRegister64(rhs), ToRegister64(lhs));
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSubI(LSubI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ const LAllocation* lhs = ins->getOperand(0);
|
|
+ const LAllocation* rhs = ins->getOperand(1);
|
|
+ const LDefinition* dest = ins->getDef(0);
|
|
+
|
|
+ MOZ_ASSERT(rhs->isConstant() || rhs->isGeneralReg());
|
|
+
|
|
+ // If there is no snapshot, we don't need to check for overflow.
|
|
+ if (!ins->snapshot()) {
|
|
+ if (rhs->isConstant())
|
|
+ masm.ma_subu(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)));
|
|
+ else
|
|
+ masm.as_subf(ToRegister(dest), ToRegister(rhs), ToRegister(lhs)); // T = B - A
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Label overflow;
|
|
+ if (rhs->isConstant())
|
|
+ masm.ma_subTestOverflow(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)), &overflow);
|
|
+ else
|
|
+ masm.ma_subTestOverflow(ToRegister(dest), ToRegister(lhs), ToRegister(rhs), &overflow);
|
|
+
|
|
+ bailoutFrom(&overflow, ins->snapshot());
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSubI64(LSubI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ const LInt64Allocation lhs = lir->getInt64Operand(LSubI64::Lhs);
|
|
+ const LInt64Allocation rhs = lir->getInt64Operand(LSubI64::Rhs);
|
|
+
|
|
+ MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs));
|
|
+
|
|
+ if (IsConstant(rhs)) {
|
|
+ masm.sub64(Imm64(ToInt64(rhs)), ToRegister64(lhs));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ masm.sub64(ToOperandOrRegister64(rhs), ToRegister64(lhs));
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitMulI(LMulI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ const LAllocation* lhs = ins->lhs();
|
|
+ const LAllocation* rhs = ins->rhs();
|
|
+ Register dest = ToRegister(ins->output());
|
|
+ MMul* mul = ins->mir();
|
|
+
|
|
+ MOZ_ASSERT_IF(mul->mode() == MMul::Integer, !mul->canBeNegativeZero() && !mul->canOverflow());
|
|
+
|
|
+ if (rhs->isConstant()) {
|
|
+ int32_t constant = ToInt32(rhs);
|
|
+ Register src = ToRegister(lhs);
|
|
+
|
|
+ // Bailout on -0.0
|
|
+ if (mul->canBeNegativeZero() && constant <= 0) {
|
|
+ Assembler::Condition cond = (constant == 0) ? Assembler::LessThan : Assembler::Equal;
|
|
+ bailoutCmp32(cond, src, Imm32(0), ins->snapshot());
|
|
+ }
|
|
+
|
|
+ switch (constant) {
|
|
+ case -1:
|
|
+ if (mul->canOverflow())
|
|
+ bailoutCmp32(Assembler::Equal, src, Imm32(INT32_MIN), ins->snapshot());
|
|
+
|
|
+ masm.as_neg(dest, src);
|
|
+ break;
|
|
+ case 0:
|
|
+ masm.move32(Imm32(0), dest);
|
|
+ break;
|
|
+ case 1:
|
|
+ masm.move32(src, dest);
|
|
+ break;
|
|
+ case 2:
|
|
+ if (mul->canOverflow()) {
|
|
+ Label mulTwoOverflow;
|
|
+ masm.ma_addTestOverflow(dest, src, src, &mulTwoOverflow);
|
|
+
|
|
+ bailoutFrom(&mulTwoOverflow, ins->snapshot());
|
|
+ } else {
|
|
+ masm.as_add(dest, src, src);
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ uint32_t shift = FloorLog2(constant);
|
|
+
|
|
+ if (!mul->canOverflow() && (constant > 0)) {
|
|
+ // If it cannot overflow, we can do lots of optimizations.
|
|
+ uint32_t rest = constant - (1 << shift);
|
|
+
|
|
+ // See if the constant has one bit set, meaning it can be
|
|
+ // encoded as a bitshift.
|
|
+ if ((1 << shift) == constant) {
|
|
+ MOZ_ASSERT(shift < 32);
|
|
+ masm.x_slwi(dest, src, shift);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // If the constant cannot be encoded as (1<<C1), see if it can
|
|
+ // be encoded as (1<<C1) | (1<<C2), which can be computed
|
|
+ // using an add and a shift.
|
|
+ uint32_t shift_rest = FloorLog2(rest);
|
|
+ if (src != dest && (1u << shift_rest) == rest) {
|
|
+ masm.x_slwi(dest, src, shift - shift_rest);
|
|
+ masm.as_add(dest, dest, src);
|
|
+ if (shift_rest != 0)
|
|
+ masm.x_slwi(dest, dest, shift_rest);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (mul->canOverflow() && (constant > 0) && (src != dest)) {
|
|
+ // To stay on the safe side, only optimize things that are a
|
|
+ // power of 2.
|
|
+
|
|
+ if ((1 << shift) == constant) {
|
|
+ // dest = lhs * pow(2, shift)
|
|
+ MOZ_ASSERT(shift < 32);
|
|
+ masm.x_slwi(dest, src, shift);
|
|
+ // At runtime, check (lhs == dest >> shift). If this does
|
|
+ // not hold, some bits were lost due to overflow, and the
|
|
+ // computation should be resumed as a double.
|
|
+ masm.as_srawi(ScratchRegister, dest, shift);
|
|
+ bailoutCmp32(Assembler::NotEqual, src, ScratchRegister, ins->snapshot());
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (mul->canOverflow()) {
|
|
+ Label mulConstOverflow;
|
|
+ masm.ma_mul_branch_overflow(dest, ToRegister(lhs), Imm32(ToInt32(rhs)),
|
|
+ &mulConstOverflow);
|
|
+
|
|
+ bailoutFrom(&mulConstOverflow, ins->snapshot());
|
|
+ } else {
|
|
+ masm.ma_mul(dest, src, Imm32(ToInt32(rhs)));
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ Label multRegOverflow;
|
|
+
|
|
+ if (mul->canOverflow()) {
|
|
+ masm.ma_mul_branch_overflow(dest, ToRegister(lhs), ToRegister(rhs), &multRegOverflow);
|
|
+ bailoutFrom(&multRegOverflow, ins->snapshot());
|
|
+ } else {
|
|
+ masm.as_mullw(dest, ToRegister(lhs), ToRegister(rhs));
|
|
+ }
|
|
+
|
|
+ if (mul->canBeNegativeZero()) {
|
|
+ Label done;
|
|
+ masm.ma_bc(dest, dest, &done, Assembler::NonZero, ShortJump);
|
|
+
|
|
+ // Result is -0 if lhs or rhs is negative.
|
|
+ // In that case result must be double value so bailout
|
|
+ Register scratch = SecondScratchReg;
|
|
+ masm.as_or(scratch, ToRegister(lhs), ToRegister(rhs));
|
|
+ bailoutCmp32(Assembler::Signed, scratch, scratch, ins->snapshot());
|
|
+
|
|
+ masm.bind(&done);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitMulI64(LMulI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ const LInt64Allocation lhs = lir->getInt64Operand(LMulI64::Lhs);
|
|
+ const LInt64Allocation rhs = lir->getInt64Operand(LMulI64::Rhs);
|
|
+ const Register64 output = ToOutRegister64(lir);
|
|
+
|
|
+ if (IsConstant(rhs)) {
|
|
+ int64_t constant = ToInt64(rhs);
|
|
+ switch (constant) {
|
|
+ case -1:
|
|
+ masm.neg64(ToRegister64(lhs));
|
|
+ return;
|
|
+ case 0:
|
|
+ masm.xor64(ToRegister64(lhs), ToRegister64(lhs));
|
|
+ return;
|
|
+ case 1:
|
|
+ // nop
|
|
+ return;
|
|
+ default:
|
|
+ if (constant > 0) {
|
|
+ if (mozilla::IsPowerOfTwo(static_cast<uint32_t>(constant + 1))) {
|
|
+ masm.move64(ToRegister64(lhs), output);
|
|
+ masm.lshift64(Imm32(FloorLog2(constant + 1)), output);
|
|
+ masm.sub64(ToRegister64(lhs), output);
|
|
+ return;
|
|
+ } else if (mozilla::IsPowerOfTwo(static_cast<uint32_t>(constant - 1))) {
|
|
+ masm.move64(ToRegister64(lhs), output);
|
|
+ masm.lshift64(Imm32(FloorLog2(constant - 1u)), output);
|
|
+ masm.add64(ToRegister64(lhs), output);
|
|
+ return;
|
|
+ }
|
|
+ // Use shift if constant is power of 2.
|
|
+ int32_t shift = mozilla::FloorLog2(constant);
|
|
+ if (int64_t(1) << shift == constant) {
|
|
+ masm.lshift64(Imm32(shift), ToRegister64(lhs));
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ Register temp = ToTempRegisterOrInvalid(lir->temp());
|
|
+ masm.mul64(Imm64(constant), ToRegister64(lhs), temp);
|
|
+ }
|
|
+ } else {
|
|
+ Register temp = ToTempRegisterOrInvalid(lir->temp());
|
|
+ masm.mul64(ToOperandOrRegister64(rhs), ToRegister64(lhs), temp);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitDivI(LDivI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ // divwo will automatically set the Overflow bit if INT32_MIN/-1 is
|
|
+ // performed, or if we divide by zero, so we just bailout based on that.
|
|
+ // However, it does not trigger an exception for a negative zero result,
|
|
+ // so we must check that first.
|
|
+
|
|
+ // Extract the registers from this instruction. We don't use a temp.
|
|
+ Register lhs = ToRegister(ins->lhs());
|
|
+ Register rhs = ToRegister(ins->rhs());
|
|
+ Register dest = ToRegister(ins->output());
|
|
+ MDiv* mir = ins->mir();
|
|
+
|
|
+ Label done, noOverflow;
|
|
+
|
|
+ // Handle negative 0. (0/-Y)
|
|
+ if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) {
|
|
+ Label nonzero;
|
|
+
|
|
+ MOZ_ASSERT(ins->snapshot());
|
|
+ masm.ma_bc(lhs, lhs, &nonzero, Assembler::NonZero, ShortJump);
|
|
+ bailoutCmp32(Assembler::LessThan, rhs, Imm32(0), ins->snapshot());
|
|
+ masm.bind(&nonzero);
|
|
+ }
|
|
+
|
|
+ // Not negative zero. So: DIVIDE! AND! CONQUER!
|
|
+ if (mir->canBeNegativeOverflow() || mir->canBeDivideByZero()) {
|
|
+ // If Overflow is set, we must bail out. But only check this if Ion
|
|
+ // doesn't already know this operation is infallible (we can emit
|
|
+ // a lot fewer instructions).
|
|
+
|
|
+ masm.ma_li(dest, 0L);
|
|
+ masm.xs_mtxer(dest); // whack XER[SO]
|
|
+ masm.as_divwo_rc(dest, lhs, rhs); // T = A / B
|
|
+
|
|
+ // Did we trap?
|
|
+ masm.ma_bc(Assembler::NSOBit, &noOverflow, ShortJump);
|
|
+ // Yes. Determine how.
|
|
+ if (mir->canBeNegativeOverflow()) {
|
|
+ Label notNegOne;
|
|
+
|
|
+ if (mir->canBeDivideByZero()) {
|
|
+ // If rhs is -1, then we overflowed by dividing INT32_MIN;
|
|
+ // otherwise, it must be divide by zero.
|
|
+ masm.as_cmpdi(rhs, -1);
|
|
+ masm.ma_bc(Assembler::NotEqual, ¬NegOne, ShortJump);
|
|
+ }
|
|
+
|
|
+ if (mir->trapOnError()) {
|
|
+ masm.wasmTrap(wasm::Trap::IntegerOverflow, mir->bytecodeOffset());
|
|
+ } else if (mir->canTruncateOverflow()) {
|
|
+ // (-INT32_MIN)|0 == INT32_MIN
|
|
+ masm.move32(Imm32(INT32_MIN), dest);
|
|
+ masm.ma_b(&done, ShortJump);
|
|
+ } else {
|
|
+ MOZ_ASSERT(mir->fallible());
|
|
+ bailout(ins->snapshot());
|
|
+ }
|
|
+
|
|
+ masm.bind(¬NegOne);
|
|
+ }
|
|
+ if (mir->canBeDivideByZero()) {
|
|
+ // Must have been divide by zero.
|
|
+ if (mir->trapOnError()) {
|
|
+ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->bytecodeOffset());
|
|
+ } else if (mir->canTruncateInfinities()) {
|
|
+ // If we can truncate division, then a truncated division by
|
|
+ // zero is always zero, and not actually an exception.
|
|
+ masm.move32(Imm32(0), dest);
|
|
+ masm.ma_b(&done, ShortJump);
|
|
+ } else {
|
|
+ MOZ_ASSERT(mir->fallible());
|
|
+ bailout(ins->snapshot());
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ masm.as_divw(dest, lhs, rhs);
|
|
+ }
|
|
+ masm.bind(&noOverflow);
|
|
+
|
|
+ if (!mir->canTruncateRemainder()) {
|
|
+ MOZ_ASSERT(mir->fallible());
|
|
+
|
|
+ // Recover the remainder to see if we need to bailout.
|
|
+ if (HasPPCISA3()) {
|
|
+ masm.as_modsw(SecondScratchReg, lhs, rhs);
|
|
+ } else {
|
|
+ // Do it the old way.
|
|
+ // We don't need to check overflow with mullw; we know it can't.
|
|
+ masm.as_mullw(ScratchRegister, dest, rhs);
|
|
+ masm.as_subf(SecondScratchReg, ScratchRegister, lhs); // T = B - A
|
|
+ }
|
|
+ bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), ins->snapshot());
|
|
+ }
|
|
+
|
|
+ masm.bind(&done);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitDivPowTwoI(LDivPowTwoI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register lhs = ToRegister(ins->numerator());
|
|
+ Register dest = ToRegister(ins->output());
|
|
+ Register tmp = ToRegister(ins->getTemp(0));
|
|
+ int32_t shift = ins->shift();
|
|
+
|
|
+ if (shift != 0) {
|
|
+ MOZ_ASSERT(shift < 32);
|
|
+ MDiv* mir = ins->mir();
|
|
+ if (!mir->isTruncated()) {
|
|
+ // If the remainder is going to be != 0, bailout since this must
|
|
+ // be a double.
|
|
+ masm.x_slwi(tmp, lhs, 32 - shift);
|
|
+ bailoutCmp32(Assembler::NonZero, tmp, tmp, ins->snapshot());
|
|
+ }
|
|
+
|
|
+ if (!mir->canBeNegativeDividend()) {
|
|
+ // Numerator is unsigned, so needs no adjusting. Do the shift.
|
|
+ masm.as_srawi(dest, lhs, shift);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Adjust the value so that shifting produces a correctly rounded result
|
|
+ // when the numerator is negative. See 10-1 "Signed Division by a Known
|
|
+ // Power of 2" in Henry S. Warren, Jr.'s Hacker's Delight.
|
|
+ if (shift > 1) {
|
|
+ masm.as_srawi(tmp, lhs, 31);
|
|
+ masm.x_srwi(tmp, tmp, 32 - shift);
|
|
+ masm.add32(lhs, tmp);
|
|
+ } else {
|
|
+ MOZ_ASSERT(shift == 1);
|
|
+ masm.x_srwi(tmp, lhs, 31);
|
|
+ masm.add32(lhs, tmp);
|
|
+ }
|
|
+
|
|
+ // Do the shift.
|
|
+ masm.as_srawi(dest, tmp, shift);
|
|
+ } else {
|
|
+ masm.move32(lhs, dest);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitModI(LModI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ // Extract the registers from this instruction (callTemp not currently
|
|
+ // used for POWER9, but may be a useful temp register for POWER8-).
|
|
+ Register lhs = ToRegister(ins->lhs());
|
|
+ Register rhs = ToRegister(ins->rhs());
|
|
+ Register dest = ToRegister(ins->output());
|
|
+ MMod* mir = ins->mir();
|
|
+ Label done, prevent;
|
|
+
|
|
+ // If computing 0/x where x < 0, modsw won't raise an exception but
|
|
+ // we have to return -0.0 (which requires a bailout). Since we have to
|
|
+ // probe the y/0 case necessarily, let's just check for all of them.
|
|
+ if (mir->canBeDivideByZero()) {
|
|
+ if (mir->isTruncated()) {
|
|
+ if (mir->trapOnError()) {
|
|
+ Label nonZero;
|
|
+ masm.ma_bc(rhs, rhs, &nonZero, Assembler::NonZero, ShortJump);
|
|
+ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->bytecodeOffset());
|
|
+ masm.bind(&nonZero);
|
|
+ } else {
|
|
+ Label skip;
|
|
+ masm.ma_bc(rhs, Imm32(0), &skip, Assembler::NotEqual, ShortJump);
|
|
+ masm.move32(Imm32(0), dest);
|
|
+ masm.ma_b(&done, ShortJump);
|
|
+ masm.bind(&skip);
|
|
+ }
|
|
+ } else {
|
|
+ MOZ_ASSERT(mir->fallible());
|
|
+ bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot());
|
|
+ }
|
|
+ }
|
|
+
|
|
+#if(0)
|
|
+ if (mir->canBeNegativeDividend()) {
|
|
+ Label dividendOk;
|
|
+ masm.ma_bc(rhs, Imm32(0), ÷ndOk, Assembler::GreaterThan, ShortJump);
|
|
+
|
|
+ if (mir->isTruncated()) {
|
|
+ // NaN|0 == 0 and (0 % -X)|0 == 0
|
|
+ Label skip;
|
|
+ masm.ma_bc(lhs, Imm32(0), &skip, Assembler::NotEqual, ShortJump);
|
|
+ masm.move32(Imm32(0), dest);
|
|
+ masm.ma_b(&done, ShortJump);
|
|
+ masm.bind(&skip);
|
|
+ } else {
|
|
+ MOZ_ASSERT(mir->fallible());
|
|
+ bailoutCmp32(Assembler::Equal, lhs, Imm32(0), ins->snapshot());
|
|
+ }
|
|
+
|
|
+ // Now we need to do a test division. If divwo. flags overflow,
|
|
+ // then we must be trying to do INT_MIN % -1. modsw can't detect
|
|
+ // this either.
|
|
+ masm.ma_li(dest, 0L);
|
|
+ masm.xs_mtxer(dest); // whack XER[SO]
|
|
+ masm.as_divwo_rc(dest, lhs, rhs);
|
|
+ masm.ma_bc(Assembler::NSOBit, ÷ndOk, ShortJump);
|
|
+
|
|
+ // Overflowed.
|
|
+ if (mir->isTruncated()) {
|
|
+ // (INT_MIN % -1)|0 == 0
|
|
+ Label skip;
|
|
+ masm.ma_bc(rhs, Imm32(-1), &skip, Assembler::NotEqual, ShortJump);
|
|
+ masm.move32(Imm32(0), dest);
|
|
+ masm.ma_b(&done, ShortJump);
|
|
+ masm.bind(&skip);
|
|
+ } else {
|
|
+ MOZ_ASSERT(mir->fallible());
|
|
+ bailoutCmp32(Assembler::Equal, rhs, Imm32(-1), ins->snapshot());
|
|
+ }
|
|
+
|
|
+ // Dividend wasn't negative, or not INT_MIN % -1.
|
|
+ masm.bind(÷ndOk);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ // Test division functioned (or statically known not to fail), so now
|
|
+ // we can safely compute the modulo. The definition is consistent with
|
|
+ // modsw on POWER9.
|
|
+ if (HasPPCISA3()) {
|
|
+ masm.as_modsw(dest, lhs, rhs);
|
|
+ } else {
|
|
+ // Compute remainder manually. We can use the already computed
|
|
+ // quotient in |dest| if we know we computed it, or do a divw here
|
|
+ // since we have already tested for all the needed failure cases.
|
|
+ MOZ_CRASH("implement for POWER8 and earlier");
|
|
+ }
|
|
+
|
|
+ // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0
|
|
+ if (mir->canBeNegativeDividend()) {
|
|
+ if (mir->isTruncated()) {
|
|
+ // -0.0|0 == 0
|
|
+ } else {
|
|
+ MOZ_ASSERT(mir->fallible());
|
|
+ // See if X < 0
|
|
+ masm.ma_bc(dest, Imm32(0), &done, Assembler::NotEqual, ShortJump);
|
|
+ bailoutCmp32(Assembler::LessThan, lhs, Imm32(0), ins->snapshot());
|
|
+ }
|
|
+ }
|
|
+ masm.bind(&done);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitModPowTwoI(LModPowTwoI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ Register in = ToRegister(ins->getOperand(0));
|
|
+ Register out = ToRegister(ins->getDef(0));
|
|
+ MMod* mir = ins->mir();
|
|
+ Label negative, done;
|
|
+
|
|
+ masm.move32(in, out);
|
|
+ masm.ma_bc(in, in, &done, Assembler::Zero, ShortJump);
|
|
+ // Switch based on sign of the lhs.
|
|
+ // Positive numbers are just a bitmask
|
|
+ masm.ma_bc(in, in, &negative, Assembler::Signed, ShortJump);
|
|
+ {
|
|
+ masm.and32(Imm32((1 << ins->shift()) - 1), out);
|
|
+ masm.ma_b(&done, ShortJump);
|
|
+ }
|
|
+
|
|
+ // Negative numbers need a negate, bitmask, negate
|
|
+ {
|
|
+ masm.bind(&negative);
|
|
+ masm.neg32(out);
|
|
+ masm.and32(Imm32((1 << ins->shift()) - 1), out);
|
|
+ masm.neg32(out);
|
|
+ }
|
|
+ if (mir->canBeNegativeDividend()) {
|
|
+ if (!mir->isTruncated()) {
|
|
+ MOZ_ASSERT(mir->fallible());
|
|
+ bailoutCmp32(Assembler::Equal, out, Imm32(0L), ins->snapshot());
|
|
+ } else {
|
|
+ // -0|0 == 0
|
|
+ }
|
|
+ }
|
|
+ masm.bind(&done);
|
|
+}
|
|
+
|
|
+// This isn't used on ISA 3.0+ because we have hardware modulo, but it's
|
|
+// here for future expansion on older chips.
|
|
+void
|
|
+CodeGenerator::visitModMaskI(LModMaskI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(!HasPPCISA3());
|
|
+ // We wish to compute x % (1<<y) - 1 for a known constant, y.
|
|
+
|
|
+ //Register src = ToRegister(ins->getOperand(0));
|
|
+ //Register dest = ToRegister(ins->getDef(0));
|
|
+ //Register tmp0 = ToRegister(ins->getTemp(0));
|
|
+ //Register tmp1 = ToRegister(ins->getTemp(1));
|
|
+ MMod* mir = ins->mir();
|
|
+
|
|
+ if (!mir->isTruncated() && mir->canBeNegativeDividend()) {
|
|
+ // Sorry, POWER8, you'll need to do like MIPS.
|
|
+ MOZ_CRASH("implement visitModMaskI for POWER8-");
|
|
+ } else {
|
|
+ MOZ_CRASH("implement visitModMaskI for POWER8-");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitBitNotI(LBitNotI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ const LAllocation* input = ins->getOperand(0);
|
|
+ const LDefinition* dest = ins->getDef(0);
|
|
+ MOZ_ASSERT(!input->isConstant());
|
|
+
|
|
+ masm.x_not(ToRegister(dest), ToRegister(input));
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitBitOpI(LBitOpI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ const LAllocation* lhs = ins->getOperand(0);
|
|
+ const LAllocation* rhs = ins->getOperand(1);
|
|
+ const LDefinition* dest = ins->getDef(0);
|
|
+ // all of these bitops should be either imm32's, or integer registers.
|
|
+ switch (ins->bitop()) {
|
|
+ case JSOp::BitOr:
|
|
+ if (rhs->isConstant())
|
|
+ masm.ma_or(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)));
|
|
+ else
|
|
+ masm.as_or(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
|
|
+ break;
|
|
+ case JSOp::BitXor:
|
|
+ if (rhs->isConstant())
|
|
+ masm.ma_xor(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)));
|
|
+ else
|
|
+ masm.as_xor(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
|
|
+ break;
|
|
+ case JSOp::BitAnd:
|
|
+ if (rhs->isConstant())
|
|
+ masm.ma_and(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)));
|
|
+ else
|
|
+ masm.as_and(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("unexpected binary opcode");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitBitOpI64(LBitOpI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ const LInt64Allocation lhs = lir->getInt64Operand(LBitOpI64::Lhs);
|
|
+ const LInt64Allocation rhs = lir->getInt64Operand(LBitOpI64::Rhs);
|
|
+
|
|
+ MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs));
|
|
+
|
|
+ switch (lir->bitop()) {
|
|
+ case JSOp::BitOr:
|
|
+ if (IsConstant(rhs))
|
|
+ masm.or64(Imm64(ToInt64(rhs)), ToRegister64(lhs));
|
|
+ else
|
|
+ masm.or64(ToOperandOrRegister64(rhs), ToRegister64(lhs));
|
|
+ break;
|
|
+ case JSOp::BitXor:
|
|
+ if (IsConstant(rhs))
|
|
+ masm.xor64(Imm64(ToInt64(rhs)), ToRegister64(lhs));
|
|
+ else
|
|
+ masm.xor64(ToOperandOrRegister64(rhs), ToRegister64(lhs));
|
|
+ break;
|
|
+ case JSOp::BitAnd:
|
|
+ if (IsConstant(rhs))
|
|
+ masm.and64(Imm64(ToInt64(rhs)), ToRegister64(lhs));
|
|
+ else
|
|
+ masm.and64(ToOperandOrRegister64(rhs), ToRegister64(lhs));
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("unexpected binary opcode");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitShiftI(LShiftI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register lhs = ToRegister(ins->lhs());
|
|
+ const LAllocation* rhs = ins->rhs();
|
|
+ Register dest = ToRegister(ins->output());
|
|
+
|
|
+ if (rhs->isConstant()) {
|
|
+ int32_t shift = ToInt32(rhs) & 0x1F;
|
|
+ switch (ins->bitop()) {
|
|
+ case JSOp::Lsh:
|
|
+ if (shift)
|
|
+ masm.x_slwi(dest, lhs, shift);
|
|
+ else
|
|
+ masm.move32(lhs, dest);
|
|
+ break;
|
|
+ case JSOp::Rsh:
|
|
+ if (shift)
|
|
+ masm.as_srawi(dest, lhs, shift);
|
|
+ else
|
|
+ masm.move32(lhs, dest);
|
|
+ break;
|
|
+ case JSOp::Ursh:
|
|
+ if (shift) {
|
|
+ masm.x_srwi(dest, lhs, shift);
|
|
+ } else {
|
|
+ // x >>> 0 can overflow.
|
|
+ if (ins->mir()->toUrsh()->fallible()) {
|
|
+ bailoutCmp32(Assembler::LessThan, lhs, Imm32(0), ins->snapshot());
|
|
+ }
|
|
+ masm.move32(lhs, dest);
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("Unexpected shift op");
|
|
+ }
|
|
+ } else {
|
|
+ // The shift amounts should be AND'ed into the 0-31 range.
|
|
+ masm.as_andi_rc(dest, ToRegister(rhs), 0x1f);
|
|
+
|
|
+ switch (ins->bitop()) {
|
|
+ case JSOp::Lsh:
|
|
+ masm.as_slw(dest, lhs, dest);
|
|
+ break;
|
|
+ case JSOp::Rsh:
|
|
+ masm.as_sraw(dest, lhs, dest);
|
|
+ break;
|
|
+ case JSOp::Ursh:
|
|
+ masm.as_srw(dest, lhs, dest);
|
|
+ if (ins->mir()->toUrsh()->fallible()) {
|
|
+ // x >>> 0 can overflow.
|
|
+ bailoutCmp32(Assembler::LessThan, dest, Imm32(0), ins->snapshot());
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("Unexpected shift op");
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitShiftI64(LShiftI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ const LInt64Allocation lhs = lir->getInt64Operand(LShiftI64::Lhs);
|
|
+ LAllocation* rhs = lir->getOperand(LShiftI64::Rhs);
|
|
+
|
|
+ MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs));
|
|
+
|
|
+ if (rhs->isConstant()) {
|
|
+ int32_t shift = int32_t(rhs->toConstant()->toInt64() & 0x3F);
|
|
+ switch (lir->bitop()) {
|
|
+ case JSOp::Lsh:
|
|
+ if (shift)
|
|
+ masm.lshift64(Imm32(shift), ToRegister64(lhs));
|
|
+ break;
|
|
+ case JSOp::Rsh:
|
|
+ if (shift)
|
|
+ masm.rshift64Arithmetic(Imm32(shift), ToRegister64(lhs));
|
|
+ break;
|
|
+ case JSOp::Ursh:
|
|
+ if (shift)
|
|
+ masm.rshift64(Imm32(shift), ToRegister64(lhs));
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("Unexpected shift op");
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ switch (lir->bitop()) {
|
|
+ case JSOp::Lsh:
|
|
+ masm.lshift64(ToRegister(rhs), ToRegister64(lhs));
|
|
+ break;
|
|
+ case JSOp::Rsh:
|
|
+ masm.rshift64Arithmetic(ToRegister(rhs), ToRegister64(lhs));
|
|
+ break;
|
|
+ case JSOp::Ursh:
|
|
+ masm.rshift64(ToRegister(rhs), ToRegister64(lhs));
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("Unexpected shift op");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitRotateI64(LRotateI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ MRotate* mir = lir->mir();
|
|
+ LAllocation* count = lir->count();
|
|
+
|
|
+ Register64 input = ToRegister64(lir->input());
|
|
+ Register64 output = ToOutRegister64(lir);
|
|
+ Register temp = ToTempRegisterOrInvalid(lir->temp());
|
|
+
|
|
+ MOZ_ASSERT(input == output);
|
|
+
|
|
+ if (count->isConstant()) {
|
|
+ int32_t c = int32_t(count->toConstant()->toInt64() & 0x3F);
|
|
+ if (!c) {
|
|
+ masm.move64(input, output);
|
|
+ return;
|
|
+ }
|
|
+ if (mir->isLeftRotate())
|
|
+ masm.rotateLeft64(Imm32(c), input, output, temp);
|
|
+ else
|
|
+ masm.rotateRight64(Imm32(c), input, output, temp);
|
|
+ } else {
|
|
+ if (mir->isLeftRotate())
|
|
+ masm.rotateLeft64(ToRegister(count), input, output, temp);
|
|
+ else
|
|
+ masm.rotateRight64(ToRegister(count), input, output, temp);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitUrshD(LUrshD* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register lhs = ToRegister(ins->lhs());
|
|
+ Register temp = ToRegister(ins->temp());
|
|
+
|
|
+ const LAllocation* rhs = ins->rhs();
|
|
+ FloatRegister out = ToFloatRegister(ins->output());
|
|
+
|
|
+ if (rhs->isConstant()) {
|
|
+ // See MacroAssembler::rshift32 for constraints.
|
|
+ int32_t rrhs = ToInt32(rhs);
|
|
+ MOZ_ASSERT(rrhs >= 0);
|
|
+
|
|
+ if (!(rrhs % 32)) {
|
|
+ // Effective no-op. Drop the mic.
|
|
+ masm.convertUInt32ToDouble(lhs, out);
|
|
+ return;
|
|
+ } else {
|
|
+ masm.x_srwi(temp, lhs, (rrhs & 31));
|
|
+ }
|
|
+ } else {
|
|
+ masm.as_srw(temp, lhs, ToRegister(rhs));
|
|
+ }
|
|
+
|
|
+ masm.convertUInt32ToDouble(temp, out);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitClzI(LClzI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register input = ToRegister(ins->input());
|
|
+ Register output = ToRegister(ins->output());
|
|
+
|
|
+ masm.as_cntlzw(output, input);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitCtzI(LCtzI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register input = ToRegister(ins->input());
|
|
+ Register output = ToRegister(ins->output());
|
|
+
|
|
+ if (HasPPCISA3()) {
|
|
+ masm.as_cnttzw(output, input);
|
|
+ } else {
|
|
+ MOZ_CRASH("cnttzw");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitPopcntI(LPopcntI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register input = ToRegister(ins->input());
|
|
+ Register output = ToRegister(ins->output());
|
|
+
|
|
+ masm.as_popcntw(output, input);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitPopcntI64(LPopcntI64* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register64 input = ToRegister64(ins->getInt64Operand(0));
|
|
+ Register64 output = ToOutRegister64(ins);
|
|
+
|
|
+ masm.as_popcntd(output.reg, input.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitPowHalfD(LPowHalfD* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister input = ToFloatRegister(ins->input());
|
|
+ FloatRegister output = ToFloatRegister(ins->output());
|
|
+
|
|
+ Label done, skip;
|
|
+
|
|
+ // Masm.pow(-Infinity, 0.5) == Infinity.
|
|
+ masm.loadConstantDouble(NegativeInfinity<double>(), ScratchDoubleReg);
|
|
+ masm.as_fcmpu(input, ScratchDoubleReg);
|
|
+ masm.ma_bc(Assembler::DoubleNotEqualOrUnordered, &skip, ShortJump);
|
|
+ masm.as_fneg(output, ScratchDoubleReg);
|
|
+ masm.ma_b(&done, ShortJump);
|
|
+
|
|
+ masm.bind(&skip);
|
|
+ // Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5).
|
|
+ // Adding 0 converts any -0 to 0.
|
|
+ masm.zeroDouble(ScratchDoubleReg);
|
|
+ masm.as_fadd(output, input, ScratchDoubleReg);
|
|
+ masm.as_fsqrt(output, output);
|
|
+
|
|
+ masm.bind(&done);
|
|
+}
|
|
+
|
|
+MoveOperand
|
|
+CodeGeneratorPPC64::toMoveOperand(LAllocation a) const
|
|
+{
|
|
+ if (a.isGeneralReg())
|
|
+ return MoveOperand(ToRegister(a));
|
|
+ if (a.isFloatReg()) {
|
|
+ return MoveOperand(ToFloatRegister(a));
|
|
+ }
|
|
+ MoveOperand::Kind kind =
|
|
+ a.isStackArea() ? MoveOperand::Kind::EffectiveAddress : MoveOperand::Kind::Memory;
|
|
+ Address address = ToAddress(a);
|
|
+ MOZ_ASSERT((address.offset & 3) == 0);
|
|
+ return MoveOperand(address, kind);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitMathD(LMathD* math)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister src1 = ToFloatRegister(math->getOperand(0));
|
|
+ FloatRegister src2 = ToFloatRegister(math->getOperand(1));
|
|
+ FloatRegister output = ToFloatRegister(math->getDef(0));
|
|
+
|
|
+ switch (math->jsop()) {
|
|
+ case JSOp::Add:
|
|
+ masm.as_fadd(output, src1, src2);
|
|
+ break;
|
|
+ case JSOp::Sub:
|
|
+ masm.as_fsub(output, src1, src2); // T = A - B
|
|
+ break;
|
|
+ case JSOp::Mul:
|
|
+ masm.as_fmul(output, src1, src2);
|
|
+ break;
|
|
+ case JSOp::Div:
|
|
+ masm.as_fdiv(output, src1, src2);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("unexpected opcode");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitMathF(LMathF* math)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister src1 = ToFloatRegister(math->getOperand(0));
|
|
+ FloatRegister src2 = ToFloatRegister(math->getOperand(1));
|
|
+ FloatRegister output = ToFloatRegister(math->getDef(0));
|
|
+
|
|
+ switch (math->jsop()) {
|
|
+ case JSOp::Add:
|
|
+ masm.as_fadds(output, src1, src2);
|
|
+ break;
|
|
+ case JSOp::Sub:
|
|
+ masm.as_fsubs(output, src1, src2);
|
|
+ break;
|
|
+ case JSOp::Mul:
|
|
+ masm.as_fmuls(output, src1, src2);
|
|
+ break;
|
|
+ case JSOp::Div:
|
|
+ masm.as_fdivs(output, src1, src2);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("unexpected opcode");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitTruncateDToInt32(LTruncateDToInt32* ins)
|
|
+{
|
|
+ emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()),
|
|
+ ins->mir());
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitTruncateFToInt32(LTruncateFToInt32* ins)
|
|
+{
|
|
+ emitTruncateFloat32(ToFloatRegister(ins->input()), ToRegister(ins->output()),
|
|
+ ins->mir());
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ auto input = ToFloatRegister(lir->input());
|
|
+ auto output = ToRegister(lir->output());
|
|
+
|
|
+ MWasmTruncateToInt32* mir = lir->mir();
|
|
+ MIRType fromType = mir->input()->type();
|
|
+
|
|
+ MOZ_ASSERT(fromType == MIRType::Double || fromType == MIRType::Float32);
|
|
+
|
|
+ auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input, output);
|
|
+ addOutOfLineCode(ool, mir);
|
|
+
|
|
+ Label* oolEntry = ool->entry();
|
|
+ if (mir->isUnsigned()) {
|
|
+ if (fromType == MIRType::Double)
|
|
+ masm.wasmTruncateDoubleToUInt32(input, output, mir->isSaturating(), oolEntry);
|
|
+ else if (fromType == MIRType::Float32)
|
|
+ masm.wasmTruncateFloat32ToUInt32(input, output, mir->isSaturating(), oolEntry);
|
|
+ else
|
|
+ MOZ_CRASH("unexpected type");
|
|
+
|
|
+ masm.bind(ool->rejoin());
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (fromType == MIRType::Double)
|
|
+ masm.wasmTruncateDoubleToInt32(input, output, mir->isSaturating(), oolEntry);
|
|
+ else if (fromType == MIRType::Float32)
|
|
+ masm.wasmTruncateFloat32ToInt32(input, output, mir->isSaturating(), oolEntry);
|
|
+ else
|
|
+ MOZ_CRASH("unexpected type");
|
|
+
|
|
+ masm.bind(ool->rejoin());
|
|
+}
|
|
+
|
|
+
|
|
+void
|
|
+CodeGeneratorPPC64::visitOutOfLineBailout(OutOfLineBailout* ool)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ // Push snapshotOffset and make sure stack is aligned.
|
|
+// XXX: this should just be an stdu
|
|
+ masm.subPtr(Imm32(sizeof(Value)), StackPointer);
|
|
+ masm.storePtr(ImmWord(ool->snapshot()->snapshotOffset()), Address(StackPointer, 0));
|
|
+
|
|
+ masm.jump(&deoptLabel_);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGeneratorPPC64::visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCheck* ool)
|
|
+{
|
|
+ if(ool->toType() == MIRType::Int32)
|
|
+ {
|
|
+ masm.outOfLineWasmTruncateToInt32Check(ool->input(), ool->output(), ool->fromType(),
|
|
+ ool->flags(), ool->rejoin(), ool->bytecodeOffset());
|
|
+ } else {
|
|
+ MOZ_ASSERT(ool->toType() == MIRType::Int64);
|
|
+ masm.outOfLineWasmTruncateToInt64Check(ool->input(), ool->output64(), ool->fromType(),
|
|
+ ool->flags(), ool->rejoin(), ool->bytecodeOffset());
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitCopySignF(LCopySignF* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister lhs = ToFloatRegister(ins->getOperand(0));
|
|
+ FloatRegister rhs = ToFloatRegister(ins->getOperand(1));
|
|
+ FloatRegister output = ToFloatRegister(ins->getDef(0));
|
|
+
|
|
+ // ISA 2.05+
|
|
+ masm.as_fcpsgn(output, lhs, rhs);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitCopySignD(LCopySignD* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister lhs = ToFloatRegister(ins->getOperand(0));
|
|
+ FloatRegister rhs = ToFloatRegister(ins->getOperand(1));
|
|
+ FloatRegister output = ToFloatRegister(ins->getDef(0));
|
|
+
|
|
+ // ISA 2.05+
|
|
+ masm.as_fcpsgn(output, lhs, rhs);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitValue(LValue* value)
|
|
+{
|
|
+ ADBlock();
|
|
+ const ValueOperand out = ToOutValue(value);
|
|
+
|
|
+ masm.moveValue(value->value(), out);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitDouble(LDouble* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ const LDefinition* out = ins->getDef(0);
|
|
+
|
|
+ masm.loadConstantDouble(ins->value(), ToFloatRegister(out));
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitFloat32(LFloat32* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ const LDefinition* out = ins->getDef(0);
|
|
+ masm.loadConstantFloat32(ins->value(), ToFloatRegister(out));
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitTestDAndBranch(LTestDAndBranch* test)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister input = ToFloatRegister(test->input());
|
|
+
|
|
+ MBasicBlock* ifTrue = test->ifTrue();
|
|
+ MBasicBlock* ifFalse = test->ifFalse();
|
|
+
|
|
+// XXX: zeroDouble
|
|
+ masm.loadConstantDouble(0.0, ScratchDoubleReg);
|
|
+ // If 0, or NaN, the result is false.
|
|
+
|
|
+ if (isNextBlock(ifFalse->lir())) {
|
|
+ branchToBlock(Assembler::DoubleFloat, input, ScratchDoubleReg, ifTrue,
|
|
+ Assembler::DoubleNotEqual);
|
|
+ } else {
|
|
+ branchToBlock(Assembler::DoubleFloat, input, ScratchDoubleReg, ifFalse,
|
|
+ Assembler::DoubleEqualOrUnordered);
|
|
+ jumpToBlock(ifTrue);
|
|
+ }
|
|
+}
|
|
+
|
|
+// XXX: duplicate code
|
|
+void
|
|
+CodeGenerator::visitTestFAndBranch(LTestFAndBranch* test)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister input = ToFloatRegister(test->input());
|
|
+
|
|
+ MBasicBlock* ifTrue = test->ifTrue();
|
|
+ MBasicBlock* ifFalse = test->ifFalse();
|
|
+
|
|
+// XXX: zeroDouble
|
|
+ masm.loadConstantFloat32(0.0f, ScratchFloat32Reg);
|
|
+ // If 0, or NaN, the result is false.
|
|
+
|
|
+ if (isNextBlock(ifFalse->lir())) {
|
|
+ branchToBlock(Assembler::SingleFloat, input, ScratchFloat32Reg, ifTrue,
|
|
+ Assembler::DoubleNotEqual);
|
|
+ } else {
|
|
+ branchToBlock(Assembler::SingleFloat, input, ScratchFloat32Reg, ifFalse,
|
|
+ Assembler::DoubleEqualOrUnordered);
|
|
+ jumpToBlock(ifTrue);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitCompareD(LCompareD* comp)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister lhs = ToFloatRegister(comp->left());
|
|
+ FloatRegister rhs = ToFloatRegister(comp->right());
|
|
+ Register dest = ToRegister(comp->output());
|
|
+
|
|
+ Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
|
|
+ masm.ma_cmp_set_double(dest, lhs, rhs, cond);
|
|
+}
|
|
+
|
|
+// XXX: duplicate code
|
|
+void
|
|
+CodeGenerator::visitCompareF(LCompareF* comp)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister lhs = ToFloatRegister(comp->left());
|
|
+ FloatRegister rhs = ToFloatRegister(comp->right());
|
|
+ Register dest = ToRegister(comp->output());
|
|
+
|
|
+ Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop());
|
|
+ masm.ma_cmp_set_double(dest, lhs, rhs, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitCompareDAndBranch(LCompareDAndBranch* comp)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister lhs = ToFloatRegister(comp->left());
|
|
+ FloatRegister rhs = ToFloatRegister(comp->right());
|
|
+
|
|
+ Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop());
|
|
+ MBasicBlock* ifTrue = comp->ifTrue();
|
|
+ MBasicBlock* ifFalse = comp->ifFalse();
|
|
+
|
|
+ if (isNextBlock(ifFalse->lir())) {
|
|
+ branchToBlock(Assembler::DoubleFloat, lhs, rhs, ifTrue, cond);
|
|
+ } else {
|
|
+ branchToBlock(Assembler::DoubleFloat, lhs, rhs, ifFalse,
|
|
+ Assembler::InvertCondition(cond));
|
|
+ jumpToBlock(ifTrue);
|
|
+ }
|
|
+}
|
|
+
|
|
+// XXX; duplicate code
|
|
+void
|
|
+CodeGenerator::visitCompareFAndBranch(LCompareFAndBranch* comp)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister lhs = ToFloatRegister(comp->left());
|
|
+ FloatRegister rhs = ToFloatRegister(comp->right());
|
|
+
|
|
+ Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop());
|
|
+ MBasicBlock* ifTrue = comp->ifTrue();
|
|
+ MBasicBlock* ifFalse = comp->ifFalse();
|
|
+
|
|
+ if (isNextBlock(ifFalse->lir())) {
|
|
+ branchToBlock(Assembler::SingleFloat, lhs, rhs, ifTrue, cond);
|
|
+ } else {
|
|
+ branchToBlock(Assembler::SingleFloat, lhs, rhs, ifFalse,
|
|
+ Assembler::InvertCondition(cond));
|
|
+ jumpToBlock(ifTrue);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitBitAndAndBranch(LBitAndAndBranch* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ if (lir->right()->isConstant())
|
|
+ masm.ma_and(ScratchRegister, ToRegister(lir->left()), Imm32(ToInt32(lir->right())));
|
|
+ else
|
|
+ masm.as_and(ScratchRegister, ToRegister(lir->left()), ToRegister(lir->right()));
|
|
+// XXX: use direct result instead of doing a second compare here
|
|
+ emitBranch(ScratchRegister, ScratchRegister, lir->cond(), lir->ifTrue(),
|
|
+ lir->ifFalse());
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmUint32ToDouble(LWasmUint32ToDouble* lir)
|
|
+{
|
|
+ masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmUint32ToFloat32(LWasmUint32ToFloat32* lir)
|
|
+{
|
|
+ masm.convertUInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output()));
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitNotI(LNotI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ masm.cmp32Set(Assembler::Equal, ToRegister(ins->input()), Imm32(0),
|
|
+ ToRegister(ins->output()));
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitNotD(LNotD* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ // Since this operation is |not|, we want to set a bit if
|
|
+ // the double is falsey, which means 0.0, -0.0 or NaN.
|
|
+ FloatRegister in = ToFloatRegister(ins->input());
|
|
+ Register dest = ToRegister(ins->output());
|
|
+
|
|
+ masm.zeroDouble(ScratchDoubleReg);
|
|
+ masm.ma_cmp_set_double(dest, in, ScratchDoubleReg, Assembler::DoubleEqualOrUnordered);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitNotF(LNotF* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ // Since this operation is |not|, we want to set a bit if
|
|
+ // the float32 is falsey, which means 0.0, -0.0 or NaN.
|
|
+ FloatRegister in = ToFloatRegister(ins->input());
|
|
+ Register dest = ToRegister(ins->output());
|
|
+
|
|
+ masm.zeroDouble(ScratchFloat32Reg);
|
|
+ masm.ma_cmp_set_double(dest, in, ScratchFloat32Reg, Assembler::DoubleEqualOrUnordered);
|
|
+}
|
|
+
|
|
+#if(0)
|
|
+void
|
|
+CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ masm.memoryBarrier(ins->type());
|
|
+}
|
|
+#endif
|
|
+
|
|
+void
|
|
+CodeGeneratorPPC64::generateInvalidateEpilogue()
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ // Ensure that there is enough space in the buffer for the OsiPoint
|
|
+ // patching to occur. Otherwise, we could overwrite the invalidation
|
|
+ // epilogue.
|
|
+ for (size_t i = 0; i < sizeof(void*); i += Assembler::NopSize())
|
|
+ masm.nop();
|
|
+
|
|
+ masm.bind(&invalidate_);
|
|
+
|
|
+ // Push the return address of the point that we bailed out at to the stack
|
|
+ masm.pushReturnAddress(); // LR
|
|
+
|
|
+ // Push the Ion script onto the stack (when we determine what that
|
|
+ // pointer is).
|
|
+ invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1)));
|
|
+ TrampolinePtr thunk = gen->jitRuntime()->getInvalidationThunk();
|
|
+ masm.jump(thunk); // A thunk sat on a thump.
|
|
+}
|
|
+
|
|
+class js::jit::OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorPPC64>
|
|
+{
|
|
+ MTableSwitch* mir_;
|
|
+ CodeLabel jumpLabel_;
|
|
+
|
|
+ void accept(CodeGeneratorPPC64* codegen) {
|
|
+ codegen->visitOutOfLineTableSwitch(this);
|
|
+ }
|
|
+
|
|
+ public:
|
|
+ OutOfLineTableSwitch(MTableSwitch* mir)
|
|
+ : mir_(mir)
|
|
+ {}
|
|
+
|
|
+ MTableSwitch* mir() const {
|
|
+ return mir_;
|
|
+ }
|
|
+
|
|
+ CodeLabel* jumpLabel() {
|
|
+ return &jumpLabel_;
|
|
+ }
|
|
+};
|
|
+
|
|
+void
|
|
+CodeGeneratorPPC64::visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool)
|
|
+{
|
|
+ ADBlock();
|
|
+ MTableSwitch* mir = ool->mir();
|
|
+
|
|
+ masm.haltingAlign(sizeof(void*));
|
|
+ masm.bind(ool->jumpLabel());
|
|
+ masm.addCodeLabel(*ool->jumpLabel());
|
|
+
|
|
+ for (size_t i = 0; i < mir->numCases(); i++) {
|
|
+ LBlock* caseblock = skipTrivialBlocks(mir->getCase(i))->lir();
|
|
+ Label* caseheader = caseblock->label();
|
|
+ uint32_t caseoffset = caseheader->offset();
|
|
+
|
|
+ // The entries of the jump table need to be absolute addresses and thus
|
|
+ // must be patched after codegen is finished.
|
|
+ CodeLabel cl;
|
|
+ masm.writeCodePointer(&cl);
|
|
+ cl.target()->bind(caseoffset);
|
|
+ masm.addCodeLabel(cl);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGeneratorPPC64::emitTableSwitchDispatch(MTableSwitch* mir, Register index,
|
|
+ Register base)
|
|
+{
|
|
+ ADBlock();
|
|
+ Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
|
|
+
|
|
+ // Lower value with low value
|
|
+ if (mir->low() != 0)
|
|
+ masm.subPtr(Imm32(mir->low()), index);
|
|
+
|
|
+ // Jump to default case if input is out of range
|
|
+ int32_t cases = mir->numCases();
|
|
+ masm.branchPtr(Assembler::AboveOrEqual, index, ImmWord(cases), defaultcase);
|
|
+
|
|
+ // To fill in the CodeLabels for the case entries, we need to first
|
|
+ // generate the case entries (we don't yet know their offsets in the
|
|
+ // instruction stream).
|
|
+ OutOfLineTableSwitch* ool = new(alloc()) OutOfLineTableSwitch(mir);
|
|
+ addOutOfLineCode(ool, mir);
|
|
+
|
|
+ // Compute the position where a pointer to the right case stands.
|
|
+ masm.ma_li(base, ool->jumpLabel());
|
|
+
|
|
+ BaseIndex pointer(base, index, ScalePointer);
|
|
+
|
|
+ // Jump to the right case
|
|
+ masm.branchToComputedAddress(pointer);
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+void
|
|
+CodeGeneratorPPC64::emitWasmLoad(T* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ const MWasmLoad* mir = lir->mir();
|
|
+
|
|
+ Register ptrScratch = InvalidReg;
|
|
+ if(!lir->ptrCopy()->isBogusTemp()){
|
|
+ ptrScratch = ToRegister(lir->ptrCopy());
|
|
+ }
|
|
+
|
|
+ if (IsUnaligned(mir->access())) {
|
|
+ if (IsFloatingPointType(mir->type())) {
|
|
+ masm.wasmUnalignedLoadFP(mir->access(), HeapReg, ToRegister(lir->ptr()), ptrScratch,
|
|
+ ToFloatRegister(lir->output()), ToRegister(lir->getTemp(1)));
|
|
+ } else {
|
|
+ masm.wasmUnalignedLoad(mir->access(), HeapReg, ToRegister(lir->ptr()),
|
|
+ ptrScratch, ToRegister(lir->output()), ToRegister(lir->getTemp(1)));
|
|
+ }
|
|
+ } else {
|
|
+ masm.wasmLoad(mir->access(), HeapReg, ToRegister(lir->ptr()), ptrScratch,
|
|
+ ToAnyRegister(lir->output()));
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmLoad(LWasmLoad* lir)
|
|
+{
|
|
+ emitWasmLoad(lir);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmUnalignedLoad(LWasmUnalignedLoad* lir)
|
|
+{
|
|
+ emitWasmLoad(lir);
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+void
|
|
+CodeGeneratorPPC64::emitWasmStore(T* lir)
|
|
+{
|
|
+ const MWasmStore* mir = lir->mir();
|
|
+
|
|
+ Register ptrScratch = InvalidReg;
|
|
+ if(!lir->ptrCopy()->isBogusTemp()){
|
|
+ ptrScratch = ToRegister(lir->ptrCopy());
|
|
+ }
|
|
+
|
|
+ if (IsUnaligned(mir->access())) {
|
|
+ if (mir->access().type() == Scalar::Float32 ||
|
|
+ mir->access().type() == Scalar::Float64) {
|
|
+ masm.wasmUnalignedStoreFP(mir->access(), ToFloatRegister(lir->value()),
|
|
+ HeapReg, ToRegister(lir->ptr()), ptrScratch,
|
|
+ ToRegister(lir->getTemp(1)));
|
|
+ } else {
|
|
+ masm.wasmUnalignedStore(mir->access(), ToRegister(lir->value()), HeapReg,
|
|
+ ToRegister(lir->ptr()), ptrScratch,
|
|
+ ToRegister(lir->getTemp(1)));
|
|
+ }
|
|
+ } else {
|
|
+ masm.wasmStore(mir->access(), ToAnyRegister(lir->value()), HeapReg,
|
|
+ ToRegister(lir->ptr()), ptrScratch);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmStore(LWasmStore* lir)
|
|
+{
|
|
+ emitWasmStore(lir);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmUnalignedStore(LWasmUnalignedStore* lir)
|
|
+{
|
|
+ emitWasmStore(lir);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ const MAsmJSLoadHeap* mir = ins->mir();
|
|
+ const LAllocation* ptr = ins->ptr();
|
|
+ const LDefinition* out = ins->output();
|
|
+ const LAllocation* boundsCheckLimit = ins->boundsCheckLimit();
|
|
+
|
|
+ bool isSigned;
|
|
+ int size;
|
|
+ bool isFloat = false;
|
|
+ switch (mir->access().type()) {
|
|
+ case Scalar::Int8: isSigned = true; size = 8; break;
|
|
+ case Scalar::Uint8: isSigned = false; size = 8; break;
|
|
+ case Scalar::Int16: isSigned = true; size = 16; break;
|
|
+ case Scalar::Uint16: isSigned = false; size = 16; break;
|
|
+ case Scalar::Int32: isSigned = true; size = 32; break;
|
|
+ case Scalar::Uint32: isSigned = false; size = 32; break;
|
|
+ case Scalar::Float64: isFloat = true; size = 64; break;
|
|
+ case Scalar::Float32: isFloat = true; size = 32; break;
|
|
+ default: MOZ_CRASH("unexpected array type");
|
|
+ }
|
|
+
|
|
+ if (ptr->isConstant()) {
|
|
+ MOZ_ASSERT(!mir->needsBoundsCheck());
|
|
+ int32_t ptrImm = ptr->toConstant()->toInt32();
|
|
+ MOZ_ASSERT(ptrImm >= 0);
|
|
+ if (isFloat) {
|
|
+ if (size == 32) {
|
|
+ masm.loadFloat32(Address(HeapReg, ptrImm), ToFloatRegister(out));
|
|
+ } else {
|
|
+ masm.loadDouble(Address(HeapReg, ptrImm), ToFloatRegister(out));
|
|
+ }
|
|
+ } else {
|
|
+ masm.ma_load(ToRegister(out), Address(HeapReg, ptrImm),
|
|
+ static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Register ptrReg = ToRegister(ptr);
|
|
+
|
|
+ if (!mir->needsBoundsCheck()) {
|
|
+ if (isFloat) {
|
|
+ if (size == 32) {
|
|
+ masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out));
|
|
+ } else {
|
|
+ masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out));
|
|
+ }
|
|
+ } else {
|
|
+ masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne),
|
|
+ static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Label done, outOfRange;
|
|
+ masm.wasmBoundsCheck32(Assembler::AboveOrEqual, ptrReg, ToRegister(boundsCheckLimit),
|
|
+ &outOfRange);
|
|
+ // Offset is ok, let's load value.
|
|
+ if (isFloat) {
|
|
+ if (size == 32)
|
|
+ masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out));
|
|
+ else
|
|
+ masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out));
|
|
+ } else {
|
|
+ masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne),
|
|
+ static_cast<LoadStoreSize>(size), isSigned ? SignExtend : ZeroExtend);
|
|
+ }
|
|
+ masm.ma_b(&done, ShortJump);
|
|
+ masm.bind(&outOfRange);
|
|
+ // Offset is out of range. Load default values.
|
|
+ if (isFloat) {
|
|
+ if (size == 32)
|
|
+ masm.loadConstantFloat32(float(GenericNaN()), ToFloatRegister(out));
|
|
+ else
|
|
+ masm.loadConstantDouble(GenericNaN(), ToFloatRegister(out));
|
|
+ } else {
|
|
+ masm.move32(Imm32(0), ToRegister(out));
|
|
+ }
|
|
+ masm.bind(&done);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ const MAsmJSStoreHeap* mir = ins->mir();
|
|
+ const LAllocation* value = ins->value();
|
|
+ const LAllocation* ptr = ins->ptr();
|
|
+ const LAllocation* boundsCheckLimit = ins->boundsCheckLimit();
|
|
+
|
|
+ bool isSigned; // Not currently used, but we might later.
|
|
+ int size;
|
|
+ bool isFloat = false;
|
|
+ switch (mir->access().type()) {
|
|
+ case Scalar::Int8: isSigned = true; size = 8; break;
|
|
+ case Scalar::Uint8: isSigned = false; size = 8; break;
|
|
+ case Scalar::Int16: isSigned = true; size = 16; break;
|
|
+ case Scalar::Uint16: isSigned = false; size = 16; break;
|
|
+ case Scalar::Int32: isSigned = true; size = 32; break;
|
|
+ case Scalar::Uint32: isSigned = false; size = 32; break;
|
|
+ case Scalar::Float64: isFloat = true; size = 64; break;
|
|
+ case Scalar::Float32: isFloat = true; size = 32; break;
|
|
+ default: MOZ_CRASH("unexpected array type");
|
|
+ }
|
|
+
|
|
+ if (ptr->isConstant()) {
|
|
+ MOZ_ASSERT(!mir->needsBoundsCheck());
|
|
+ int32_t ptrImm = ptr->toConstant()->toInt32();
|
|
+ MOZ_ASSERT(ptrImm >= 0);
|
|
+
|
|
+ if (isFloat) {
|
|
+ FloatRegister freg = ToFloatRegister(value);
|
|
+ Address addr(HeapReg, ptrImm);
|
|
+ if (size == 32)
|
|
+ masm.storeFloat32(freg, addr);
|
|
+ else
|
|
+ masm.storeDouble(freg, addr);
|
|
+ } else {
|
|
+ masm.ma_store(ToRegister(value), Address(HeapReg, ptrImm),
|
|
+ static_cast<LoadStoreSize>(size));
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Register ptrReg = ToRegister(ptr);
|
|
+ Address dstAddr(ptrReg, 0);
|
|
+
|
|
+ if (!mir->needsBoundsCheck()) {
|
|
+ if (isFloat) {
|
|
+ FloatRegister freg = ToFloatRegister(value);
|
|
+ BaseIndex bi(HeapReg, ptrReg, TimesOne);
|
|
+ if (size == 32)
|
|
+ masm.storeFloat32(freg, bi);
|
|
+ else
|
|
+ masm.storeDouble(freg, bi);
|
|
+ } else {
|
|
+ masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne),
|
|
+ static_cast<LoadStoreSize>(size));
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Label outOfRange;
|
|
+ masm.wasmBoundsCheck32(Assembler::AboveOrEqual, ptrReg, ToRegister(boundsCheckLimit),
|
|
+ &outOfRange);
|
|
+
|
|
+ // Offset is ok, let's store value.
|
|
+ if (isFloat) {
|
|
+ if (size == 32) {
|
|
+ masm.storeFloat32(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne));
|
|
+ } else
|
|
+ masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne));
|
|
+ } else {
|
|
+ masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne),
|
|
+ static_cast<LoadStoreSize>(size));
|
|
+ }
|
|
+
|
|
+ masm.bind(&outOfRange);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmCompareExchangeHeap(LWasmCompareExchangeHeap* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ MWasmCompareExchangeHeap* mir = ins->mir();
|
|
+ Scalar::Type vt = mir->access().type();
|
|
+ Register ptrReg = ToRegister(ins->ptr());
|
|
+ BaseIndex srcAddr(HeapReg, ptrReg, TimesOne, mir->access().offset());
|
|
+ MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
|
|
+
|
|
+ Register oldval = ToRegister(ins->oldValue());
|
|
+ Register newval = ToRegister(ins->newValue());
|
|
+ Register valueTemp = ToTempRegisterOrInvalid(ins->valueTemp());
|
|
+ Register offsetTemp = ToTempRegisterOrInvalid(ins->offsetTemp());
|
|
+ Register maskTemp = ToTempRegisterOrInvalid(ins->maskTemp());
|
|
+
|
|
+ masm.compareExchange(vt, Synchronization::Full(), srcAddr, oldval, newval, valueTemp,
|
|
+ offsetTemp, maskTemp, ToRegister(ins->output()));
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmAtomicExchangeHeap(LWasmAtomicExchangeHeap* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ MWasmAtomicExchangeHeap* mir = ins->mir();
|
|
+ Scalar::Type vt = mir->access().type();
|
|
+ Register ptrReg = ToRegister(ins->ptr());
|
|
+ Register value = ToRegister(ins->value());
|
|
+ BaseIndex srcAddr(HeapReg, ptrReg, TimesOne, mir->access().offset());
|
|
+ MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
|
|
+
|
|
+ Register valueTemp = ToTempRegisterOrInvalid(ins->valueTemp());
|
|
+ Register offsetTemp = ToTempRegisterOrInvalid(ins->offsetTemp());
|
|
+ Register maskTemp = ToTempRegisterOrInvalid(ins->maskTemp());
|
|
+
|
|
+ masm.atomicExchange(vt, Synchronization::Full(), srcAddr, value, valueTemp, offsetTemp,
|
|
+ maskTemp, ToRegister(ins->output()));
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmAtomicBinopHeap(LWasmAtomicBinopHeap* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(ins->mir()->hasUses());
|
|
+ MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
|
|
+
|
|
+ MWasmAtomicBinopHeap* mir = ins->mir();
|
|
+ Scalar::Type vt = mir->access().type();
|
|
+ Register ptrReg = ToRegister(ins->ptr());
|
|
+ Register valueTemp = ToTempRegisterOrInvalid(ins->valueTemp());
|
|
+ Register offsetTemp = ToTempRegisterOrInvalid(ins->offsetTemp());
|
|
+ Register maskTemp = ToTempRegisterOrInvalid(ins->maskTemp());
|
|
+
|
|
+ BaseIndex srcAddr(HeapReg, ptrReg, TimesOne, mir->access().offset());
|
|
+
|
|
+ masm.atomicFetchOp(vt, Synchronization::Full(), mir->operation(), ToRegister(ins->value()),
|
|
+ srcAddr, valueTemp, offsetTemp, maskTemp, ToRegister(ins->output()));
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmAtomicBinopHeapForEffect(LWasmAtomicBinopHeapForEffect* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(!ins->mir()->hasUses());
|
|
+ MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
|
|
+
|
|
+ MWasmAtomicBinopHeap* mir = ins->mir();
|
|
+ Scalar::Type vt = mir->access().type();
|
|
+ Register ptrReg = ToRegister(ins->ptr());
|
|
+ Register valueTemp = ToTempRegisterOrInvalid(ins->valueTemp());
|
|
+ Register offsetTemp = ToTempRegisterOrInvalid(ins->offsetTemp());
|
|
+ Register maskTemp = ToTempRegisterOrInvalid(ins->maskTemp());
|
|
+
|
|
+ BaseIndex srcAddr(HeapReg, ptrReg, TimesOne, mir->access().offset());
|
|
+ masm.atomicEffectOpJS(vt, Synchronization::Full(), mir->operation(), ToRegister(ins->value()),
|
|
+ srcAddr, valueTemp, offsetTemp, maskTemp);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmStackArg(LWasmStackArg* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ const MWasmStackArg* mir = ins->mir();
|
|
+ if (ins->arg()->isConstant()) {
|
|
+ masm.storePtr(ImmWord(ToInt32(ins->arg())), Address(StackPointer, mir->spOffset()));
|
|
+ } else {
|
|
+ if (ins->arg()->isGeneralReg()) {
|
|
+ masm.storePtr(ToRegister(ins->arg()), Address(StackPointer, mir->spOffset()));
|
|
+ } else if (mir->input()->type() == MIRType::Double) {
|
|
+ masm.storeDouble(ToFloatRegister(ins->arg()),
|
|
+ Address(StackPointer, mir->spOffset()));
|
|
+ } else {
|
|
+ masm.storeFloat32(ToFloatRegister(ins->arg()),
|
|
+ Address(StackPointer, mir->spOffset()));
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmStackArgI64(LWasmStackArgI64* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ const MWasmStackArg* mir = ins->mir();
|
|
+ Address dst(StackPointer, mir->spOffset());
|
|
+ if (IsConstant(ins->arg()))
|
|
+ masm.store64(Imm64(ToInt64(ins->arg())), dst);
|
|
+ else
|
|
+ masm.store64(ToRegister64(ins->arg()), dst);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmSelect(LWasmSelect* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ MIRType mirType = ins->mir()->type();
|
|
+
|
|
+ Register cond = ToRegister(ins->condExpr());
|
|
+ const LAllocation* falseExpr = ins->falseExpr();
|
|
+
|
|
+ if (mirType == MIRType::Int32) {
|
|
+ Register out = ToRegister(ins->output());
|
|
+ MOZ_ASSERT(ToRegister(ins->trueExpr()) == out, "true expr input is reused for output");
|
|
+ masm.as_cmpdi(cond, 0);
|
|
+MOZ_CRASH("visitWasmSelect NYI for isel"); // XXX: also check if 64-bit int
|
|
+ masm.as_isel(out, out, ToRegister(falseExpr), 2); // CR0[EQ]
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ FloatRegister out = ToFloatRegister(ins->output());
|
|
+ MOZ_ASSERT(ToFloatRegister(ins->trueExpr()) == out, "true expr input is reused for output");
|
|
+
|
|
+ // We can't use fsel because cond isn't a float register.
|
|
+ Label done;
|
|
+ masm.ma_bc(cond, cond, &done, Assembler::NonZero, ShortJump);
|
|
+
|
|
+ if (mirType == MIRType::Float32)
|
|
+ masm.loadFloat32(ToAddress(falseExpr), out);
|
|
+ else if (mirType == MIRType::Double)
|
|
+ masm.loadDouble(ToAddress(falseExpr), out);
|
|
+ else
|
|
+ MOZ_CRASH("unhandled type in visitWasmSelect!");
|
|
+
|
|
+ masm.bind(&done);
|
|
+}
|
|
+
|
|
+// We expect to handle only the case where compare is {U,}Int32 and select is
|
|
+// {U,}Int32, and the "true" input is reused for the output.
|
|
+void CodeGenerator::visitWasmCompareAndSelect(LWasmCompareAndSelect* ins) {
|
|
+ bool cmpIs32bit = ins->compareType() == MCompare::Compare_Int32 ||
|
|
+ ins->compareType() == MCompare::Compare_UInt32;
|
|
+ bool selIs32bit = ins->mir()->type() == MIRType::Int32;
|
|
+
|
|
+ MOZ_RELEASE_ASSERT(
|
|
+ cmpIs32bit && selIs32bit,
|
|
+ "CodeGenerator::visitWasmCompareAndSelect: unexpected types");
|
|
+
|
|
+ Register trueExprAndDest = ToRegister(ins->output());
|
|
+ MOZ_ASSERT(ToRegister(ins->ifTrueExpr()) == trueExprAndDest,
|
|
+ "true expr input is reused for output");
|
|
+
|
|
+ Assembler::Condition cond = Assembler::InvertCondition(
|
|
+ JSOpToCondition(ins->compareType(), ins->jsop()));
|
|
+ const LAllocation* rhs = ins->rightExpr();
|
|
+ const LAllocation* falseExpr = ins->ifFalseExpr();
|
|
+ Register lhs = ToRegister(ins->leftExpr());
|
|
+
|
|
+ // This turns into an isel.
|
|
+ masm.cmp32Move32(cond, lhs, ToRegister(rhs), ToRegister(falseExpr),
|
|
+ trueExprAndDest);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmReinterpret(LWasmReinterpret* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(gen->compilingWasm());
|
|
+ MWasmReinterpret* ins = lir->mir();
|
|
+
|
|
+ MIRType to = ins->type();
|
|
+ DebugOnly<MIRType> from = ins->input()->type();
|
|
+
|
|
+ switch (to) {
|
|
+// XXX: use VSX float move instructions (moveToDouble, etc)
|
|
+ // We don't have much choice other than to spill to memory for these.
|
|
+ case MIRType::Int32:
|
|
+ MOZ_ASSERT(from == MIRType::Float32);
|
|
+ masm.as_stfsu(ToFloatRegister(lir->input()), StackPointer, -4);
|
|
+ // Keep in separate dispatch groups.
|
|
+ masm.nop();
|
|
+ masm.nop();
|
|
+ masm.nop();
|
|
+ masm.as_lwz(ToRegister(lir->output()), StackPointer, 0);
|
|
+ masm.as_addi(StackPointer, StackPointer, 4);
|
|
+ break;
|
|
+ case MIRType::Float32:
|
|
+ MOZ_ASSERT(from == MIRType::Int32);
|
|
+ masm.as_stwu(ToRegister(lir->input()), StackPointer, -4);
|
|
+ // Keep in separate dispatch groups.
|
|
+ masm.nop();
|
|
+ masm.nop();
|
|
+ masm.nop();
|
|
+ masm.as_lfs(ToFloatRegister(lir->output()), StackPointer, 0);
|
|
+ masm.as_addi(StackPointer, StackPointer, 4);
|
|
+ break;
|
|
+ case MIRType::Double:
|
|
+ case MIRType::Int64:
|
|
+ MOZ_CRASH("not handled by this LIR opcode");
|
|
+ default:
|
|
+ MOZ_CRASH("unexpected WasmReinterpret");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitUDivOrMod(LUDivOrMod* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register lhs = ToRegister(ins->lhs());
|
|
+ Register rhs = ToRegister(ins->rhs());
|
|
+ Register output = ToRegister(ins->output());
|
|
+ Label done;
|
|
+
|
|
+ // Although divwuo can flag overflow for divide by zero, we end up
|
|
+ // checking anyway to deal with the Infinity|0 situation, so we just don't
|
|
+ // bother and use regular (cheaper) divwu.
|
|
+ if (ins->canBeDivideByZero()) {
|
|
+ if (ins->mir()->isTruncated()) {
|
|
+ if (ins->trapOnError()) {
|
|
+ Label nonZero;
|
|
+ masm.ma_bc(rhs, rhs, &nonZero, Assembler::NonZero);
|
|
+ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->bytecodeOffset());
|
|
+ masm.bind(&nonZero);
|
|
+ } else {
|
|
+ // Infinity|0 == 0
|
|
+ Label notzero;
|
|
+ masm.ma_bc(rhs, rhs, ¬zero, Assembler::NonZero, ShortJump);
|
|
+ masm.move32(Imm32(0), output);
|
|
+ masm.ma_b(&done, ShortJump);
|
|
+ masm.bind(¬zero);
|
|
+ }
|
|
+ } else {
|
|
+ bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot());
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // If the remainder is > 0, bailout since this must be a double.
|
|
+ if (ins->mir()->isDiv()) {
|
|
+ // Compute quotient to output register; separately recover remainder.
|
|
+ masm.as_divwu(output, lhs, rhs);
|
|
+ masm.as_mullw(SecondScratchReg, output, rhs);
|
|
+ masm.as_subf(ScratchRegister, SecondScratchReg, lhs); // T = B - A
|
|
+ if (!ins->mir()->toDiv()->canTruncateRemainder())
|
|
+ bailoutCmp32(Assembler::NonZero, ScratchRegister, ScratchRegister, ins->snapshot());
|
|
+ } else {
|
|
+ // Compute remainder to output register.
|
|
+ masm.as_divwu(ScratchRegister, lhs, rhs);
|
|
+ masm.as_mullw(SecondScratchReg, ScratchRegister, rhs);
|
|
+ masm.as_subf(output, SecondScratchReg, lhs);
|
|
+ }
|
|
+
|
|
+ if (!ins->mir()->isTruncated())
|
|
+ bailoutCmp32(Assembler::LessThan, output, Imm32(0), ins->snapshot());
|
|
+
|
|
+ masm.bind(&done);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitEffectiveAddress(LEffectiveAddress* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ const MEffectiveAddress* mir = ins->mir();
|
|
+ Register base = ToRegister(ins->base());
|
|
+ Register index = ToRegister(ins->index());
|
|
+ Register output = ToRegister(ins->output());
|
|
+
|
|
+ BaseIndex address(base, index, mir->scale(), mir->displacement());
|
|
+ masm.computeEffectiveAddress(address, output);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitNegI(LNegI* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register input = ToRegister(ins->input());
|
|
+ Register output = ToRegister(ins->output());
|
|
+
|
|
+ masm.as_neg(output, input);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitNegI64(LNegI64* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register input = ToRegister(ins->input());
|
|
+ Register output = ToRegister(ins->output());
|
|
+
|
|
+ masm.as_neg(output, input);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitNegD(LNegD* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister input = ToFloatRegister(ins->input());
|
|
+ FloatRegister output = ToFloatRegister(ins->output());
|
|
+
|
|
+ masm.as_fneg(output, input);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitNegF(LNegF* ins)
|
|
+{
|
|
+ ADBlock();
|
|
+ FloatRegister input = ToFloatRegister(ins->input());
|
|
+ FloatRegister output = ToFloatRegister(ins->output());
|
|
+
|
|
+ masm.as_fneg(output, input);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmAddOffset(LWasmAddOffset* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ MWasmAddOffset* mir = lir->mir();
|
|
+ Register base = ToRegister(lir->base());
|
|
+ Register out = ToRegister(lir->output());
|
|
+
|
|
+ Label ok;
|
|
+// XXX: add short parameter since usually short
|
|
+ masm.ma_addTestCarry(Assembler::CarryClear, out, base, Imm32(mir->offset()), &ok, /* is32 */ true);
|
|
+ masm.wasmTrap(wasm::Trap::OutOfBounds, mir->bytecodeOffset());
|
|
+ masm.bind(&ok);
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmAddOffset64(LWasmAddOffset64* lir) {
|
|
+ ADBlock();
|
|
+ MWasmAddOffset* mir = lir->mir();
|
|
+ Register64 base = ToRegister64(lir->base());
|
|
+ Register64 out = ToOutRegister64(lir);
|
|
+
|
|
+ Label ok;
|
|
+// XXX: add short parameter since usually short
|
|
+ masm.ma_addTestCarry(Assembler::CarryClear, out.reg, base.reg,
|
|
+ ImmWord(mir->offset()), &ok, /* is32 */ false);
|
|
+ masm.wasmTrap(wasm::Trap::OutOfBounds, mir->bytecodeOffset());
|
|
+ masm.bind(&ok);
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitNearbyInt(LNearbyInt* lir) {
|
|
+ FloatRegister input = ToFloatRegister(lir->input());
|
|
+ FloatRegister output = ToFloatRegister(lir->output());
|
|
+
|
|
+ RoundingMode roundingMode = lir->mir()->roundingMode();
|
|
+ masm.nearbyIntDouble(roundingMode, input, output);
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitNearbyIntF(LNearbyIntF* lir) {
|
|
+ FloatRegister input = ToFloatRegister(lir->input());
|
|
+ FloatRegister output = ToFloatRegister(lir->output());
|
|
+
|
|
+ RoundingMode roundingMode = lir->mir()->roundingMode();
|
|
+ masm.nearbyIntFloat32(roundingMode, input, output);
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmBuiltinTruncateDToInt32(
|
|
+ LWasmBuiltinTruncateDToInt32* lir) {
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmBuiltinTruncateFToInt32(LWasmBuiltinTruncateFToInt32 *lir)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+/*
|
|
+void
|
|
+CodeGenerator::visitWasmHeapBase(LWasmHeapBase *lir)
|
|
+{
|
|
+ MOZ_ASSERT(lir->instance()->isBogus());
|
|
+ masm.movePtr(HeapReg, ToRegister(lir->output()));
|
|
+}
|
|
+*/
|
|
+
|
|
+void
|
|
+CodeGenerator::visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(lir->mir()->hasUses());
|
|
+
|
|
+ AnyRegister output = ToAnyRegister(lir->output());
|
|
+ Register elements = ToRegister(lir->elements());
|
|
+ Register outTemp = ToTempRegisterOrInvalid(lir->temp2());
|
|
+ Register valueTemp = ToTempRegisterOrInvalid(lir->valueTemp());
|
|
+ Register offsetTemp = ToTempRegisterOrInvalid(lir->offsetTemp());
|
|
+ Register maskTemp = ToTempRegisterOrInvalid(lir->maskTemp());
|
|
+ Register value = ToRegister(lir->value());
|
|
+
|
|
+ Scalar::Type arrayType = lir->mir()->arrayType();
|
|
+ int width = Scalar::byteSize(arrayType);
|
|
+
|
|
+ if (lir->index()->isConstant()) {
|
|
+ Address mem(elements, ToInt32(lir->index()) * width);
|
|
+ masm.atomicFetchOpJS(arrayType, Synchronization::Full(), lir->mir()->operation(), value,
|
|
+ mem, valueTemp, offsetTemp, maskTemp, outTemp, output);
|
|
+ } else {
|
|
+ BaseIndex mem(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
|
|
+ masm.atomicFetchOpJS(arrayType, Synchronization::Full(), lir->mir()->operation(), value,
|
|
+ mem, valueTemp, offsetTemp, maskTemp, outTemp, output);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitAtomicTypedArrayElementBinopForEffect(LAtomicTypedArrayElementBinopForEffect* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(!lir->mir()->hasUses());
|
|
+
|
|
+ Register elements = ToRegister(lir->elements());
|
|
+ Register valueTemp = ToTempRegisterOrInvalid(lir->valueTemp());
|
|
+ Register offsetTemp = ToTempRegisterOrInvalid(lir->offsetTemp());
|
|
+ Register maskTemp = ToTempRegisterOrInvalid(lir->maskTemp());
|
|
+ Register value = ToRegister(lir->value());
|
|
+ Scalar::Type arrayType = lir->mir()->arrayType();
|
|
+ int width = Scalar::byteSize(arrayType);
|
|
+
|
|
+ if (lir->index()->isConstant()) {
|
|
+ Address mem(elements, ToInt32(lir->index()) * width);
|
|
+ masm.atomicEffectOpJS(arrayType, Synchronization::Full(), lir->mir()->operation(), value,
|
|
+ mem, valueTemp, offsetTemp, maskTemp);
|
|
+ } else {
|
|
+ BaseIndex mem(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
|
|
+ masm.atomicEffectOpJS(arrayType, Synchronization::Full(), lir->mir()->operation(), value,
|
|
+ mem, valueTemp, offsetTemp, maskTemp);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register elements = ToRegister(lir->elements());
|
|
+ AnyRegister output = ToAnyRegister(lir->output());
|
|
+ Register outTemp = ToTempRegisterOrInvalid(lir->temp());
|
|
+
|
|
+ Register oldval = ToRegister(lir->oldval());
|
|
+ Register newval = ToRegister(lir->newval());
|
|
+ Register valueTemp = ToTempRegisterOrInvalid(lir->valueTemp());
|
|
+ Register offsetTemp = ToTempRegisterOrInvalid(lir->offsetTemp());
|
|
+ Register maskTemp = ToTempRegisterOrInvalid(lir->maskTemp());
|
|
+
|
|
+ Scalar::Type arrayType = lir->mir()->arrayType();
|
|
+ int width = Scalar::byteSize(arrayType);
|
|
+
|
|
+ if (lir->index()->isConstant()) {
|
|
+ Address dest(elements, ToInt32(lir->index()) * width);
|
|
+ masm.compareExchangeJS(arrayType, Synchronization::Full(), dest, oldval, newval,
|
|
+ valueTemp, offsetTemp, maskTemp, outTemp, output);
|
|
+ } else {
|
|
+ BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
|
|
+ masm.compareExchangeJS(arrayType, Synchronization::Full(), dest, oldval, newval,
|
|
+ valueTemp, offsetTemp, maskTemp, outTemp, output);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register elements = ToRegister(lir->elements());
|
|
+ AnyRegister output = ToAnyRegister(lir->output());
|
|
+ Register outTemp = ToTempRegisterOrInvalid(lir->temp());
|
|
+
|
|
+ Register value = ToRegister(lir->value());
|
|
+ Register valueTemp = ToTempRegisterOrInvalid(lir->valueTemp());
|
|
+ Register offsetTemp = ToTempRegisterOrInvalid(lir->offsetTemp());
|
|
+ Register maskTemp = ToTempRegisterOrInvalid(lir->maskTemp());
|
|
+
|
|
+ Scalar::Type arrayType = lir->mir()->arrayType();
|
|
+ int width = Scalar::byteSize(arrayType);
|
|
+
|
|
+ if (lir->index()->isConstant()) {
|
|
+ Address dest(elements, ToInt32(lir->index()) * width);
|
|
+ masm.atomicExchangeJS(arrayType, Synchronization::Full(), dest, value, valueTemp,
|
|
+ offsetTemp, maskTemp, outTemp, output);
|
|
+ } else {
|
|
+ BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
|
|
+ masm.atomicExchangeJS(arrayType, Synchronization::Full(), dest, value, valueTemp,
|
|
+ offsetTemp, maskTemp, outTemp, output);
|
|
+ }
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitAtomicLoad64(LAtomicLoad64* lir) {
|
|
+ Register elements = ToRegister(lir->elements());
|
|
+ Register temp = ToRegister(lir->temp());
|
|
+ Register64 temp64 = ToRegister64(lir->temp64());
|
|
+ Register out = ToRegister(lir->output());
|
|
+
|
|
+ const MLoadUnboxedScalar* mir = lir->mir();
|
|
+
|
|
+ Scalar::Type storageType = mir->storageType();
|
|
+
|
|
+ auto sync = Synchronization::Load();
|
|
+
|
|
+ masm.memoryBarrierBefore(sync);
|
|
+ if (lir->index()->isConstant()) {
|
|
+ Address source =
|
|
+ ToAddress(elements, lir->index(), storageType, mir->offsetAdjustment());
|
|
+ masm.load64(source, temp64);
|
|
+ } else {
|
|
+ BaseIndex source(elements, ToRegister(lir->index()),
|
|
+ ScaleFromScalarType(storageType), mir->offsetAdjustment());
|
|
+ masm.load64(source, temp64);
|
|
+ }
|
|
+ masm.memoryBarrierAfter(sync);
|
|
+
|
|
+ emitCreateBigInt(lir, storageType, temp64, out, temp);
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitAtomicStore64(LAtomicStore64* lir) {
|
|
+ Register elements = ToRegister(lir->elements());
|
|
+ Register value = ToRegister(lir->value());
|
|
+ Register64 temp1 = ToRegister64(lir->temp1());
|
|
+
|
|
+ Scalar::Type writeType = lir->mir()->writeType();
|
|
+
|
|
+ masm.loadBigInt64(value, temp1);
|
|
+
|
|
+ auto sync = Synchronization::Store();
|
|
+
|
|
+ masm.memoryBarrierBefore(sync);
|
|
+ if (lir->index()->isConstant()) {
|
|
+ Address dest = ToAddress(elements, lir->index(), writeType);
|
|
+ masm.store64(temp1, dest);
|
|
+ } else {
|
|
+ BaseIndex dest(elements, ToRegister(lir->index()),
|
|
+ ScaleFromScalarType(writeType));
|
|
+ masm.store64(temp1, dest);
|
|
+ }
|
|
+ masm.memoryBarrierAfter(sync);
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitCompareExchangeTypedArrayElement64(
|
|
+ LCompareExchangeTypedArrayElement64* lir) {
|
|
+ Register elements = ToRegister(lir->elements());
|
|
+ Register oldval = ToRegister(lir->oldval());
|
|
+ Register newval = ToRegister(lir->newval());
|
|
+ Register64 temp1 = ToRegister64(lir->temp1());
|
|
+ Register64 temp2 = ToRegister64(lir->temp2());
|
|
+ Register out = ToRegister(lir->output());
|
|
+ Register64 tempOut(out);
|
|
+
|
|
+ Scalar::Type arrayType = lir->mir()->arrayType();
|
|
+
|
|
+ masm.loadBigInt64(oldval, temp1);
|
|
+ masm.loadBigInt64(newval, tempOut);
|
|
+
|
|
+ if (lir->index()->isConstant()) {
|
|
+ Address dest = ToAddress(elements, lir->index(), arrayType);
|
|
+ masm.compareExchange64(Synchronization::Full(), dest, temp1, tempOut,
|
|
+ temp2);
|
|
+ } else {
|
|
+ BaseIndex dest(elements, ToRegister(lir->index()),
|
|
+ ScaleFromScalarType(arrayType));
|
|
+ masm.compareExchange64(Synchronization::Full(), dest, temp1, tempOut,
|
|
+ temp2);
|
|
+ }
|
|
+
|
|
+ emitCreateBigInt(lir, arrayType, temp2, out, temp1.scratchReg());
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitAtomicExchangeTypedArrayElement64(
|
|
+ LAtomicExchangeTypedArrayElement64* lir) {
|
|
+ Register elements = ToRegister(lir->elements());
|
|
+ Register value = ToRegister(lir->value());
|
|
+ Register64 temp1 = ToRegister64(lir->temp1());
|
|
+ Register64 temp2 = Register64(ToRegister(lir->temp2()));
|
|
+ Register out = ToRegister(lir->output());
|
|
+
|
|
+ Scalar::Type arrayType = lir->mir()->arrayType();
|
|
+
|
|
+ masm.loadBigInt64(value, temp1);
|
|
+
|
|
+ if (lir->index()->isConstant()) {
|
|
+ Address dest = ToAddress(elements, lir->index(), arrayType);
|
|
+ masm.atomicExchange64(Synchronization::Full(), dest, temp1, temp2);
|
|
+ } else {
|
|
+ BaseIndex dest(elements, ToRegister(lir->index()),
|
|
+ ScaleFromScalarType(arrayType));
|
|
+ masm.atomicExchange64(Synchronization::Full(), dest, temp1, temp2);
|
|
+ }
|
|
+
|
|
+ emitCreateBigInt(lir, arrayType, temp2, out, temp1.scratchReg());
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitAtomicTypedArrayElementBinop64(
|
|
+ LAtomicTypedArrayElementBinop64* lir) {
|
|
+ MOZ_ASSERT(lir->mir()->hasUses());
|
|
+
|
|
+ Register elements = ToRegister(lir->elements());
|
|
+ Register value = ToRegister(lir->value());
|
|
+ Register64 temp1 = ToRegister64(lir->temp1());
|
|
+ Register64 temp2 = ToRegister64(lir->temp2());
|
|
+ Register out = ToRegister(lir->output());
|
|
+ Register64 tempOut = Register64(out);
|
|
+
|
|
+ Scalar::Type arrayType = lir->mir()->arrayType();
|
|
+ AtomicOp atomicOp = lir->mir()->operation();
|
|
+
|
|
+ masm.loadBigInt64(value, temp1);
|
|
+
|
|
+ if (lir->index()->isConstant()) {
|
|
+ Address dest = ToAddress(elements, lir->index(), arrayType);
|
|
+ masm.atomicFetchOp64(Synchronization::Full(), atomicOp, temp1, dest,
|
|
+ tempOut, temp2);
|
|
+ } else {
|
|
+ BaseIndex dest(elements, ToRegister(lir->index()),
|
|
+ ScaleFromScalarType(arrayType));
|
|
+ masm.atomicFetchOp64(Synchronization::Full(), atomicOp, temp1, dest,
|
|
+ tempOut, temp2);
|
|
+ }
|
|
+
|
|
+ emitCreateBigInt(lir, arrayType, temp2, out, temp1.scratchReg());
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitAtomicTypedArrayElementBinopForEffect64(
|
|
+ LAtomicTypedArrayElementBinopForEffect64* lir) {
|
|
+ MOZ_ASSERT(!lir->mir()->hasUses());
|
|
+
|
|
+ Register elements = ToRegister(lir->elements());
|
|
+ Register value = ToRegister(lir->value());
|
|
+ Register64 temp1 = ToRegister64(lir->temp1());
|
|
+ Register64 temp2 = ToRegister64(lir->temp2());
|
|
+
|
|
+ Scalar::Type arrayType = lir->mir()->arrayType();
|
|
+ AtomicOp atomicOp = lir->mir()->operation();
|
|
+
|
|
+ masm.loadBigInt64(value, temp1);
|
|
+
|
|
+ if (lir->index()->isConstant()) {
|
|
+ Address dest = ToAddress(elements, lir->index(), arrayType);
|
|
+ masm.atomicEffectOp64(Synchronization::Full(), atomicOp, temp1, dest,
|
|
+ temp2);
|
|
+ } else {
|
|
+ BaseIndex dest(elements, ToRegister(lir->index()),
|
|
+ ScaleFromScalarType(arrayType));
|
|
+ masm.atomicEffectOp64(Synchronization::Full(), atomicOp, temp1, dest,
|
|
+ temp2);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmCompareExchangeI64(LWasmCompareExchangeI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register ptr = ToRegister(lir->ptr());
|
|
+ Register64 oldValue = ToRegister64(lir->oldValue());
|
|
+ Register64 newValue = ToRegister64(lir->newValue());
|
|
+ Register64 output = ToOutRegister64(lir);
|
|
+ uint32_t offset = lir->mir()->access().offset();
|
|
+
|
|
+ BaseIndex addr(HeapReg, ptr, TimesOne, offset);
|
|
+ masm.wasmCompareExchange64(lir->mir()->access(), addr, oldValue, newValue,
|
|
+ output);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmAtomicExchangeI64(LWasmAtomicExchangeI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register ptr = ToRegister(lir->ptr());
|
|
+ Register64 value = ToRegister64(lir->value());
|
|
+ Register64 output = ToOutRegister64(lir);
|
|
+ uint32_t offset = lir->mir()->access().offset();
|
|
+
|
|
+ BaseIndex addr(HeapReg, ptr, TimesOne, offset);
|
|
+ masm.wasmAtomicExchange64(lir->mir()->access(), addr, value, output);
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitWasmAtomicBinopI64(LWasmAtomicBinopI64* lir)
|
|
+{
|
|
+ ADBlock();
|
|
+ Register ptr = ToRegister(lir->ptr());
|
|
+ Register64 value = ToRegister64(lir->value());
|
|
+ Register64 output = ToOutRegister64(lir);
|
|
+ Register64 temp(ToRegister(lir->getTemp(0)));
|
|
+ uint32_t offset = lir->mir()->access().offset();
|
|
+
|
|
+ BaseIndex addr(HeapReg, ptr, TimesOne, offset);
|
|
+
|
|
+ masm.wasmAtomicFetchOp64(lir->mir()->access(), lir->mir()->operation(),
|
|
+ value, addr, temp, output);
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitSimd128(LSimd128* ins) { MOZ_CRASH("No SIMD"); }
|
|
+
|
|
+void CodeGenerator::visitWasmTernarySimd128(LWasmTernarySimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmBinarySimd128(LWasmBinarySimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmBinarySimd128WithConstant(
|
|
+ LWasmBinarySimd128WithConstant* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmVariableShiftSimd128(
|
|
+ LWasmVariableShiftSimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmConstantShiftSimd128(
|
|
+ LWasmConstantShiftSimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmSignReplicationSimd128(
|
|
+ LWasmSignReplicationSimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmShuffleSimd128(LWasmShuffleSimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmPermuteSimd128(LWasmPermuteSimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmReplaceLaneSimd128(LWasmReplaceLaneSimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmReplaceInt64LaneSimd128(
|
|
+ LWasmReplaceInt64LaneSimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmScalarToSimd128(LWasmScalarToSimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmInt64ToSimd128(LWasmInt64ToSimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmUnarySimd128(LWasmUnarySimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmReduceSimd128(LWasmReduceSimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmReduceAndBranchSimd128(
|
|
+ LWasmReduceAndBranchSimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmReduceSimd128ToInt64(
|
|
+ LWasmReduceSimd128ToInt64* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmLoadLaneSimd128(LWasmLoadLaneSimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+void CodeGenerator::visitWasmStoreLaneSimd128(LWasmStoreLaneSimd128* ins) {
|
|
+ MOZ_CRASH("No SIMD");
|
|
+}
|
|
+
|
|
+#if 0
|
|
+void
|
|
+CodeGenerator::visitSimdSplatX4(LSimdSplatX4* lir)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimd128Int(LSimd128Int* ins)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimd128Float(LSimd128Float* ins)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdExtractElementI(LSimdExtractElementI* ins)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdExtractElementF(LSimdExtractElementF* ins)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdBinaryCompIx4(LSimdBinaryCompIx4* lir)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdBinaryCompFx4(LSimdBinaryCompFx4* lir)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdBinaryArithIx4(LSimdBinaryArithIx4* lir)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdBinaryArithFx4(LSimdBinaryArithFx4* lir)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdBinaryBitwise(LSimdBinaryBitwise* lir)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitNearbyInt(LNearbyInt*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdShift(LSimdShift*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitNearbyIntF(LNearbyIntF*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdSelect(LSimdSelect*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdAllTrue(LSimdAllTrue*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdAnyTrue(LSimdAnyTrue*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdShuffle(LSimdShuffle*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdSplatX8(LSimdSplatX8*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdSplatX16(LSimdSplatX16*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdSwizzleF(LSimdSwizzleF*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdSwizzleI(LSimdSwizzleI*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdShuffleX4(LSimdShuffleX4*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdBinaryCompIx8(LSimdBinaryCompIx8*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdUnaryArithFx4(LSimdUnaryArithFx4*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdUnaryArithIx4(LSimdUnaryArithIx4*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdUnaryArithIx8(LSimdUnaryArithIx8*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitFloat32x4ToInt32x4(LFloat32x4ToInt32x4*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitInt32x4ToFloat32x4(LInt32x4ToFloat32x4*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdBinaryArithIx8(LSimdBinaryArithIx8*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdBinaryCompIx16(LSimdBinaryCompIx16*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdInsertElementF(LSimdInsertElementF*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdInsertElementI(LSimdInsertElementI*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdUnaryArithIx16(LSimdUnaryArithIx16*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitFloat32x4ToUint32x4(LFloat32x4ToUint32x4*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdBinaryArithIx16(LSimdBinaryArithIx16*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdExtractElementB(LSimdExtractElementB*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdGeneralShuffleF(LSimdGeneralShuffleF*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdGeneralShuffleI(LSimdGeneralShuffleI*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdReinterpretCast(LSimdReinterpretCast*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdBinarySaturating(LSimdBinarySaturating*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+CodeGenerator::visitSimdExtractElementU2D(LSimdExtractElementU2D*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+#endif
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/CodeGenerator-ppc64.h
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/CodeGenerator-ppc64.h Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,191 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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_PPC64_CodeGenerator_PPC64_h
|
|
+#define jit_PPC64_CodeGenerator_PPC64_h
|
|
+
|
|
+#include "jit/ppc64/Assembler-ppc64.h"
|
|
+#include "jit/shared/CodeGenerator-shared.h"
|
|
+
|
|
+namespace js {
|
|
+namespace jit {
|
|
+
|
|
+class OutOfLineBailout;
|
|
+class OutOfLineTableSwitch;
|
|
+class CodeGeneratorPPC64;
|
|
+
|
|
+using OutOfLineWasmTruncateCheck = OutOfLineWasmTruncateCheckBase<CodeGeneratorPPC64>;
|
|
+
|
|
+class CodeGeneratorPPC64 : public CodeGeneratorShared
|
|
+{
|
|
+ friend class MoveResolverPPC64;
|
|
+
|
|
+ protected:
|
|
+ CodeGeneratorPPC64(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm);
|
|
+
|
|
+ NonAssertingLabel deoptLabel_;
|
|
+
|
|
+ Operand ToOperand(const LAllocation& a);
|
|
+ Operand ToOperand(const LAllocation* a);
|
|
+ Operand ToOperand(const LDefinition* def);
|
|
+
|
|
+ Operand ToOperandOrRegister64(const LInt64Allocation input);
|
|
+
|
|
+ MoveOperand toMoveOperand(LAllocation a) const;
|
|
+
|
|
+ template <typename T1, typename T2>
|
|
+ void bailoutCmp32(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot* snapshot) {
|
|
+ Label bail;
|
|
+ masm.branch32(c, lhs, rhs, &bail);
|
|
+ bailoutFrom(&bail, snapshot);
|
|
+ }
|
|
+ template<typename T>
|
|
+ void bailoutTest32(Assembler::Condition c, Register lhs, T rhs, LSnapshot* snapshot) {
|
|
+ Label bail;
|
|
+ masm.branchTest32(c, lhs, rhs, &bail);
|
|
+ bailoutFrom(&bail, snapshot);
|
|
+ }
|
|
+ template <typename T1, typename T2>
|
|
+ void bailoutCmpPtr(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot* snapshot) {
|
|
+ Label bail;
|
|
+ masm.branchPtr(c, lhs, rhs, &bail);
|
|
+ bailoutFrom(&bail, snapshot);
|
|
+ }
|
|
+ void bailoutTestPtr(Assembler::Condition c, Register lhs, Register rhs, LSnapshot* snapshot) {
|
|
+ Label bail;
|
|
+ masm.branchTestPtr(c, lhs, rhs, &bail);
|
|
+ bailoutFrom(&bail, snapshot);
|
|
+ }
|
|
+ void bailoutIfFalseBool(Register reg, LSnapshot* snapshot) {
|
|
+ Label bail;
|
|
+ masm.branchTest32(Assembler::Zero, reg, Imm32(0xFF), &bail);
|
|
+ bailoutFrom(&bail, snapshot);
|
|
+ }
|
|
+
|
|
+ void bailoutFrom(Label* label, LSnapshot* snapshot);
|
|
+ void bailout(LSnapshot* snapshot);
|
|
+
|
|
+ bool generateOutOfLineCode();
|
|
+
|
|
+ template <typename T>
|
|
+ void branchToBlock(Register lhs, T rhs, MBasicBlock* mir, Assembler::Condition cond)
|
|
+ {
|
|
+ masm.ma_bc(lhs, rhs, skipTrivialBlocks(mir)->lir()->label(), cond);
|
|
+ }
|
|
+ void branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs,
|
|
+ MBasicBlock* mir, Assembler::DoubleCondition cond);
|
|
+
|
|
+ // Emits a branch that directs control flow to the true block if |cond| is
|
|
+ // true, and the false block if |cond| is false.
|
|
+ template <typename T>
|
|
+ void emitBranch(Register lhs, T rhs, Assembler::Condition cond,
|
|
+ MBasicBlock* mirTrue, MBasicBlock* mirFalse)
|
|
+ {
|
|
+ if (isNextBlock(mirFalse->lir())) {
|
|
+ branchToBlock(lhs, rhs, mirTrue, cond);
|
|
+ } else {
|
|
+ branchToBlock(lhs, rhs, mirFalse, Assembler::InvertCondition(cond));
|
|
+ jumpToBlock(mirTrue);
|
|
+ }
|
|
+ }
|
|
+ void testZeroEmitBranch(Assembler::Condition cond, Register reg,
|
|
+ MBasicBlock* ifTrue, MBasicBlock* ifFalse)
|
|
+ {
|
|
+ emitBranch(reg, Imm32(0), cond, ifTrue, ifFalse);
|
|
+ }
|
|
+
|
|
+ void emitBigIntDiv(LBigIntDiv*, Register, Register, Register, Label*);
|
|
+ void emitBigIntMod(LBigIntMod*, Register, Register, Register, Label*);
|
|
+
|
|
+ void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base);
|
|
+
|
|
+ template <typename T>
|
|
+ void emitWasmLoad(T* ins);
|
|
+ template <typename T>
|
|
+ void emitWasmStore(T* ins);
|
|
+
|
|
+ void generateInvalidateEpilogue();
|
|
+
|
|
+ // Generating a result.
|
|
+ template<typename S, typename T>
|
|
+ void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value,
|
|
+ const T& mem, Register flagTemp, Register outTemp,
|
|
+ Register valueTemp, Register offsetTemp, Register maskTemp,
|
|
+ AnyRegister output);
|
|
+
|
|
+ // Generating no result.
|
|
+ template<typename S, typename T>
|
|
+ void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value,
|
|
+ const T& mem, Register flagTemp, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp);
|
|
+
|
|
+ public:
|
|
+ // Out of line visitors.
|
|
+ void visitOutOfLineBailout(OutOfLineBailout* ool);
|
|
+ void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool);
|
|
+ void visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCheck* ool);
|
|
+
|
|
+ protected:
|
|
+ void testNullEmitBranch(Assembler::Condition cond, const ValueOperand& value,
|
|
+ MBasicBlock* ifTrue, MBasicBlock* ifFalse)
|
|
+ {
|
|
+ MOZ_ASSERT(value.valueReg() != SecondScratchReg);
|
|
+ masm.splitTag(value.valueReg(), SecondScratchReg);
|
|
+ emitBranch(SecondScratchReg, ImmTag(JSVAL_TAG_NULL), cond, ifTrue, ifFalse);
|
|
+ }
|
|
+ void testUndefinedEmitBranch(Assembler::Condition cond, const ValueOperand& value,
|
|
+ MBasicBlock* ifTrue, MBasicBlock* ifFalse)
|
|
+ {
|
|
+ MOZ_ASSERT(value.valueReg() != SecondScratchReg);
|
|
+ masm.splitTag(value.valueReg(), SecondScratchReg);
|
|
+ emitBranch(SecondScratchReg, ImmTag(JSVAL_TAG_UNDEFINED), cond, ifTrue, ifFalse);
|
|
+ }
|
|
+ void testObjectEmitBranch(Assembler::Condition cond, const ValueOperand& value,
|
|
+ MBasicBlock* ifTrue, MBasicBlock* ifFalse)
|
|
+ {
|
|
+ MOZ_ASSERT(value.valueReg() != SecondScratchReg);
|
|
+ masm.splitTag(value.valueReg(), SecondScratchReg);
|
|
+ emitBranch(SecondScratchReg, ImmTag(JSVAL_TAG_OBJECT), cond, ifTrue, ifFalse);
|
|
+ }
|
|
+
|
|
+
|
|
+ template <typename T>
|
|
+ void emitWasmLoadI64(T* ins);
|
|
+ template <typename T>
|
|
+ void emitWasmStoreI64(T* ins);
|
|
+
|
|
+ ValueOperand ToValue(LInstruction* ins, size_t pos);
|
|
+ ValueOperand ToTempValue(LInstruction* ins, size_t pos);
|
|
+
|
|
+ // Functions for LTestVAndBranch.
|
|
+ void splitTagForTest(const ValueOperand& value, ScratchTagScope& tag);
|
|
+};
|
|
+
|
|
+typedef CodeGeneratorPPC64 CodeGeneratorSpecific;
|
|
+
|
|
+// An out-of-line bailout thunk.
|
|
+class OutOfLineBailout : public OutOfLineCodeBase<CodeGeneratorPPC64>
|
|
+{
|
|
+ LSnapshot* snapshot_;
|
|
+ uint32_t frameSize_;
|
|
+
|
|
+ public:
|
|
+ OutOfLineBailout(LSnapshot* snapshot, uint32_t frameSize)
|
|
+ : snapshot_(snapshot),
|
|
+ frameSize_(frameSize)
|
|
+ { }
|
|
+
|
|
+ void accept(CodeGeneratorPPC64* codegen) override;
|
|
+
|
|
+ LSnapshot* snapshot() const {
|
|
+ return snapshot_;
|
|
+ }
|
|
+};
|
|
+
|
|
+} // namespace jit
|
|
+} // namespace js
|
|
+
|
|
+#endif /* jit_PPC64_CodeGenerator_PPC64_h */
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/LIR-ppc64.h
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/LIR-ppc64.h Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,611 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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_ppc64le_LIR_ppc64le_h
|
|
+#define jit_ppc64le_LIR_ppc64le_h
|
|
+
|
|
+namespace js {
|
|
+namespace jit {
|
|
+
|
|
+class LUnbox : public LInstructionHelper<1, 1, 0>
|
|
+{
|
|
+ protected:
|
|
+ LUnbox(LNode::Opcode opcode, const LAllocation& input)
|
|
+ : LInstructionHelper(opcode)
|
|
+ {
|
|
+ setOperand(0, input);
|
|
+ }
|
|
+
|
|
+ public:
|
|
+ LIR_HEADER(Unbox);
|
|
+
|
|
+ explicit LUnbox(const LAllocation& input)
|
|
+ : LInstructionHelper(classOpcode)
|
|
+ {
|
|
+ setOperand(0, input);
|
|
+ }
|
|
+
|
|
+ static const size_t Input = 0;
|
|
+
|
|
+ MUnbox* mir() const {
|
|
+ return mir_->toUnbox();
|
|
+ }
|
|
+ const char* extraName() const {
|
|
+ return StringFromMIRType(mir()->type());
|
|
+ }
|
|
+};
|
|
+
|
|
+class LUnboxFloatingPoint : public LUnbox
|
|
+{
|
|
+ MIRType type_;
|
|
+
|
|
+ public:
|
|
+ LIR_HEADER(UnboxFloatingPoint);
|
|
+
|
|
+ LUnboxFloatingPoint(const LAllocation& input, MIRType type)
|
|
+ : LUnbox(classOpcode, input),
|
|
+ type_(type)
|
|
+ { }
|
|
+
|
|
+ MIRType type() const {
|
|
+ return type_;
|
|
+ }
|
|
+};
|
|
+
|
|
+class LDivOrModI64 : public LBinaryMath<1>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(DivOrModI64)
|
|
+
|
|
+ LDivOrModI64(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp)
|
|
+ : LBinaryMath(classOpcode)
|
|
+ {
|
|
+ setOperand(0, lhs);
|
|
+ setOperand(1, rhs);
|
|
+ setTemp(0, temp);
|
|
+ }
|
|
+
|
|
+ const LDefinition* remainder() {
|
|
+ return getTemp(0);
|
|
+ }
|
|
+ MBinaryArithInstruction* mir() const {
|
|
+ MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
|
+ return static_cast<MBinaryArithInstruction*>(mir_);
|
|
+ }
|
|
+
|
|
+ bool canBeDivideByZero() const {
|
|
+ if (mir_->isMod())
|
|
+ return mir_->toMod()->canBeDivideByZero();
|
|
+ return mir_->toDiv()->canBeDivideByZero();
|
|
+ }
|
|
+ bool canBeNegativeOverflow() const {
|
|
+ if (mir_->isMod())
|
|
+ return mir_->toMod()->canBeNegativeDividend();
|
|
+ return mir_->toDiv()->canBeNegativeOverflow();
|
|
+ }
|
|
+ wasm::BytecodeOffset bytecodeOffset() const {
|
|
+ MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
|
+ if (mir_->isMod())
|
|
+ return mir_->toMod()->bytecodeOffset();
|
|
+ return mir_->toDiv()->bytecodeOffset();
|
|
+ }
|
|
+};
|
|
+
|
|
+class LUDivOrModI64 : public LBinaryMath<1>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(UDivOrModI64);
|
|
+
|
|
+ LUDivOrModI64(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp)
|
|
+ : LBinaryMath(classOpcode)
|
|
+ {
|
|
+ setOperand(0, lhs);
|
|
+ setOperand(1, rhs);
|
|
+ setTemp(0, temp);
|
|
+ }
|
|
+
|
|
+ const LDefinition* remainder() {
|
|
+ return getTemp(0);
|
|
+ }
|
|
+ const char* extraName() const {
|
|
+ return mir()->isTruncated() ? "Truncated" : nullptr;
|
|
+ }
|
|
+
|
|
+ MBinaryArithInstruction* mir() const {
|
|
+ MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
|
+ return static_cast<MBinaryArithInstruction*>(mir_);
|
|
+ }
|
|
+ bool canBeDivideByZero() const {
|
|
+ if (mir_->isMod())
|
|
+ return mir_->toMod()->canBeDivideByZero();
|
|
+ return mir_->toDiv()->canBeDivideByZero();
|
|
+ }
|
|
+ wasm::BytecodeOffset bytecodeOffset() const {
|
|
+ MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
|
+ if (mir_->isMod())
|
|
+ return mir_->toMod()->bytecodeOffset();
|
|
+ return mir_->toDiv()->bytecodeOffset();
|
|
+ }
|
|
+};
|
|
+
|
|
+class LWasmTruncateToInt64 : public LInstructionHelper<1, 1, 0>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(WasmTruncateToInt64);
|
|
+
|
|
+ explicit LWasmTruncateToInt64(const LAllocation& in)
|
|
+ : LInstructionHelper(classOpcode)
|
|
+ {
|
|
+ setOperand(0, in);
|
|
+ }
|
|
+
|
|
+ MWasmTruncateToInt64* mir() const {
|
|
+ return mir_->toWasmTruncateToInt64();
|
|
+ }
|
|
+};
|
|
+
|
|
+class LInt64ToFloatingPoint : public LInstructionHelper<1, 1, 0>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(Int64ToFloatingPoint);
|
|
+
|
|
+ explicit LInt64ToFloatingPoint(const LInt64Allocation& in)
|
|
+ : LInstructionHelper(classOpcode)
|
|
+ {
|
|
+ setInt64Operand(0, in);
|
|
+ }
|
|
+
|
|
+ MInt64ToFloatingPoint* mir() const {
|
|
+ return mir_->toInt64ToFloatingPoint();
|
|
+ }
|
|
+};
|
|
+
|
|
+// Convert a 32-bit unsigned integer to a double.
|
|
+class LWasmUint32ToDouble : public LInstructionHelper<1, 1, 0>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(WasmUint32ToDouble)
|
|
+
|
|
+ LWasmUint32ToDouble(const LAllocation& input)
|
|
+ : LInstructionHelper(classOpcode)
|
|
+ {
|
|
+ setOperand(0, input);
|
|
+ }
|
|
+};
|
|
+
|
|
+// Convert a 32-bit unsigned integer to a float32.
|
|
+class LWasmUint32ToFloat32 : public LInstructionHelper<1, 1, 0>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(WasmUint32ToFloat32)
|
|
+
|
|
+ LWasmUint32ToFloat32(const LAllocation& input)
|
|
+ : LInstructionHelper(classOpcode)
|
|
+ {
|
|
+ setOperand(0, input);
|
|
+ }
|
|
+};
|
|
+
|
|
+
|
|
+class LDivI : public LBinaryMath<1>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(DivI);
|
|
+
|
|
+ LDivI(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp)
|
|
+ : LBinaryMath(classOpcode)
|
|
+ {
|
|
+ setOperand(0, lhs);
|
|
+ setOperand(1, rhs);
|
|
+ setTemp(0, temp);
|
|
+ }
|
|
+
|
|
+ MDiv* mir() const {
|
|
+ return mir_->toDiv();
|
|
+ }
|
|
+};
|
|
+
|
|
+class LDivPowTwoI : public LInstructionHelper<1, 1, 1>
|
|
+{
|
|
+ const int32_t shift_;
|
|
+
|
|
+ public:
|
|
+ LIR_HEADER(DivPowTwoI)
|
|
+
|
|
+ LDivPowTwoI(const LAllocation& lhs, int32_t shift, const LDefinition& temp)
|
|
+ : LInstructionHelper(classOpcode),
|
|
+ shift_(shift)
|
|
+ {
|
|
+ setOperand(0, lhs);
|
|
+ setTemp(0, temp);
|
|
+ }
|
|
+
|
|
+ const LAllocation* numerator() {
|
|
+ return getOperand(0);
|
|
+ }
|
|
+ int32_t shift() const {
|
|
+ return shift_;
|
|
+ }
|
|
+ MDiv* mir() const {
|
|
+ return mir_->toDiv();
|
|
+ }
|
|
+};
|
|
+
|
|
+class LModI : public LBinaryMath<1>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(ModI);
|
|
+
|
|
+ LModI(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& callTemp)
|
|
+ : LBinaryMath(classOpcode)
|
|
+ {
|
|
+ setOperand(0, lhs);
|
|
+ setOperand(1, rhs);
|
|
+ setTemp(0, callTemp);
|
|
+ }
|
|
+
|
|
+ const LDefinition* callTemp() {
|
|
+ return getTemp(0);
|
|
+ }
|
|
+ MMod* mir() const {
|
|
+ return mir_->toMod();
|
|
+ }
|
|
+};
|
|
+
|
|
+class LModPowTwoI : public LInstructionHelper<1, 1, 0>
|
|
+{
|
|
+ const int32_t shift_;
|
|
+
|
|
+ public:
|
|
+ LIR_HEADER(ModPowTwoI);
|
|
+
|
|
+ LModPowTwoI(const LAllocation& lhs, int32_t shift)
|
|
+ : LInstructionHelper(classOpcode),
|
|
+ shift_(shift)
|
|
+ {
|
|
+ setOperand(0, lhs);
|
|
+ }
|
|
+
|
|
+ int32_t shift() const {
|
|
+ return shift_;
|
|
+ }
|
|
+ MMod* mir() const {
|
|
+ return mir_->toMod();
|
|
+ }
|
|
+};
|
|
+
|
|
+class LModMaskI : public LInstructionHelper<1, 1, 2>
|
|
+{
|
|
+ const int32_t shift_;
|
|
+
|
|
+ public:
|
|
+ LIR_HEADER(ModMaskI);
|
|
+
|
|
+ LModMaskI(const LAllocation& lhs, const LDefinition& temp0, const LDefinition& temp1,
|
|
+ int32_t shift)
|
|
+ : LInstructionHelper(classOpcode),
|
|
+ shift_(shift)
|
|
+ {
|
|
+ setOperand(0, lhs);
|
|
+ setTemp(0, temp0);
|
|
+ setTemp(1, temp1);
|
|
+ }
|
|
+
|
|
+ int32_t shift() const {
|
|
+ return shift_;
|
|
+ }
|
|
+ MMod* mir() const {
|
|
+ return mir_->toMod();
|
|
+ }
|
|
+};
|
|
+
|
|
+// Takes a tableswitch with an integer to decide
|
|
+class LTableSwitch : public LInstructionHelper<0, 1, 2>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(TableSwitch);
|
|
+
|
|
+ LTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
|
|
+ const LDefinition& jumpTablePointer, MTableSwitch* ins)
|
|
+ : LInstructionHelper(classOpcode)
|
|
+ {
|
|
+ setOperand(0, in);
|
|
+ setTemp(0, inputCopy);
|
|
+ setTemp(1, jumpTablePointer);
|
|
+ setMir(ins);
|
|
+ }
|
|
+
|
|
+ MTableSwitch* mir() const {
|
|
+ return mir_->toTableSwitch();
|
|
+ }
|
|
+ const LAllocation* index() {
|
|
+ return getOperand(0);
|
|
+ }
|
|
+ const LDefinition* tempInt() {
|
|
+ return getTemp(0);
|
|
+ }
|
|
+ // This is added to share the same CodeGenerator prefixes.
|
|
+ const LDefinition* tempPointer() {
|
|
+ return getTemp(1);
|
|
+ }
|
|
+};
|
|
+
|
|
+// Takes a tableswitch with an integer to decide
|
|
+class LTableSwitchV : public LInstructionHelper<0, BOX_PIECES, 3>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(TableSwitchV);
|
|
+
|
|
+ LTableSwitchV(const LBoxAllocation& input, const LDefinition& inputCopy,
|
|
+ const LDefinition& floatCopy, const LDefinition& jumpTablePointer,
|
|
+ MTableSwitch* ins)
|
|
+ : LInstructionHelper(classOpcode)
|
|
+ {
|
|
+ setBoxOperand(InputValue, input);
|
|
+ setTemp(0, inputCopy);
|
|
+ setTemp(1, floatCopy);
|
|
+ setTemp(2, jumpTablePointer);
|
|
+ setMir(ins);
|
|
+ }
|
|
+
|
|
+ MTableSwitch* mir() const {
|
|
+ return mir_->toTableSwitch();
|
|
+ }
|
|
+
|
|
+ static const size_t InputValue = 0;
|
|
+
|
|
+ const LDefinition* tempInt() {
|
|
+ return getTemp(0);
|
|
+ }
|
|
+ const LDefinition* tempFloat() {
|
|
+ return getTemp(1);
|
|
+ }
|
|
+ const LDefinition* tempPointer() {
|
|
+ return getTemp(2);
|
|
+ }
|
|
+};
|
|
+
|
|
+class LMulI : public LBinaryMath<0>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(MulI);
|
|
+
|
|
+ LMulI()
|
|
+ : LBinaryMath(classOpcode)
|
|
+ {}
|
|
+
|
|
+ MMul* mir() {
|
|
+ return mir_->toMul();
|
|
+ }
|
|
+};
|
|
+
|
|
+class LUDivOrMod : public LBinaryMath<0>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(UDivOrMod);
|
|
+
|
|
+ LUDivOrMod()
|
|
+ : LBinaryMath(classOpcode)
|
|
+ {}
|
|
+
|
|
+ MBinaryArithInstruction* mir() const {
|
|
+ MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
|
+ return static_cast<MBinaryArithInstruction*>(mir_);
|
|
+ }
|
|
+
|
|
+ bool canBeDivideByZero() const {
|
|
+ if (mir_->isMod())
|
|
+ return mir_->toMod()->canBeDivideByZero();
|
|
+ return mir_->toDiv()->canBeDivideByZero();
|
|
+ }
|
|
+
|
|
+ bool trapOnError() const {
|
|
+ if (mir_->isMod())
|
|
+ return mir_->toMod()->trapOnError();
|
|
+ return mir_->toDiv()->trapOnError();
|
|
+ }
|
|
+
|
|
+ wasm::BytecodeOffset bytecodeOffset() const {
|
|
+ MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
|
|
+ if (mir_->isMod())
|
|
+ return mir_->toMod()->bytecodeOffset();
|
|
+ return mir_->toDiv()->bytecodeOffset();
|
|
+ }
|
|
+};
|
|
+
|
|
+namespace details {
|
|
+
|
|
+// Base class for the int64 and non-int64 variants.
|
|
+template<size_t NumDefs>
|
|
+class LWasmUnalignedLoadBase : public details::LWasmLoadBase<NumDefs, 2>
|
|
+{
|
|
+ public:
|
|
+ typedef LWasmLoadBase<NumDefs, 2> Base;
|
|
+
|
|
+ explicit LWasmUnalignedLoadBase(LNode::Opcode opcode, const LAllocation& ptr,
|
|
+ const LDefinition& valueHelper)
|
|
+ : Base(opcode, ptr, LAllocation())
|
|
+ {
|
|
+ Base::setTemp(0, LDefinition::BogusTemp());
|
|
+ Base::setTemp(1, valueHelper);
|
|
+ }
|
|
+
|
|
+ const LAllocation* ptr() {
|
|
+ return Base::getOperand(0);
|
|
+ }
|
|
+ const LDefinition* ptrCopy() {
|
|
+ return Base::getTemp(0);
|
|
+ }
|
|
+};
|
|
+
|
|
+} // namespace details
|
|
+
|
|
+class LWasmUnalignedLoad : public details::LWasmUnalignedLoadBase<1>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(WasmUnalignedLoad);
|
|
+
|
|
+ explicit LWasmUnalignedLoad(const LAllocation& ptr, const LDefinition& valueHelper)
|
|
+ : LWasmUnalignedLoadBase(classOpcode, ptr, valueHelper)
|
|
+ {}
|
|
+};
|
|
+
|
|
+class LWasmUnalignedLoadI64 : public details::LWasmUnalignedLoadBase<INT64_PIECES>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(WasmUnalignedLoadI64);
|
|
+
|
|
+ explicit LWasmUnalignedLoadI64(const LAllocation& ptr, const LDefinition& valueHelper)
|
|
+ : LWasmUnalignedLoadBase(classOpcode, ptr, valueHelper)
|
|
+ {}
|
|
+};
|
|
+
|
|
+namespace details {
|
|
+
|
|
+// Base class for the int64 and non-int64 variants.
|
|
+template<size_t NumOps>
|
|
+class LWasmUnalignedStoreBase : public LInstructionHelper<0, NumOps, 2>
|
|
+{
|
|
+ public:
|
|
+ typedef LInstructionHelper<0, NumOps, 2> Base;
|
|
+
|
|
+ static const size_t PtrIndex = 0;
|
|
+ static const size_t ValueIndex = 1;
|
|
+
|
|
+ LWasmUnalignedStoreBase(LNode::Opcode opcode, const LAllocation& ptr,
|
|
+ const LDefinition& valueHelper)
|
|
+ : Base(opcode)
|
|
+ {
|
|
+ Base::setOperand(0, ptr);
|
|
+ Base::setTemp(0, LDefinition::BogusTemp());
|
|
+ Base::setTemp(1, valueHelper);
|
|
+ }
|
|
+
|
|
+ MWasmStore* mir() const {
|
|
+ return Base::mir_->toWasmStore();
|
|
+ }
|
|
+ const LAllocation* ptr() {
|
|
+ return Base::getOperand(PtrIndex);
|
|
+ }
|
|
+ const LDefinition* ptrCopy() {
|
|
+ return Base::getTemp(0);
|
|
+ }
|
|
+};
|
|
+
|
|
+} // namespace details
|
|
+
|
|
+class LWasmUnalignedStore : public details::LWasmUnalignedStoreBase<2>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(WasmUnalignedStore);
|
|
+
|
|
+ LWasmUnalignedStore(const LAllocation& ptr, const LAllocation& value,
|
|
+ const LDefinition& valueHelper)
|
|
+ : LWasmUnalignedStoreBase(classOpcode, ptr, valueHelper)
|
|
+ {
|
|
+ setOperand(1, value);
|
|
+ }
|
|
+
|
|
+ const LAllocation* value() {
|
|
+ return Base::getOperand(ValueIndex);
|
|
+ }
|
|
+};
|
|
+
|
|
+class LWasmUnalignedStoreI64 : public details::LWasmUnalignedStoreBase<1 + INT64_PIECES>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(WasmUnalignedStoreI64);
|
|
+ LWasmUnalignedStoreI64(const LAllocation& ptr, const LInt64Allocation& value,
|
|
+ const LDefinition& valueHelper)
|
|
+ : LWasmUnalignedStoreBase(classOpcode, ptr, valueHelper)
|
|
+ {
|
|
+ setInt64Operand(1, value);
|
|
+ }
|
|
+
|
|
+ const LInt64Allocation value() {
|
|
+ return getInt64Operand(ValueIndex);
|
|
+ }
|
|
+};
|
|
+
|
|
+class LWasmCompareExchangeI64 : public LInstructionHelper<INT64_PIECES, 1 + INT64_PIECES + INT64_PIECES, 0>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(WasmCompareExchangeI64);
|
|
+
|
|
+ LWasmCompareExchangeI64(const LAllocation& ptr, const LInt64Allocation& oldValue,
|
|
+ const LInt64Allocation& newValue)
|
|
+ : LInstructionHelper(classOpcode)
|
|
+ {
|
|
+ setOperand(0, ptr);
|
|
+ setInt64Operand(1, oldValue);
|
|
+ setInt64Operand(1 + INT64_PIECES, newValue);
|
|
+ }
|
|
+
|
|
+ const LAllocation* ptr() {
|
|
+ return getOperand(0);
|
|
+ }
|
|
+ const LInt64Allocation oldValue() {
|
|
+ return getInt64Operand(1);
|
|
+ }
|
|
+ const LInt64Allocation newValue() {
|
|
+ return getInt64Operand(1 + INT64_PIECES);
|
|
+ }
|
|
+ const MWasmCompareExchangeHeap* mir() const {
|
|
+ return mir_->toWasmCompareExchangeHeap();
|
|
+ }
|
|
+};
|
|
+
|
|
+class LWasmAtomicExchangeI64 : public LInstructionHelper<INT64_PIECES, 1 + INT64_PIECES, 0>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(WasmAtomicExchangeI64);
|
|
+
|
|
+ LWasmAtomicExchangeI64(const LAllocation& ptr, const LInt64Allocation& value)
|
|
+ : LInstructionHelper(classOpcode)
|
|
+ {
|
|
+ setOperand(0, ptr);
|
|
+ setInt64Operand(1, value);
|
|
+ }
|
|
+
|
|
+ const LAllocation* ptr() {
|
|
+ return getOperand(0);
|
|
+ }
|
|
+ const LInt64Allocation value() {
|
|
+ return getInt64Operand(1);
|
|
+ }
|
|
+ const MWasmAtomicExchangeHeap* mir() const {
|
|
+ return mir_->toWasmAtomicExchangeHeap();
|
|
+ }
|
|
+};
|
|
+
|
|
+class LWasmAtomicBinopI64 : public LInstructionHelper<INT64_PIECES, 1 + INT64_PIECES, 2>
|
|
+{
|
|
+ public:
|
|
+ LIR_HEADER(WasmAtomicBinopI64);
|
|
+
|
|
+ LWasmAtomicBinopI64(const LAllocation& ptr, const LInt64Allocation& value)
|
|
+ : LInstructionHelper(classOpcode)
|
|
+ {
|
|
+ setOperand(0, ptr);
|
|
+ setInt64Operand(1, value);
|
|
+ }
|
|
+
|
|
+ const LAllocation* ptr() {
|
|
+ return getOperand(0);
|
|
+ }
|
|
+ const LInt64Allocation value() {
|
|
+ return getInt64Operand(1);
|
|
+ }
|
|
+ const MWasmAtomicBinopHeap* mir() const {
|
|
+ return mir_->toWasmAtomicBinopHeap();
|
|
+ }
|
|
+};
|
|
+
|
|
+
|
|
+} // namespace jit
|
|
+} // namespace js
|
|
+
|
|
+#endif /* jit_ppc64le_LIR_ppc64le_h */
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/Lowering-ppc64.cpp
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/Lowering-ppc64.cpp Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,1289 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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/. */
|
|
+
|
|
+#include "mozilla/MathAlgorithms.h"
|
|
+
|
|
+#include "jit/Lowering.h"
|
|
+#include "jit/MIR.h"
|
|
+#include "jit/ppc64/Assembler-ppc64.h"
|
|
+
|
|
+#include "jit/shared/Lowering-shared-inl.h"
|
|
+
|
|
+using namespace js;
|
|
+using namespace js::jit;
|
|
+
|
|
+using mozilla::FloorLog2;
|
|
+
|
|
+LAllocation
|
|
+LIRGeneratorPPC64::useByteOpRegister(MDefinition* mir)
|
|
+{
|
|
+ return useRegister(mir);
|
|
+}
|
|
+
|
|
+LAllocation
|
|
+LIRGeneratorPPC64::useByteOpRegisterAtStart(MDefinition* mir)
|
|
+{
|
|
+ return useRegisterAtStart(mir);
|
|
+}
|
|
+
|
|
+LAllocation
|
|
+LIRGeneratorPPC64::useByteOpRegisterOrNonDoubleConstant(MDefinition* mir)
|
|
+{
|
|
+ return useRegisterOrNonDoubleConstant(mir);
|
|
+}
|
|
+
|
|
+LDefinition
|
|
+LIRGeneratorPPC64::tempByteOpRegister()
|
|
+{
|
|
+ return temp();
|
|
+}
|
|
+
|
|
+// x = !y
|
|
+void
|
|
+LIRGeneratorPPC64::lowerForALU(LInstructionHelper<1, 1, 0>* ins,
|
|
+ MDefinition* mir, MDefinition* input)
|
|
+{
|
|
+ ins->setOperand(0, useRegister(input));
|
|
+ define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
|
|
+}
|
|
+
|
|
+// z = x+y
|
|
+void
|
|
+LIRGeneratorPPC64::lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
|
|
+ MDefinition* lhs, MDefinition* rhs)
|
|
+{
|
|
+ ins->setOperand(0, useRegister(lhs));
|
|
+ ins->setOperand(1, useRegisterOrConstant(rhs));
|
|
+ define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerForALUInt64(
|
|
+ LInstructionHelper<INT64_PIECES, INT64_PIECES, 0>* ins, MDefinition* mir,
|
|
+ MDefinition* input) {
|
|
+ ins->setInt64Operand(0, useInt64RegisterAtStart(input));
|
|
+ defineInt64ReuseInput(ins, mir, 0);
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerForALUInt64(
|
|
+ LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
|
|
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs) {
|
|
+ ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
|
|
+ ins->setInt64Operand(INT64_PIECES, willHaveDifferentLIRNodes(lhs, rhs)
|
|
+ ? useInt64OrConstant(rhs)
|
|
+ : useInt64OrConstantAtStart(rhs));
|
|
+ defineInt64ReuseInput(ins, mir, 0);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerForMulInt64(LMulI64* ins, MMul* mir, MDefinition* lhs, MDefinition* rhs)
|
|
+{
|
|
+// XXX: simplify this because these are all static conditions
|
|
+ bool needsTemp = false;
|
|
+ bool cannotAliasRhs = false;
|
|
+ bool reuseInput = true;
|
|
+
|
|
+/*
|
|
+ ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
|
|
+ ins->setInt64Operand(INT64_PIECES,
|
|
+ (lhs != rhs || cannotAliasRhs) ? useInt64OrConstant(rhs) : useInt64OrConstantAtStart(rhs));
|
|
+*/
|
|
+ ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
|
|
+ ins->setInt64Operand(INT64_PIECES,
|
|
+ (willHaveDifferentLIRNodes(lhs, rhs) || cannotAliasRhs)
|
|
+ ? useInt64OrConstant(rhs)
|
|
+ : useInt64OrConstantAtStart(rhs));
|
|
+
|
|
+ if (needsTemp)
|
|
+ ins->setTemp(0, temp());
|
|
+ if(reuseInput)
|
|
+ defineInt64ReuseInput(ins, mir, 0);
|
|
+ else
|
|
+ defineInt64(ins, mir);
|
|
+}
|
|
+
|
|
+template<size_t Temps>
|
|
+void
|
|
+LIRGeneratorPPC64::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, Temps>* ins,
|
|
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
|
|
+{
|
|
+ ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
|
|
+
|
|
+ static_assert(LShiftI64::Rhs == INT64_PIECES, "Assume Rhs is located at INT64_PIECES.");
|
|
+ static_assert(LRotateI64::Count == INT64_PIECES, "Assume Count is located at INT64_PIECES.");
|
|
+
|
|
+ ins->setOperand(INT64_PIECES, useRegisterOrConstant(rhs));
|
|
+
|
|
+ defineInt64ReuseInput(ins, mir, 0);
|
|
+}
|
|
+
|
|
+template void LIRGeneratorPPC64::lowerForShiftInt64(
|
|
+ LInstructionHelper<INT64_PIECES, INT64_PIECES+1, 0>* ins, MDefinition* mir,
|
|
+ MDefinition* lhs, MDefinition* rhs);
|
|
+template void LIRGeneratorPPC64::lowerForShiftInt64(
|
|
+ LInstructionHelper<INT64_PIECES, INT64_PIECES+1, 1>* ins, MDefinition* mir,
|
|
+ MDefinition* lhs, MDefinition* rhs);
|
|
+
|
|
+void LIRGeneratorPPC64::lowerForCompareI64AndBranch(MTest* mir, MCompare* comp,
|
|
+ JSOp op, MDefinition* left,
|
|
+ MDefinition* right,
|
|
+ MBasicBlock* ifTrue,
|
|
+ MBasicBlock* ifFalse) {
|
|
+ auto* lir = new (alloc())
|
|
+ LCompareI64AndBranch(comp, op, useInt64Register(left),
|
|
+ useInt64RegisterOrConstant(right), ifTrue, ifFalse);
|
|
+ add(lir, mir);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
|
|
+ MDefinition* input)
|
|
+{
|
|
+ ins->setOperand(0, useRegister(input));
|
|
+ define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
|
|
+}
|
|
+
|
|
+template<size_t Temps>
|
|
+void
|
|
+LIRGeneratorPPC64::lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
|
|
+ MDefinition* lhs, MDefinition* rhs)
|
|
+{
|
|
+ ins->setOperand(0, useRegister(lhs));
|
|
+ ins->setOperand(1, useRegister(rhs));
|
|
+ define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
|
|
+}
|
|
+
|
|
+template void LIRGeneratorPPC64::lowerForFPU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
|
|
+ MDefinition* lhs, MDefinition* rhs);
|
|
+template void LIRGeneratorPPC64::lowerForFPU(LInstructionHelper<1, 2, 1>* ins, MDefinition* mir,
|
|
+ MDefinition* lhs, MDefinition* rhs);
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
|
|
+ MDefinition* lhs, MDefinition* rhs)
|
|
+{
|
|
+ baab->setOperand(0, useRegisterAtStart(lhs));
|
|
+ baab->setOperand(1, useRegisterOrConstantAtStart(rhs));
|
|
+ add(baab, mir);
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerWasmBuiltinTruncateToInt32(
|
|
+ MWasmBuiltinTruncateToInt32* ins) {
|
|
+/*
|
|
+ MDefinition* opd = ins->input();
|
|
+ MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32);
|
|
+
|
|
+ if (opd->type() == MIRType::Double) {
|
|
+ define(new (alloc()) LWasmBuiltinTruncateDToInt32(
|
|
+ useRegister(opd), useFixed(ins->tls(), WasmTlsReg),
|
|
+ LDefinition::BogusTemp()),
|
|
+ ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ define(new (alloc()) LWasmBuiltinTruncateFToInt32(
|
|
+ useRegister(opd), useFixed(ins->tls(), WasmTlsReg),
|
|
+ LDefinition::BogusTemp()),
|
|
+ ins);
|
|
+*/
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
|
|
+ MDefinition* lhs, MDefinition* rhs)
|
|
+{
|
|
+ ins->setOperand(0, useRegister(lhs));
|
|
+ ins->setOperand(1, useRegisterOrConstant(rhs));
|
|
+ define(ins, mir);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerDivI(MDiv* div)
|
|
+{
|
|
+ if (div->isUnsigned()) {
|
|
+ lowerUDiv(div);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Division instructions are slow. Division by constant denominators can be
|
|
+ // rewritten to use other instructions.
|
|
+ if (div->rhs()->isConstant()) {
|
|
+ int32_t rhs = div->rhs()->toConstant()->toInt32();
|
|
+ // Check for division by a positive power of two, which is an easy and
|
|
+ // important case to optimize. Note that other optimizations are also
|
|
+ // possible; division by negative powers of two can be optimized in a
|
|
+ // similar manner as positive powers of two, and division by other
|
|
+ // constants can be optimized by a reciprocal multiplication technique.
|
|
+ int32_t shift = FloorLog2(rhs);
|
|
+ if (rhs > 0 && 1 << shift == rhs) {
|
|
+ LDivPowTwoI* lir = new(alloc()) LDivPowTwoI(useRegister(div->lhs()), shift, temp());
|
|
+ if (div->fallible())
|
|
+ assignSnapshot(lir, div->bailoutKind());
|
|
+ define(lir, div);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ LDivI* lir = new(alloc()) LDivI(useRegister(div->lhs()), useRegister(div->rhs()), temp());
|
|
+ if (div->fallible())
|
|
+ assignSnapshot(lir, div->bailoutKind());
|
|
+ define(lir, div);
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerNegI(MInstruction* ins, MDefinition* input) {
|
|
+ define(new (alloc()) LNegI(useRegisterAtStart(input)), ins);
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerNegI64(MInstruction* ins, MDefinition* input) {
|
|
+ defineInt64(new (alloc()) LNegI64(useInt64RegisterAtStart(input)), ins);
|
|
+}
|
|
+
|
|
+void LIRGenerator::visitAbs(MAbs* ins) {
|
|
+ define(allocateAbs(ins, useRegisterAtStart(ins->input())), ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs)
|
|
+{
|
|
+ // Automatically reduced by xs_sr_mulli.
|
|
+ LMulI* lir = new(alloc()) LMulI;
|
|
+ if (mul->fallible())
|
|
+ assignSnapshot(lir, mul->bailoutKind());
|
|
+
|
|
+ lowerForALU(lir, mul, lhs, rhs);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerModI(MMod* mod)
|
|
+{
|
|
+ if (mod->isUnsigned()) {
|
|
+ lowerUMod(mod);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // POWER9 has hardware modulo, so we don't need to lower it there.
|
|
+ if (!HasPPCISA3() && mod->rhs()->isConstant()) {
|
|
+ int32_t rhs = mod->rhs()->toConstant()->toInt32();
|
|
+ int32_t shift = FloorLog2(rhs);
|
|
+ if (rhs > 0 && 1 << shift == rhs) {
|
|
+ LModPowTwoI* lir = new(alloc()) LModPowTwoI(useRegister(mod->lhs()), shift);
|
|
+ if (mod->fallible())
|
|
+ assignSnapshot(lir, mod->bailoutKind());
|
|
+ define(lir, mod);
|
|
+ return;
|
|
+ } else if (shift < 31 && (1 << (shift + 1)) - 1 == rhs) {
|
|
+ LModMaskI* lir = new(alloc()) LModMaskI(useRegister(mod->lhs()),
|
|
+ temp(LDefinition::GENERAL),
|
|
+ temp(LDefinition::GENERAL),
|
|
+ shift + 1);
|
|
+ if (mod->fallible())
|
|
+ assignSnapshot(lir, mod->bailoutKind());
|
|
+ define(lir, mod);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ LModI* lir = new(alloc()) LModI(useRegister(mod->lhs()), useRegister(mod->rhs()),
|
|
+ temp(LDefinition::GENERAL));
|
|
+
|
|
+ if (mod->fallible())
|
|
+ assignSnapshot(lir, mod->bailoutKind());
|
|
+ define(lir, mod);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitPowHalf(MPowHalf* ins)
|
|
+{
|
|
+ MDefinition* input = ins->input();
|
|
+ MOZ_ASSERT(input->type() == MIRType::Double);
|
|
+ LPowHalfD* lir = new(alloc()) LPowHalfD(useRegisterAtStart(input));
|
|
+ defineReuseInput(lir, ins, 0);
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerWasmSelectI(MWasmSelect* select) {
|
|
+/*
|
|
+ auto* lir = new (alloc())
|
|
+ LWasmSelect(useRegisterAtStart(select->trueExpr()),
|
|
+ useAny(select->falseExpr()), useRegister(select->condExpr()));
|
|
+ defineReuseInput(lir, select, LWasmSelect::TrueExprIndex);
|
|
+*/
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerWasmSelectI64(MWasmSelect* select) {
|
|
+/*
|
|
+ auto* lir = new (alloc()) LWasmSelectI64(
|
|
+ useInt64RegisterAtStart(select->trueExpr()),
|
|
+ useInt64(select->falseExpr()), useRegister(select->condExpr()));
|
|
+ defineInt64ReuseInput(lir, select, LWasmSelectI64::TrueExprIndex);
|
|
+*/
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+LTableSwitch*
|
|
+LIRGeneratorPPC64::newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
|
|
+ MTableSwitch* tableswitch)
|
|
+{
|
|
+ return new(alloc()) LTableSwitch(in, inputCopy, temp(), tableswitch);
|
|
+}
|
|
+
|
|
+LTableSwitchV*
|
|
+LIRGeneratorPPC64::newLTableSwitchV(MTableSwitch* tableswitch)
|
|
+{
|
|
+ return new(alloc()) LTableSwitchV(useBox(tableswitch->getOperand(0)),
|
|
+ temp(), tempDouble(), temp(), tableswitch);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerUrshD(MUrsh* mir)
|
|
+{
|
|
+ MDefinition* lhs = mir->lhs();
|
|
+ MDefinition* rhs = mir->rhs();
|
|
+
|
|
+ MOZ_ASSERT(lhs->type() == MIRType::Int32);
|
|
+ MOZ_ASSERT(rhs->type() == MIRType::Int32);
|
|
+
|
|
+ LUrshD* lir = new(alloc()) LUrshD(useRegister(lhs), useRegisterOrConstant(rhs), temp());
|
|
+ define(lir, mir);
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerPowOfTwoI(MPow* mir) {
|
|
+ int32_t base = mir->input()->toConstant()->toInt32();
|
|
+ MDefinition* power = mir->power();
|
|
+
|
|
+ auto* lir = new (alloc()) LPowOfTwoI(useRegister(power), base);
|
|
+ assignSnapshot(lir, mir->bailoutKind());
|
|
+ define(lir, mir);
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerBigIntLsh(MBigIntLsh* ins) {
|
|
+ auto* lir = new (alloc()) LBigIntLsh(
|
|
+ useRegister(ins->lhs()), useRegister(ins->rhs()), temp(), temp(), temp());
|
|
+ define(lir, ins);
|
|
+ assignSafepoint(lir, ins);
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerBigIntRsh(MBigIntRsh* ins) {
|
|
+ auto* lir = new (alloc()) LBigIntRsh(
|
|
+ useRegister(ins->lhs()), useRegister(ins->rhs()), temp(), temp(), temp());
|
|
+ define(lir, ins);
|
|
+ assignSafepoint(lir, ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitWasmNeg(MWasmNeg* ins)
|
|
+{
|
|
+ if (ins->type() == MIRType::Int32) {
|
|
+ define(new(alloc()) LNegI(useRegisterAtStart(ins->input())), ins);
|
|
+ } else if (ins->type() == MIRType::Float32) {
|
|
+ define(new(alloc()) LNegF(useRegisterAtStart(ins->input())), ins);
|
|
+ } else {
|
|
+ MOZ_ASSERT(ins->type() == MIRType::Double);
|
|
+ define(new(alloc()) LNegD(useRegisterAtStart(ins->input())), ins);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+void LIRGenerator::visitWasmHeapBase(MWasmHeapBase* ins) {
|
|
+ auto* lir = new (alloc()) LWasmHeapBase(LAllocation());
|
|
+ define(lir, ins);
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+*/
|
|
+
|
|
+void
|
|
+LIRGenerator::visitWasmLoad(MWasmLoad* ins)
|
|
+{
|
|
+ MDefinition* base = ins->base();
|
|
+ // |base| is a GPR but may be of either type. If it is 32-bit, it is
|
|
+ // sign-extended on 64-bit Power and we should explicitly promote it to
|
|
+ // 64-bit when used as an index register in memory accesses.
|
|
+ MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64);
|
|
+
|
|
+ LAllocation ptr = useRegisterAtStart(base);
|
|
+
|
|
+ if (IsUnaligned(ins->access())) {
|
|
+ if (ins->type() == MIRType::Int64) {
|
|
+ auto* lir = new(alloc()) LWasmUnalignedLoadI64(ptr, temp());
|
|
+ if (ins->access().offset())
|
|
+ lir->setTemp(0, tempCopy(base, 0));
|
|
+
|
|
+ defineInt64(lir, ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ auto* lir = new(alloc()) LWasmUnalignedLoad(ptr, temp());
|
|
+ if (ins->access().offset())
|
|
+ lir->setTemp(0, tempCopy(base, 0));
|
|
+
|
|
+ define(lir, ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (ins->type() == MIRType::Int64) {
|
|
+ auto* lir = new(alloc()) LWasmLoadI64(ptr);
|
|
+ if (ins->access().offset())
|
|
+ lir->setTemp(0, tempCopy(base, 0));
|
|
+
|
|
+ defineInt64(lir, ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ auto* lir = new(alloc()) LWasmLoad(ptr);
|
|
+ if (ins->access().offset())
|
|
+ lir->setTemp(0, tempCopy(base, 0));
|
|
+
|
|
+ define(lir, ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitWasmStore(MWasmStore* ins)
|
|
+{
|
|
+ MDefinition* base = ins->base();
|
|
+ // All about the two kinds of bases, no treble. Or trouble, maybe.
|
|
+ // How's that line go again?
|
|
+ MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64);
|
|
+
|
|
+ MDefinition* value = ins->value();
|
|
+
|
|
+ if (IsUnaligned(ins->access())) {
|
|
+ LAllocation baseAlloc = useRegisterAtStart(base);
|
|
+ if (ins->access().type() == Scalar::Int64) {
|
|
+ LInt64Allocation valueAlloc = useInt64RegisterAtStart(value);
|
|
+ auto* lir = new(alloc()) LWasmUnalignedStoreI64(baseAlloc, valueAlloc, temp());
|
|
+ if (ins->access().offset())
|
|
+ lir->setTemp(0, tempCopy(base, 0));
|
|
+
|
|
+ add(lir, ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ LAllocation valueAlloc = useRegisterAtStart(value);
|
|
+ auto* lir = new(alloc()) LWasmUnalignedStore(baseAlloc, valueAlloc, temp());
|
|
+ if (ins->access().offset())
|
|
+ lir->setTemp(0, tempCopy(base, 0));
|
|
+
|
|
+ add(lir, ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (ins->access().type() == Scalar::Int64) {
|
|
+ LAllocation baseAlloc = useRegisterAtStart(base);
|
|
+ LInt64Allocation valueAlloc = useInt64RegisterAtStart(value);
|
|
+ auto* lir = new(alloc()) LWasmStoreI64(baseAlloc, valueAlloc);
|
|
+ if (ins->access().offset())
|
|
+ lir->setTemp(0, tempCopy(base, 0));
|
|
+
|
|
+ add(lir, ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ LAllocation baseAlloc = useRegisterAtStart(base);
|
|
+ LAllocation valueAlloc = useRegisterAtStart(value);
|
|
+ auto* lir = new(alloc()) LWasmStore(baseAlloc, valueAlloc);
|
|
+ if (ins->access().offset())
|
|
+ lir->setTemp(0, tempCopy(base, 0));
|
|
+
|
|
+ add(lir, ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerUDiv(MDiv* div)
|
|
+{
|
|
+ MDefinition* lhs = div->getOperand(0);
|
|
+ MDefinition* rhs = div->getOperand(1);
|
|
+
|
|
+ LUDivOrMod* lir = new(alloc()) LUDivOrMod;
|
|
+ lir->setOperand(0, useRegister(lhs));
|
|
+ lir->setOperand(1, useRegister(rhs));
|
|
+ if (div->fallible())
|
|
+ assignSnapshot(lir, div->bailoutKind());
|
|
+
|
|
+ define(lir, div);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerUMod(MMod* mod)
|
|
+{
|
|
+ MDefinition* lhs = mod->getOperand(0);
|
|
+ MDefinition* rhs = mod->getOperand(1);
|
|
+
|
|
+ LUDivOrMod* lir = new(alloc()) LUDivOrMod;
|
|
+ lir->setOperand(0, useRegister(lhs));
|
|
+ lir->setOperand(1, useRegister(rhs));
|
|
+ if (mod->fallible())
|
|
+ assignSnapshot(lir, mod->bailoutKind());
|
|
+
|
|
+ define(lir, mod);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitWasmUnsignedToDouble(MWasmUnsignedToDouble* ins)
|
|
+{
|
|
+ MOZ_ASSERT(ins->input()->type() == MIRType::Int32);
|
|
+ LWasmUint32ToDouble* lir = new(alloc()) LWasmUint32ToDouble(useRegisterAtStart(ins->input()));
|
|
+ define(lir, ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitWasmUnsignedToFloat32(MWasmUnsignedToFloat32* ins)
|
|
+{
|
|
+ MOZ_ASSERT(ins->input()->type() == MIRType::Int32);
|
|
+ LWasmUint32ToFloat32* lir = new(alloc()) LWasmUint32ToFloat32(useRegisterAtStart(ins->input()));
|
|
+ define(lir, ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
|
|
+{
|
|
+ MOZ_ASSERT(ins->access().offset() == 0);
|
|
+
|
|
+ MDefinition* base = ins->base();
|
|
+ MOZ_ASSERT(base->type() == MIRType::Int32);
|
|
+ LAllocation baseAlloc;
|
|
+ LAllocation limitAlloc;
|
|
+ // We'd prefer base in a register or this will be slow.
|
|
+ if (base->isConstant() && !ins->needsBoundsCheck()) {
|
|
+ // A bounds check is only skipped for a positive index.
|
|
+ MOZ_ASSERT(base->toConstant()->toInt32() >= 0);
|
|
+ baseAlloc = LAllocation(base->toConstant());
|
|
+ } else {
|
|
+ baseAlloc = useRegisterAtStart(base);
|
|
+ if (ins->needsBoundsCheck()) {
|
|
+ MDefinition* boundsCheckLimit = ins->boundsCheckLimit();
|
|
+ MOZ_ASSERT(boundsCheckLimit->type() == MIRType::Int32);
|
|
+ limitAlloc = useRegisterAtStart(boundsCheckLimit);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ define(new(alloc()) LAsmJSLoadHeap(baseAlloc, limitAlloc, LAllocation()), ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
|
|
+{
|
|
+ MOZ_ASSERT(ins->access().offset() == 0);
|
|
+
|
|
+ MDefinition* base = ins->base();
|
|
+ MOZ_ASSERT(base->type() == MIRType::Int32);
|
|
+ LAllocation baseAlloc;
|
|
+ LAllocation limitAlloc;
|
|
+ if (base->isConstant() && !ins->needsBoundsCheck()) {
|
|
+ MOZ_ASSERT(base->toConstant()->toInt32() >= 0);
|
|
+ baseAlloc = LAllocation(base->toConstant());
|
|
+ } else{
|
|
+ baseAlloc = useRegisterAtStart(base);
|
|
+ if (ins->needsBoundsCheck()) {
|
|
+ MDefinition* boundsCheckLimit = ins->boundsCheckLimit();
|
|
+ MOZ_ASSERT(boundsCheckLimit->type() == MIRType::Int32);
|
|
+ limitAlloc = useRegisterAtStart(boundsCheckLimit);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ add(new(alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value()), limitAlloc, LAllocation()), ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitSubstr(MSubstr* ins)
|
|
+{
|
|
+ LSubstr* lir = new (alloc()) LSubstr(useRegister(ins->string()),
|
|
+ useRegister(ins->begin()),
|
|
+ useRegister(ins->length()),
|
|
+ temp(),
|
|
+ temp(),
|
|
+ tempByteOpRegister());
|
|
+ define(lir, ins);
|
|
+ assignSafepoint(lir, ins);
|
|
+}
|
|
+
|
|
+void LIRGenerator::visitCompareExchangeTypedArrayElement(
|
|
+ MCompareExchangeTypedArrayElement* ins) {
|
|
+ MOZ_ASSERT(ins->arrayType() != Scalar::Float32);
|
|
+ MOZ_ASSERT(ins->arrayType() != Scalar::Float64);
|
|
+
|
|
+ MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
|
|
+ MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr);
|
|
+
|
|
+ const LUse elements = useRegister(ins->elements());
|
|
+ const LAllocation index =
|
|
+ useRegisterOrIndexConstant(ins->index(), ins->arrayType());
|
|
+
|
|
+ const LAllocation newval = useRegister(ins->newval());
|
|
+ const LAllocation oldval = useRegister(ins->oldval());
|
|
+
|
|
+ if (Scalar::isBigIntType(ins->arrayType())) {
|
|
+ LInt64Definition temp1 = tempInt64();
|
|
+ LInt64Definition temp2 = tempInt64();
|
|
+
|
|
+ auto* lir = new (alloc()) LCompareExchangeTypedArrayElement64(
|
|
+ elements, index, oldval, newval, temp1, temp2);
|
|
+ define(lir, ins);
|
|
+ assignSafepoint(lir, ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // If the target is an FPR then we need a temp at the
|
|
+ // CodeGenerator level for creating the result. XXX: we do?
|
|
+
|
|
+ LDefinition outTemp = LDefinition::BogusTemp();
|
|
+ LDefinition valueTemp = LDefinition::BogusTemp();
|
|
+ LDefinition offsetTemp = LDefinition::BogusTemp();
|
|
+ LDefinition maskTemp = LDefinition::BogusTemp();
|
|
+
|
|
+ if (ins->arrayType() == Scalar::Uint32 && IsFloatingPointType(ins->type())) {
|
|
+ outTemp = temp();
|
|
+ }
|
|
+
|
|
+ if (Scalar::byteSize(ins->arrayType()) < 4) {
|
|
+ valueTemp = temp();
|
|
+ offsetTemp = temp();
|
|
+ maskTemp = temp();
|
|
+ }
|
|
+
|
|
+ LCompareExchangeTypedArrayElement* lir = new (alloc())
|
|
+ LCompareExchangeTypedArrayElement(elements, index, oldval, newval,
|
|
+ outTemp, valueTemp, offsetTemp,
|
|
+ maskTemp);
|
|
+
|
|
+ define(lir, ins);
|
|
+}
|
|
+
|
|
+void LIRGenerator::visitAtomicExchangeTypedArrayElement(
|
|
+ MAtomicExchangeTypedArrayElement* ins) {
|
|
+ MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
|
|
+ MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr);
|
|
+
|
|
+ const LUse elements = useRegister(ins->elements());
|
|
+ const LAllocation index =
|
|
+ useRegisterOrIndexConstant(ins->index(), ins->arrayType());
|
|
+
|
|
+ const LAllocation value = useRegister(ins->value());
|
|
+
|
|
+ if (Scalar::isBigIntType(ins->arrayType())) {
|
|
+ LInt64Definition temp1 = tempInt64();
|
|
+ LDefinition temp2 = temp();
|
|
+
|
|
+ auto* lir = new (alloc()) LAtomicExchangeTypedArrayElement64(
|
|
+ elements, index, value, temp1, temp2);
|
|
+ define(lir, ins);
|
|
+ assignSafepoint(lir, ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Same thing.
|
|
+ // XXX: same thing?
|
|
+
|
|
+ MOZ_ASSERT(ins->arrayType() <= Scalar::Uint32);
|
|
+
|
|
+ LDefinition outTemp = LDefinition::BogusTemp();
|
|
+ LDefinition valueTemp = LDefinition::BogusTemp();
|
|
+ LDefinition offsetTemp = LDefinition::BogusTemp();
|
|
+ LDefinition maskTemp = LDefinition::BogusTemp();
|
|
+
|
|
+ if (ins->arrayType() == Scalar::Uint32) {
|
|
+ MOZ_ASSERT(ins->type() == MIRType::Double);
|
|
+ outTemp = temp();
|
|
+ }
|
|
+
|
|
+ if (Scalar::byteSize(ins->arrayType()) < 4) {
|
|
+ valueTemp = temp();
|
|
+ offsetTemp = temp();
|
|
+ maskTemp = temp();
|
|
+ }
|
|
+
|
|
+ LAtomicExchangeTypedArrayElement* lir =
|
|
+ new (alloc()) LAtomicExchangeTypedArrayElement(
|
|
+ elements, index, value, outTemp, valueTemp, offsetTemp, maskTemp);
|
|
+
|
|
+ define(lir, ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitWasmCompareExchangeHeap(MWasmCompareExchangeHeap* ins)
|
|
+{
|
|
+ MDefinition* base = ins->base();
|
|
+ // Again with the bases?
|
|
+ MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64);
|
|
+
|
|
+ if (ins->access().type() == Scalar::Int64) {
|
|
+ auto* lir = new (alloc()) LWasmCompareExchangeI64(
|
|
+ useRegister(base), useInt64Register(ins->oldValue()),
|
|
+ useInt64Register(ins->newValue()));
|
|
+ defineInt64(lir, ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ LDefinition valueTemp = LDefinition::BogusTemp();
|
|
+ LDefinition offsetTemp = LDefinition::BogusTemp();
|
|
+ LDefinition maskTemp = LDefinition::BogusTemp();
|
|
+
|
|
+ if (ins->access().byteSize() < 4) {
|
|
+ valueTemp = temp();
|
|
+ offsetTemp = temp();
|
|
+ maskTemp = temp();
|
|
+ }
|
|
+
|
|
+ LWasmCompareExchangeHeap* lir = new (alloc()) LWasmCompareExchangeHeap(
|
|
+ useRegister(base), useRegister(ins->oldValue()),
|
|
+ useRegister(ins->newValue()), valueTemp, offsetTemp, maskTemp);
|
|
+
|
|
+ define(lir, ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitWasmAtomicExchangeHeap(MWasmAtomicExchangeHeap* ins)
|
|
+{
|
|
+ MDefinition* base = ins->base();
|
|
+ // See comment in visitWasmLoad re the type of 'base'.
|
|
+ MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64);
|
|
+
|
|
+ if (ins->access().type() == Scalar::Int64) {
|
|
+ auto* lir = new (alloc()) LWasmAtomicExchangeI64(
|
|
+ useRegister(base), useInt64Register(ins->value()));
|
|
+ defineInt64(lir, ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ LDefinition valueTemp = LDefinition::BogusTemp();
|
|
+ LDefinition offsetTemp = LDefinition::BogusTemp();
|
|
+ LDefinition maskTemp = LDefinition::BogusTemp();
|
|
+
|
|
+ if (ins->access().byteSize() < 4) {
|
|
+ valueTemp = temp();
|
|
+ offsetTemp = temp();
|
|
+ maskTemp = temp();
|
|
+ }
|
|
+
|
|
+ LWasmAtomicExchangeHeap* lir = new (alloc())
|
|
+ LWasmAtomicExchangeHeap(useRegister(base), useRegister(ins->value()),
|
|
+ valueTemp, offsetTemp, maskTemp);
|
|
+ define(lir, ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins)
|
|
+{
|
|
+ MDefinition* base = ins->base();
|
|
+ // See comment in visitWasmLoad re the type of 'base'.
|
|
+ MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64);
|
|
+
|
|
+ if (ins->access().type() == Scalar::Int64) {
|
|
+ auto* lir = new (alloc())
|
|
+ LWasmAtomicBinopI64(useRegister(base), useInt64Register(ins->value()));
|
|
+ lir->setTemp(0, temp());
|
|
+ defineInt64(lir, ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ LDefinition valueTemp = LDefinition::BogusTemp();
|
|
+ LDefinition offsetTemp = LDefinition::BogusTemp();
|
|
+ LDefinition maskTemp = LDefinition::BogusTemp();
|
|
+
|
|
+ if (ins->access().byteSize() < 4) {
|
|
+ valueTemp = temp();
|
|
+ offsetTemp = temp();
|
|
+ maskTemp = temp();
|
|
+ }
|
|
+
|
|
+ if (!ins->hasUses()) {
|
|
+ LWasmAtomicBinopHeapForEffect* lir = new (alloc())
|
|
+ LWasmAtomicBinopHeapForEffect(useRegister(base),
|
|
+ useRegister(ins->value()), valueTemp,
|
|
+ offsetTemp, maskTemp);
|
|
+ add(lir, ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ LWasmAtomicBinopHeap* lir = new (alloc())
|
|
+ LWasmAtomicBinopHeap(useRegister(base), useRegister(ins->value()),
|
|
+ valueTemp, offsetTemp, maskTemp);
|
|
+
|
|
+ define(lir, ins);
|
|
+}
|
|
+
|
|
+void LIRGenerator::visitAtomicTypedArrayElementBinop(
|
|
+ MAtomicTypedArrayElementBinop* ins) {
|
|
+ MOZ_ASSERT(ins->arrayType() != Scalar::Uint8Clamped);
|
|
+ MOZ_ASSERT(ins->arrayType() != Scalar::Float32);
|
|
+ MOZ_ASSERT(ins->arrayType() != Scalar::Float64);
|
|
+
|
|
+ MOZ_ASSERT(ins->elements()->type() == MIRType::Elements);
|
|
+ MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr);
|
|
+
|
|
+ const LUse elements = useRegister(ins->elements());
|
|
+ const LAllocation index =
|
|
+ useRegisterOrIndexConstant(ins->index(), ins->arrayType());
|
|
+ const LAllocation value = useRegister(ins->value());
|
|
+
|
|
+ if (Scalar::isBigIntType(ins->arrayType())) {
|
|
+ LInt64Definition temp1 = tempInt64();
|
|
+ LInt64Definition temp2 = tempInt64();
|
|
+
|
|
+ // Case 1: the result of the operation is not used.
|
|
+ //
|
|
+ // We can omit allocating the result BigInt.
|
|
+
|
|
+ if (ins->isForEffect()) {
|
|
+ auto* lir = new (alloc()) LAtomicTypedArrayElementBinopForEffect64(
|
|
+ elements, index, value, temp1, temp2);
|
|
+ add(lir, ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Case 2: the result of the operation is used.
|
|
+
|
|
+ auto* lir = new (alloc())
|
|
+ LAtomicTypedArrayElementBinop64(elements, index, value, temp1, temp2);
|
|
+ define(lir, ins);
|
|
+ assignSafepoint(lir, ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ LDefinition valueTemp = LDefinition::BogusTemp();
|
|
+ LDefinition offsetTemp = LDefinition::BogusTemp();
|
|
+ LDefinition maskTemp = LDefinition::BogusTemp();
|
|
+
|
|
+ if (Scalar::byteSize(ins->arrayType()) < 4) {
|
|
+ valueTemp = temp();
|
|
+ offsetTemp = temp();
|
|
+ maskTemp = temp();
|
|
+ }
|
|
+
|
|
+ if (ins->isForEffect()) {
|
|
+ LAtomicTypedArrayElementBinopForEffect* lir =
|
|
+ new (alloc()) LAtomicTypedArrayElementBinopForEffect(
|
|
+ elements, index, value, valueTemp, offsetTemp, maskTemp);
|
|
+ add(lir, ins);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // For a Uint32Array with a known double result we need a temp for
|
|
+ // the intermediate output.
|
|
+
|
|
+ LDefinition outTemp = LDefinition::BogusTemp();
|
|
+
|
|
+ if (ins->arrayType() == Scalar::Uint32 && IsFloatingPointType(ins->type())) {
|
|
+ outTemp = temp();
|
|
+ }
|
|
+
|
|
+ LAtomicTypedArrayElementBinop* lir =
|
|
+ new (alloc()) LAtomicTypedArrayElementBinop(
|
|
+ elements, index, value, outTemp, valueTemp, offsetTemp, maskTemp);
|
|
+ define(lir, ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitCopySign(MCopySign* ins)
|
|
+{
|
|
+ MDefinition* lhs = ins->lhs();
|
|
+ MDefinition* rhs = ins->rhs();
|
|
+
|
|
+ MOZ_ASSERT(IsFloatingPointType(lhs->type()));
|
|
+ MOZ_ASSERT(lhs->type() == rhs->type());
|
|
+ MOZ_ASSERT(lhs->type() == ins->type());
|
|
+
|
|
+ LInstructionHelper<1, 2, 2>* lir;
|
|
+ if (lhs->type() == MIRType::Double)
|
|
+ lir = new(alloc()) LCopySignD();
|
|
+ else
|
|
+ lir = new(alloc()) LCopySignF();
|
|
+
|
|
+ lir->setTemp(0, temp());
|
|
+ lir->setTemp(1, temp());
|
|
+
|
|
+ lir->setOperand(0, useRegisterAtStart(lhs));
|
|
+ lir->setOperand(1, useRegister(rhs));
|
|
+ defineReuseInput(lir, ins, 0);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins)
|
|
+{
|
|
+ defineInt64(new(alloc()) LExtendInt32ToInt64(useRegisterAtStart(ins->input())), ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitSignExtendInt64(MSignExtendInt64* ins)
|
|
+{
|
|
+ defineInt64(new(alloc()) LSignExtendInt64(useInt64RegisterAtStart(ins->input())), ins);
|
|
+}
|
|
+
|
|
+bool LIRGeneratorShared::canSpecializeWasmCompareAndSelect(
|
|
+ MCompare::CompareType compTy, MIRType insTy) {
|
|
+ // XXX: for now
|
|
+ // Should be able to implement this for (u)int32 compare/select
|
|
+ // with isel, and probably floats/doubles with fsel.
|
|
+/*
|
|
+ return insTy == MIRType::Int32 && (compTy == MCompare::Compare_Int32 ||
|
|
+ compTy == MCompare::Compare_UInt32);
|
|
+*/
|
|
+ return false;
|
|
+}
|
|
+
|
|
+void LIRGeneratorShared::lowerWasmCompareAndSelect(MWasmSelect* ins,
|
|
+ MDefinition* lhs,
|
|
+ MDefinition* rhs,
|
|
+ MCompare::CompareType compTy,
|
|
+ JSOp jsop) {
|
|
+ MOZ_ASSERT(canSpecializeWasmCompareAndSelect(compTy, ins->type()));
|
|
+/*
|
|
+ auto* lir = new (alloc()) LWasmCompareAndSelect(
|
|
+ useRegister(lhs), useRegister(rhs), compTy, jsop,
|
|
+ useRegisterAtStart(ins->trueExpr()), useRegister(ins->falseExpr()));
|
|
+ defineReuseInput(lir, ins, LWasmCompareAndSelect::IfTrueExprIndex);
|
|
+*/
|
|
+ MOZ_CRASH("that was unexpected");
|
|
+}
|
|
+
|
|
+void LIRGenerator::visitWasmTernarySimd128(MWasmTernarySimd128* ins) {
|
|
+ MOZ_CRASH("ternary SIMD NYI");
|
|
+}
|
|
+
|
|
+void LIRGenerator::visitWasmBinarySimd128(MWasmBinarySimd128* ins) {
|
|
+ MOZ_CRASH("binary SIMD NYI");
|
|
+}
|
|
+
|
|
+bool MWasmBinarySimd128::specializeForConstantRhs() {
|
|
+ // Probably many we want to do here
|
|
+ return false;
|
|
+}
|
|
+
|
|
+void LIRGenerator::visitWasmBinarySimd128WithConstant(
|
|
+ MWasmBinarySimd128WithConstant* ins) {
|
|
+ MOZ_CRASH("binary SIMD with constant NYI");
|
|
+}
|
|
+
|
|
+void LIRGenerator::visitWasmShiftSimd128(MWasmShiftSimd128* ins) {
|
|
+ MOZ_CRASH("shift SIMD NYI");
|
|
+}
|
|
+
|
|
+void LIRGenerator::visitWasmShuffleSimd128(MWasmShuffleSimd128* ins) {
|
|
+ MOZ_CRASH("shuffle SIMD NYI");
|
|
+}
|
|
+
|
|
+void LIRGenerator::visitWasmReplaceLaneSimd128(MWasmReplaceLaneSimd128* ins) {
|
|
+ MOZ_CRASH("replace-lane SIMD NYI");
|
|
+}
|
|
+
|
|
+void LIRGenerator::visitWasmScalarToSimd128(MWasmScalarToSimd128* ins) {
|
|
+ MOZ_CRASH("scalar-to-SIMD NYI");
|
|
+}
|
|
+
|
|
+void LIRGenerator::visitWasmUnarySimd128(MWasmUnarySimd128* ins) {
|
|
+ MOZ_CRASH("unary SIMD NYI");
|
|
+}
|
|
+
|
|
+void LIRGenerator::visitWasmReduceSimd128(MWasmReduceSimd128* ins) {
|
|
+ MOZ_CRASH("reduce-SIMD NYI");
|
|
+}
|
|
+
|
|
+void LIRGenerator::visitWasmLoadLaneSimd128(MWasmLoadLaneSimd128* ins) {
|
|
+ MOZ_CRASH("load-lane SIMD NYI");
|
|
+}
|
|
+
|
|
+void LIRGenerator::visitWasmStoreLaneSimd128(MWasmStoreLaneSimd128* ins) {
|
|
+ MOZ_CRASH("store-lane SIMD NYI");
|
|
+}
|
|
+
|
|
+// XXX: end mips-shared
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::defineInt64Phi(MPhi* phi, size_t lirIndex)
|
|
+{
|
|
+ defineTypedPhi(phi, lirIndex);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerInt64PhiInput(MPhi* phi, uint32_t inputPosition,
|
|
+ LBlock* block, size_t lirIndex)
|
|
+{
|
|
+ lowerTypedPhiInput(phi, inputPosition, block, lirIndex);
|
|
+}
|
|
+
|
|
+LBoxAllocation
|
|
+LIRGeneratorPPC64::useBoxFixed(MDefinition* mir, Register reg1, Register reg2, bool useAtStart)
|
|
+{
|
|
+ MOZ_ASSERT(mir->type() == MIRType::Value);
|
|
+
|
|
+ ensureDefined(mir);
|
|
+ return LBoxAllocation(LUse(reg1, mir->virtualRegister(), useAtStart));
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerDivI64(MDiv* div)
|
|
+{
|
|
+ if (div->isUnsigned()) {
|
|
+ lowerUDivI64(div);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ LDivOrModI64* lir = new(alloc()) LDivOrModI64(useRegister(div->lhs()), useRegister(div->rhs()),
|
|
+ temp());
|
|
+ defineInt64(lir, div);
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) {
|
|
+// XXX: we don't?
|
|
+ MOZ_CRASH("We don't use runtime div for this architecture");
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerModI64(MMod* mod)
|
|
+{
|
|
+ if (mod->isUnsigned()) {
|
|
+ lowerUModI64(mod);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ LDivOrModI64* lir = new(alloc()) LDivOrModI64(useRegister(mod->lhs()), useRegister(mod->rhs()),
|
|
+ temp());
|
|
+ defineInt64(lir, mod);
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) {
|
|
+// XXX: we don't?
|
|
+ MOZ_CRASH("We don't use runtime mod for this architecture");
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerUDivI64(MDiv* div)
|
|
+{
|
|
+ LUDivOrModI64* lir = new(alloc()) LUDivOrModI64(useRegister(div->lhs()),
|
|
+ useRegister(div->rhs()),
|
|
+ temp());
|
|
+ defineInt64(lir, div);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerUModI64(MMod* mod)
|
|
+{
|
|
+ LUDivOrModI64* lir = new(alloc()) LUDivOrModI64(useRegister(mod->lhs()),
|
|
+ useRegister(mod->rhs()),
|
|
+ temp());
|
|
+ defineInt64(lir, mod);
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerBigIntDiv(MBigIntDiv* ins) {
|
|
+ auto* lir = new (alloc()) LBigIntDiv(useRegister(ins->lhs()),
|
|
+ useRegister(ins->rhs()), temp(), temp());
|
|
+ define(lir, ins);
|
|
+ assignSafepoint(lir, ins);
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerBigIntMod(MBigIntMod* ins) {
|
|
+ auto* lir = new (alloc()) LBigIntMod(useRegister(ins->lhs()),
|
|
+ useRegister(ins->rhs()), temp(), temp());
|
|
+ define(lir, ins);
|
|
+ assignSafepoint(lir, ins);
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerAtomicLoad64(MLoadUnboxedScalar* ins) {
|
|
+ const LUse elements = useRegister(ins->elements());
|
|
+ const LAllocation index =
|
|
+ useRegisterOrIndexConstant(ins->index(), ins->storageType());
|
|
+
|
|
+ auto* lir = new (alloc()) LAtomicLoad64(elements, index, temp(), tempInt64());
|
|
+ define(lir, ins);
|
|
+ assignSafepoint(lir, ins);
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerAtomicStore64(MStoreUnboxedScalar* ins) {
|
|
+ LUse elements = useRegister(ins->elements());
|
|
+ LAllocation index =
|
|
+ useRegisterOrIndexConstant(ins->index(), ins->writeType());
|
|
+ LAllocation value = useRegister(ins->value());
|
|
+
|
|
+ add(new (alloc()) LAtomicStore64(elements, index, value, tempInt64()), ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitBox(MBox* box)
|
|
+{
|
|
+ MDefinition* opd = box->getOperand(0);
|
|
+
|
|
+ // If the operand is a constant, emit near its uses.
|
|
+ if (opd->isConstant() && box->canEmitAtUses()) {
|
|
+ emitAtUses(box);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (opd->isConstant()) {
|
|
+ define(new(alloc()) LValue(opd->toConstant()->toJSValue()), box, LDefinition(LDefinition::BOX));
|
|
+ } else {
|
|
+ LBox* ins = new(alloc()) LBox(useRegister(opd), opd->type());
|
|
+ define(ins, box, LDefinition(LDefinition::BOX));
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitUnbox(MUnbox* unbox)
|
|
+{
|
|
+ MDefinition* box = unbox->getOperand(0);
|
|
+
|
|
+/*
|
|
+ if (box->type() == MIRType::ObjectOrNull) {
|
|
+ LUnboxObjectOrNull* lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(box));
|
|
+ if (unbox->fallible())
|
|
+ assignSnapshot(lir, unbox->bailoutKind());
|
|
+ defineReuseInput(lir, unbox, 0);
|
|
+ return;
|
|
+ }
|
|
+*/
|
|
+
|
|
+ MOZ_ASSERT(box->type() == MIRType::Value);
|
|
+
|
|
+ LUnbox* lir;
|
|
+ if (IsFloatingPointType(unbox->type())) {
|
|
+ lir = new(alloc()) LUnboxFloatingPoint(useRegisterAtStart(box), unbox->type());
|
|
+ } else if (unbox->fallible()) {
|
|
+ // If the unbox is fallible, load the Value in a register first to
|
|
+ // avoid multiple loads.
|
|
+ lir = new(alloc()) LUnbox(useRegisterAtStart(box));
|
|
+ } else {
|
|
+ lir = new(alloc()) LUnbox(useAtStart(box));
|
|
+ }
|
|
+
|
|
+ if (unbox->fallible())
|
|
+ assignSnapshot(lir, unbox->bailoutKind());
|
|
+
|
|
+ define(lir, unbox);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitReturnImpl(MDefinition* opd, bool isGenerator)
|
|
+{
|
|
+ MOZ_ASSERT(opd->type() == MIRType::Value);
|
|
+
|
|
+ LReturn* ins = new(alloc()) LReturn(isGenerator);
|
|
+ ins->setOperand(0, useFixed(opd, JSReturnReg));
|
|
+ add(ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::defineUntypedPhi(MPhi* phi, size_t lirIndex)
|
|
+{
|
|
+ defineTypedPhi(phi, lirIndex);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition,
|
|
+ LBlock* block, size_t lirIndex)
|
|
+{
|
|
+ lowerTypedPhiInput(phi, inputPosition, block, lirIndex);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerTruncateDToInt32(MTruncateToInt32* ins)
|
|
+{
|
|
+ MDefinition* opd = ins->input();
|
|
+ MOZ_ASSERT(opd->type() == MIRType::Double);
|
|
+
|
|
+ define(new(alloc())
|
|
+ LTruncateDToInt32(useRegister(opd), tempDouble()), ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerTruncateFToInt32(MTruncateToInt32* ins)
|
|
+{
|
|
+ MDefinition* opd = ins->input();
|
|
+ MOZ_ASSERT(opd->type() == MIRType::Float32);
|
|
+
|
|
+ define(new(alloc())
|
|
+ LTruncateFToInt32(useRegister(opd), tempFloat32()), ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins)
|
|
+{
|
|
+ MDefinition* opd = ins->input();
|
|
+ MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32);
|
|
+
|
|
+ defineInt64(new(alloc()) LWasmTruncateToInt64(useRegister(opd)), ins);
|
|
+}
|
|
+
|
|
+void LIRGeneratorPPC64::lowerWasmBuiltinTruncateToInt64(
|
|
+ MWasmBuiltinTruncateToInt64* ins) {
|
|
+ MOZ_CRASH("We don't use it for this architecture");
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins)
|
|
+{
|
|
+ MDefinition* opd = ins->input();
|
|
+ MOZ_ASSERT(opd->type() == MIRType::Int64);
|
|
+ MOZ_ASSERT(IsFloatingPointType(ins->type()));
|
|
+
|
|
+ define(new(alloc()) LInt64ToFloatingPoint(useInt64Register(opd)), ins);
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGeneratorPPC64::lowerBuiltinInt64ToFloatingPoint(MBuiltinInt64ToFloatingPoint *)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+#if 0
|
|
+// A beautiful AltiVec and VSX world awaits me if I could only stop procrastinating.
|
|
+void
|
|
+LIRGenerator::visitSimdInsertElement(MSimdInsertElement*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitSimdExtractElement(MSimdExtractElement*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitSimdBinaryArith(MSimdBinaryArith*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitSimdSelect(MSimdSelect*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitSimdSplat(MSimdSplat*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitSimdValueX4(MSimdValueX4*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitSimdBinarySaturating(MSimdBinarySaturating*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitSimdSwizzle(MSimdSwizzle*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitSimdShuffle(MSimdShuffle*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+LIRGenerator::visitSimdGeneralShuffle(MSimdGeneralShuffle*)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+#endif
|
|
+
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/Lowering-ppc64.h
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/Lowering-ppc64.h Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,136 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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_ppc64le_Lowering_ppc64le_h
|
|
+#define jit_ppc64le_Lowering_ppc64le_h
|
|
+
|
|
+#include "jit/shared/Lowering-shared.h"
|
|
+
|
|
+namespace js {
|
|
+namespace jit {
|
|
+
|
|
+class LIRGeneratorPPC64 : public LIRGeneratorShared
|
|
+{
|
|
+ protected:
|
|
+ LIRGeneratorPPC64(MIRGenerator* gen, MIRGraph& graph, LIRGraph& lirGraph)
|
|
+ : LIRGeneratorShared(gen, graph, lirGraph)
|
|
+ { }
|
|
+
|
|
+ // x86 has constraints on what registers can be formatted for 1-byte
|
|
+ // stores and loads, but on Power all GPRs are okay.
|
|
+ LAllocation useByteOpRegister(MDefinition* mir);
|
|
+ LAllocation useByteOpRegisterAtStart(MDefinition* mir);
|
|
+ LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition* mir);
|
|
+ LDefinition tempByteOpRegister();
|
|
+
|
|
+ bool needTempForPostBarrier() { return false; }
|
|
+
|
|
+ void lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
|
|
+ MDefinition* rhs);
|
|
+ void lowerUrshD(MUrsh* mir);
|
|
+
|
|
+ void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
|
|
+ MDefinition* input);
|
|
+ void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
|
|
+ MDefinition* lhs, MDefinition* rhs);
|
|
+
|
|
+ void lowerForALUInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES, 0>* ins,
|
|
+ MDefinition* mir, MDefinition* input);
|
|
+ void lowerForALUInt64(
|
|
+ LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
|
|
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs);
|
|
+
|
|
+ void lowerForMulInt64(LMulI64* ins, MMul* mir, MDefinition* lhs, MDefinition* rhs);
|
|
+ template<size_t Temps>
|
|
+ void lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, Temps>* ins,
|
|
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs);
|
|
+ void lowerForCompareI64AndBranch(MTest* mir, MCompare* comp, JSOp op,
|
|
+ MDefinition* left, MDefinition* right,
|
|
+ MBasicBlock* ifTrue, MBasicBlock* ifFalse);
|
|
+
|
|
+ void lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
|
|
+ MDefinition* src);
|
|
+ template<size_t Temps>
|
|
+ void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
|
|
+ MDefinition* lhs, MDefinition* rhs);
|
|
+
|
|
+#if 0
|
|
+ void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
|
|
+ MDefinition* lhs, MDefinition* rhs)
|
|
+ {
|
|
+ return lowerForFPU(ins, mir, lhs, rhs);
|
|
+ }
|
|
+ void lowerForCompFx4(LSimdBinaryCompFx4* ins, MSimdBinaryComp* mir,
|
|
+ MDefinition* lhs, MDefinition* rhs)
|
|
+ {
|
|
+ return lowerForFPU(ins, mir, lhs, rhs);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
|
|
+ MDefinition* lhs, MDefinition* rhs);
|
|
+ void lowerDivI(MDiv* div);
|
|
+ void lowerModI(MMod* mod);
|
|
+ void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs);
|
|
+ void lowerPowOfTwoI(MPow* mir);
|
|
+ void lowerUDiv(MDiv* div);
|
|
+ void lowerUMod(MMod* mod);
|
|
+
|
|
+ LTableSwitch* newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
|
|
+ MTableSwitch* ins);
|
|
+ LTableSwitchV* newLTableSwitchV(MTableSwitch* ins);
|
|
+
|
|
+ void lowerPhi(MPhi* phi);
|
|
+ void lowerInt64PhiInput(MPhi*, uint32_t, LBlock*, size_t);
|
|
+ void defineInt64Phi(MPhi*, size_t);
|
|
+
|
|
+ // Returns a box allocation. reg2 is ignored since we're 64-bit.
|
|
+ LBoxAllocation useBoxFixed(MDefinition* mir, Register reg1, Register reg2,
|
|
+ bool useAtStart = false);
|
|
+
|
|
+ inline LDefinition tempToUnbox() {
|
|
+ return temp();
|
|
+ }
|
|
+
|
|
+ void lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, size_t lirIndex);
|
|
+ void defineUntypedPhi(MPhi* phi, size_t lirIndex);
|
|
+
|
|
+ void lowerTruncateDToInt32(MTruncateToInt32* ins);
|
|
+ void lowerTruncateFToInt32(MTruncateToInt32* ins);
|
|
+
|
|
+ void lowerDivI64(MDiv* div);
|
|
+ void lowerModI64(MMod* mod);
|
|
+ void lowerUDivI64(MDiv* div);
|
|
+ void lowerUModI64(MMod* mod);
|
|
+ void lowerNegI(MInstruction* ins, MDefinition* input);
|
|
+ void lowerNegI64(MInstruction* ins, MDefinition* input);
|
|
+
|
|
+ void lowerAtomicLoad64(MLoadUnboxedScalar* ins);
|
|
+ void lowerAtomicStore64(MStoreUnboxedScalar* ins);
|
|
+
|
|
+ // BigInt
|
|
+ void lowerBigIntDiv(MBigIntDiv *ins);
|
|
+ void lowerBigIntMod(MBigIntMod *ins);
|
|
+ void lowerBigIntLsh(MBigIntLsh *ins);
|
|
+ void lowerBigIntRsh(MBigIntRsh *ins);
|
|
+
|
|
+ // WASM bits
|
|
+ void lowerWasmBuiltinTruncateToInt32(MWasmBuiltinTruncateToInt32 *ins);
|
|
+ void lowerWasmBuiltinTruncateToInt64(MWasmBuiltinTruncateToInt64 *ins);
|
|
+ void lowerWasmBuiltinDivI64(MWasmBuiltinDivI64 *ins);
|
|
+ void lowerWasmBuiltinModI64(MWasmBuiltinModI64 *ins);
|
|
+ void lowerWasmSelectI(MWasmSelect* select);
|
|
+ void lowerWasmSelectI64(MWasmSelect* select);
|
|
+
|
|
+ void lowerBuiltinInt64ToFloatingPoint(MBuiltinInt64ToFloatingPoint *ins);
|
|
+};
|
|
+
|
|
+typedef LIRGeneratorPPC64 LIRGeneratorSpecific;
|
|
+
|
|
+} // namespace jit
|
|
+} // namespace js
|
|
+
|
|
+#endif /* jit_ppc64le_Lowering_ppc64le_h */
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/MacroAssembler-ppc64-inl.h
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/MacroAssembler-ppc64-inl.h Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,2769 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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_ppc64le_MacroAssembler_ppc64le_inl_h
|
|
+#define jit_ppc64le_MacroAssembler_ppc64le_inl_h
|
|
+
|
|
+#include "jit/ppc64/MacroAssembler-ppc64.h"
|
|
+#include "jit/FlushICache.h"
|
|
+
|
|
+namespace js {
|
|
+namespace jit {
|
|
+
|
|
+//{{{ check_macroassembler_style
|
|
+
|
|
+void
|
|
+MacroAssembler::move64(Register64 src, Register64 dest)
|
|
+{
|
|
+ movePtr(src.reg, dest.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::move64(Imm64 imm, Register64 dest)
|
|
+{
|
|
+ movePtr(ImmWord(imm.value), dest.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::moveDoubleToGPR64(FloatRegister src, Register64 dest)
|
|
+{
|
|
+ moveFromDouble(src, dest.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::moveGPR64ToDouble(Register64 src, FloatRegister dest)
|
|
+{
|
|
+ moveToDouble(src.reg, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::move64To32(Register64 src, Register dest)
|
|
+{
|
|
+ // Registers are registers, so why should it be
|
|
+ // 32 bits are treated differently?
|
|
+ // (with apologies to Depeche Mode)
|
|
+ as_srawi(dest, src.reg, 0); // clear upper word and sign extend
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::move32To64ZeroExtend(Register src, Register64 dest)
|
|
+{
|
|
+ as_rldicl(dest.reg, src, 0, 32); // "clrldi"
|
|
+}
|
|
+
|
|
+void MacroAssembler::move8ZeroExtend(Register src, Register dest) {
|
|
+ // probably andi. dest, src, 0xff is simplest
|
|
+ // used in CodeGenerator
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::move8To64SignExtend(Register src, Register64 dest)
|
|
+{
|
|
+ move32To64SignExtend(src, dest);
|
|
+ move8SignExtend(dest.reg, dest.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::move16To64SignExtend(Register src, Register64 dest)
|
|
+{
|
|
+ move32To64SignExtend(src, dest);
|
|
+ move16SignExtend(dest.reg, dest.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::move32To64SignExtend(Register src, Register64 dest)
|
|
+{
|
|
+ as_srawi(dest.reg, src, 0);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::move32SignExtendToPtr(Register src, Register dest)
|
|
+{
|
|
+ as_srawi(dest, src, 0);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::move32ZeroExtendToPtr(Register src, Register dest)
|
|
+{
|
|
+ as_rldicl(dest, src, 0, 32); // "clrldi"
|
|
+}
|
|
+
|
|
+
|
|
+// ===============================================================
|
|
+// Logical instructions
|
|
+
|
|
+void
|
|
+MacroAssembler::andPtr(Register src, Register dest)
|
|
+{
|
|
+ ma_and(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::andPtr(Imm32 imm, Register dest)
|
|
+{
|
|
+ ma_and(dest, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::and64(Imm64 imm, Register64 dest)
|
|
+{
|
|
+ MOZ_ASSERT(dest.reg != ScratchRegister);
|
|
+ ma_li(ScratchRegister, ImmWord(imm.value));
|
|
+ ma_and(dest.reg, ScratchRegister);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::and64(Register64 src, Register64 dest)
|
|
+{
|
|
+ ma_and(dest.reg, src.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::and64(const Operand& src, Register64 dest)
|
|
+{
|
|
+ if (src.getTag() == Operand::MEM) {
|
|
+ MOZ_ASSERT(dest.reg != ScratchRegister);
|
|
+ Register64 scratch(ScratchRegister);
|
|
+
|
|
+ load64(src.toAddress(), scratch);
|
|
+ and64(scratch, dest);
|
|
+ } else {
|
|
+ and64(Register64(src.toReg()), dest);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::or64(Imm64 imm, Register64 dest)
|
|
+{
|
|
+ MOZ_ASSERT(dest.reg != ScratchRegister);
|
|
+ ma_li(ScratchRegister, ImmWord(imm.value));
|
|
+ ma_or(dest.reg, ScratchRegister);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
|
|
+{
|
|
+ MOZ_ASSERT(dest.reg != ScratchRegister);
|
|
+ ma_li(ScratchRegister, ImmWord(imm.value));
|
|
+ ma_xor(dest.reg, ScratchRegister);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::orPtr(Register src, Register dest)
|
|
+{
|
|
+ ma_or(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::orPtr(Imm32 imm, Register dest)
|
|
+{
|
|
+ ma_or(dest, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::or64(Register64 src, Register64 dest)
|
|
+{
|
|
+ ma_or(dest.reg, src.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::or64(const Operand& src, Register64 dest)
|
|
+{
|
|
+ if (src.getTag() == Operand::MEM) {
|
|
+ Register64 scratch(ScratchRegister);
|
|
+
|
|
+ load64(src.toAddress(), scratch);
|
|
+ or64(scratch, dest);
|
|
+ } else {
|
|
+ or64(Register64(src.toReg()), dest);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::xor64(Register64 src, Register64 dest)
|
|
+{
|
|
+ ma_xor(dest.reg, src.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::xor64(const Operand& src, Register64 dest)
|
|
+{
|
|
+ if (src.getTag() == Operand::MEM) {
|
|
+ MOZ_ASSERT(dest.reg != ScratchRegister);
|
|
+ Register64 scratch(ScratchRegister);
|
|
+
|
|
+ load64(src.toAddress(), scratch);
|
|
+ xor64(scratch, dest);
|
|
+ } else {
|
|
+ xor64(Register64(src.toReg()), dest);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::xorPtr(Register src, Register dest)
|
|
+{
|
|
+ ma_xor(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::xorPtr(Imm32 imm, Register dest)
|
|
+{
|
|
+ ma_xor(dest, imm);
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Arithmetic functions
|
|
+
|
|
+void
|
|
+MacroAssembler::addPtr(Register src, Register dest)
|
|
+{
|
|
+ ma_add(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::addPtr(Imm32 imm, Register dest)
|
|
+{
|
|
+ ma_add(dest, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::addPtr(ImmWord imm, Register dest)
|
|
+{
|
|
+ movePtr(imm, ScratchRegister);
|
|
+ addPtr(ScratchRegister, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::add64(Register64 src, Register64 dest)
|
|
+{
|
|
+ addPtr(src.reg, dest.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::add64(const Operand& src, Register64 dest)
|
|
+{
|
|
+ if (src.getTag() == Operand::MEM) {
|
|
+ Register64 scratch(ScratchRegister);
|
|
+
|
|
+ load64(src.toAddress(), scratch);
|
|
+ add64(scratch, dest);
|
|
+ } else {
|
|
+ add64(Register64(src.toReg()), dest);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::add64(Imm32 imm, Register64 dest)
|
|
+{
|
|
+ ma_add(dest.reg, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::add64(Imm64 imm, Register64 dest)
|
|
+{
|
|
+ MOZ_ASSERT(dest.reg != ScratchRegister);
|
|
+ mov(ImmWord(imm.value), ScratchRegister);
|
|
+ ma_add(dest.reg, ScratchRegister);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::notPtr(Register reg)
|
|
+{
|
|
+ as_nor(reg, reg, reg);
|
|
+}
|
|
+
|
|
+CodeOffset
|
|
+MacroAssembler::sub32FromStackPtrWithPatch(Register dest)
|
|
+{
|
|
+ CodeOffset offset = CodeOffset(currentOffset());
|
|
+ MacroAssemblerPPC64::ma_liPatchable(dest, Imm32(0));
|
|
+ as_subf(dest, dest, StackPointer); // T = B - A
|
|
+ return offset;
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::patchSub32FromStackPtr(CodeOffset offset, Imm32 imm)
|
|
+{
|
|
+ Instruction* lis = (Instruction*) m_buffer.getInst(BufferOffset(offset.offset()));
|
|
+ MacroAssemblerPPC64::UpdateLisOriValue(lis, lis->next(), imm.value);
|
|
+ FlushICache(lis, 2 * sizeof(uint32_t));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::subPtr(Register src, Register dest)
|
|
+{
|
|
+ as_subf(dest, src, dest); // T = B - A
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::subPtr(Imm32 imm, Register dest)
|
|
+{
|
|
+ ma_dsubu(dest, dest, imm); // inverted at MacroAssembler level
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::sub64(Register64 src, Register64 dest)
|
|
+{
|
|
+ as_subf(dest.reg, src.reg, dest.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::sub64(const Operand& src, Register64 dest)
|
|
+{
|
|
+ if (src.getTag() == Operand::MEM) {
|
|
+ Register64 scratch(ScratchRegister);
|
|
+
|
|
+ load64(src.toAddress(), scratch);
|
|
+ sub64(scratch, dest);
|
|
+ } else {
|
|
+ sub64(Register64(src.toReg()), dest);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::sub64(Imm64 imm, Register64 dest)
|
|
+{
|
|
+ MOZ_ASSERT(dest.reg != ScratchRegister);
|
|
+ mov(ImmWord(imm.value), ScratchRegister);
|
|
+ as_subf(dest.reg, ScratchRegister, dest.reg); // T = B - A
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::mul64(Imm64 imm, const Register64& dest)
|
|
+{
|
|
+ MOZ_ASSERT(dest.reg != ScratchRegister);
|
|
+ mov(ImmWord(imm.value), ScratchRegister);
|
|
+ as_mulld(dest.reg, ScratchRegister, dest.reg); // low order word
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::mul64(Imm64 imm, const Register64& dest, const Register temp)
|
|
+{
|
|
+ MOZ_ASSERT(temp == InvalidReg);
|
|
+ mul64(imm, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::mul64(const Register64& src, const Register64& dest, const Register temp)
|
|
+{
|
|
+ MOZ_ASSERT(temp == InvalidReg);
|
|
+ as_mulld(dest.reg, src.reg, dest.reg); // low order word
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::mul64(const Operand& src, const Register64& dest, const Register temp)
|
|
+{
|
|
+ if (src.getTag() == Operand::MEM) {
|
|
+ Register64 scratch(ScratchRegister);
|
|
+
|
|
+ load64(src.toAddress(), scratch);
|
|
+ mul64(scratch, dest, temp);
|
|
+ } else {
|
|
+ mul64(Register64(src.toReg()), dest, temp);
|
|
+ }
|
|
+}
|
|
+
|
|
+void MacroAssembler::mulPtr(Register rhs, Register srcDest) {
|
|
+ as_mulld(srcDest, srcDest, rhs); // low order word
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::mulBy3(Register src, Register dest)
|
|
+{
|
|
+ // I guess this *is* better than mulli.
|
|
+ MOZ_ASSERT(src != ScratchRegister);
|
|
+ as_add(ScratchRegister, src, src);
|
|
+ as_add(dest, ScratchRegister, src);
|
|
+}
|
|
+
|
|
+// This is used in MacroAssembler::loadInt32ToStringWithBase. Instead of
|
|
+// letting us use our superior arithmetic instructions, the JIT has reduced
|
|
+// us to faffing around with magic constants because that's what x86* does.
|
|
+// This leads to sign extension hazards.
|
|
+void MacroAssembler::mulHighUnsigned32(Imm32 imm, Register src, Register dest) {
|
|
+ MOZ_ASSERT(src != ScratchRegister);
|
|
+ // Compensate for (likely) sign extension by always clearing upper bits.
|
|
+ move32(imm, ScratchRegister);
|
|
+ as_rldicl(ScratchRegister, ScratchRegister, 0, 32); // "clrldi"
|
|
+ // loadInt32ToStringWithBase expects what is effectively unsigned multiply.
|
|
+ as_mulhwu(dest, ScratchRegister, src);
|
|
+ // Clear upper bits again, as they are undefined by the spec.
|
|
+ as_rldicl(dest, dest, 0, 32); // "clrldi"
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::inc64(AbsoluteAddress dest)
|
|
+{
|
|
+ ma_li(SecondScratchReg, ImmWord(uintptr_t(dest.addr)));
|
|
+ as_ld(ThirdScratchReg, SecondScratchReg, 0);
|
|
+ as_addi(ScratchRegister, ThirdScratchReg, 1);
|
|
+ as_std(ScratchRegister, SecondScratchReg, 0);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::negPtr(Register reg)
|
|
+{
|
|
+ as_neg(reg, reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::neg64(Register64 reg)
|
|
+{
|
|
+ negPtr(reg.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::quotient32(Register rhs, Register srcDest, bool isUnsigned)
|
|
+{
|
|
+ if (isUnsigned) {
|
|
+ as_divwu(srcDest, srcDest, rhs);
|
|
+ } else {
|
|
+ as_divw(srcDest, srcDest, rhs);
|
|
+ }
|
|
+}
|
|
+
|
|
+// byte swaps
|
|
+void
|
|
+MacroAssembler::byteSwap16SignExtend(Register reg)
|
|
+{
|
|
+ byteSwap16ZeroExtend(reg);
|
|
+ as_extsh(reg, reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::byteSwap16ZeroExtend(Register reg)
|
|
+{
|
|
+ MOZ_ASSERT(reg != ScratchRegister);
|
|
+ xs_mr(ScratchRegister, reg);
|
|
+
|
|
+ as_rlwinm(reg, ScratchRegister, 8, 16, 23);
|
|
+ as_rlwimi(reg, ScratchRegister, 24, 24, 31);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::byteSwap32(Register reg)
|
|
+{
|
|
+ // cribbed off gcc __builtin_swap32
|
|
+ // POWER10 has/will have brw, but POWER10 is naughty and has non-free
|
|
+ // RAM firmware. Naughty OMI! Spank spank!
|
|
+ MOZ_ASSERT(reg != ScratchRegister);
|
|
+ as_rlwinm(ScratchRegister, reg, 8, 0, 31); // "rotlwi"
|
|
+ as_rlwimi(ScratchRegister, reg, 24, 0, 7);
|
|
+ as_rlwimi(ScratchRegister, reg, 24, 16, 23);
|
|
+ xs_mr(reg, ScratchRegister);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::byteSwap64(Register64 reg)
|
|
+{
|
|
+ Register r = reg.reg;
|
|
+ // Use VSX for this because the 64-bit swap with rld* is just hideous.
|
|
+ // POWER10 has/will have brd, but, more electric spanking of war babies!
|
|
+ if (HasPPCISA3()) {
|
|
+ as_mtvsrd(ScratchDoubleReg, r);
|
|
+ as_xxbrd(ScratchDoubleReg, ScratchDoubleReg);
|
|
+ as_mfvsrd(r, ScratchDoubleReg);
|
|
+ } else {
|
|
+ MOZ_CRASH("NYI for pre-POWER9");
|
|
+ }
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Shift functions
|
|
+
|
|
+void
|
|
+MacroAssembler::lshiftPtr(Imm32 imm, Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(imm.value >= 0);
|
|
+
|
|
+ if (imm.value == 0) {
|
|
+ // No-op
|
|
+ } else {
|
|
+ as_rldicr(dest, dest, imm.value % 64, 63 - (imm.value % 64)); // "sldi"
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::lshiftPtr(Register shift, Register dest)
|
|
+{
|
|
+ // sld will zero out any shift amount greater than 64, but JavaScript
|
|
+ // expects this to act like a modulo, so ...
|
|
+ MOZ_ASSERT(shift != ScratchRegister);
|
|
+ MOZ_ASSERT(dest != ScratchRegister);
|
|
+
|
|
+ as_andi_rc(ScratchRegister, shift, 63);
|
|
+ as_sld(dest, dest, ScratchRegister);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::lshift64(Imm32 imm, Register64 dest)
|
|
+{
|
|
+ lshiftPtr(imm, dest.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::lshift64(Register shift, Register64 dest)
|
|
+{
|
|
+ lshiftPtr(shift, dest.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(imm.value >= 0);
|
|
+
|
|
+ // Same deal as rshift32, same twist.
|
|
+ if (!(imm.value % 64)) {
|
|
+ if (imm.value == 0) {
|
|
+ // No-op
|
|
+ } else {
|
|
+ // 64 bits right-shifted 64 bits is zero.
|
|
+ xs_li(dest, 0);
|
|
+ }
|
|
+ } else {
|
|
+ x_srdi(dest, dest, imm.value % 64);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::rshiftPtr(Register shift, Register dest)
|
|
+{
|
|
+ // JavaScript expects the shift to act like a modulo.
|
|
+ MOZ_ASSERT(shift != ScratchRegister);
|
|
+ as_andi_rc(ScratchRegister, shift, 63);
|
|
+ as_srd(dest, dest, ScratchRegister);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::rshift64(Imm32 imm, Register64 dest)
|
|
+{
|
|
+ rshiftPtr(imm, dest.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::rshift64(Register shift, Register64 dest)
|
|
+{
|
|
+ rshiftPtr(shift, dest.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(0 <= imm.value);
|
|
+ as_sradi(dest, dest, imm.value % 64);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::rshift64Arithmetic(Imm32 imm, Register64 dest)
|
|
+{
|
|
+ rshiftPtrArithmetic(imm, dest.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::rshift64Arithmetic(Register shift, Register64 dest)
|
|
+{
|
|
+ // JavaScript expects the shift to act like a modulo.
|
|
+ MOZ_ASSERT(shift != ScratchRegister);
|
|
+ as_andi_rc(ScratchRegister, shift, 63);
|
|
+ as_srad(dest.reg, dest.reg, ScratchRegister);
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Rotation functions
|
|
+
|
|
+void
|
|
+MacroAssembler::rotateLeft64(Imm32 count, Register64 src, Register64 dest, Register temp)
|
|
+{
|
|
+ MOZ_ASSERT(temp == InvalidReg);
|
|
+ MOZ_ASSERT(count.value >= 0);
|
|
+
|
|
+ if (!(count.value % 64)) {
|
|
+ if (src.reg != dest.reg)
|
|
+ xs_mr(dest.reg, src.reg);
|
|
+ } else {
|
|
+ as_rldicl(dest.reg, src.reg, (count.value % 64), 0); // "rotldi"
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::rotateLeft64(Register count, Register64 src, Register64 dest, Register temp)
|
|
+{
|
|
+ MOZ_ASSERT(temp == InvalidReg);
|
|
+ as_rldcl(dest.reg, src.reg, count, 0); // "rotld"
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::rotateRight64(Imm32 count, Register64 src, Register64 dest, Register temp)
|
|
+{
|
|
+ MOZ_ASSERT(temp == InvalidReg);
|
|
+ MOZ_ASSERT(count.value >= 0);
|
|
+
|
|
+ if (!(count.value % 64)) {
|
|
+ if (src.reg != dest.reg)
|
|
+ xs_mr(dest.reg, src.reg);
|
|
+ } else {
|
|
+ as_rldicl(dest.reg, src.reg, 64 - (count.value % 64), 0); // "rotrdi"
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::rotateRight64(Register count, Register64 src, Register64 dest, Register temp)
|
|
+{
|
|
+ MOZ_ASSERT(temp == InvalidReg);
|
|
+ MOZ_ASSERT(count != ScratchRegister);
|
|
+ MOZ_ASSERT(count != SecondScratchReg);
|
|
+ MOZ_ASSERT(src.reg != ScratchRegister);
|
|
+ MOZ_ASSERT(src.reg != SecondScratchReg);
|
|
+
|
|
+ xs_li(ScratchRegister, 64);
|
|
+ as_andi_rc(SecondScratchReg, count, 63);
|
|
+ as_subf(ScratchRegister, SecondScratchReg, ScratchRegister); // T = B - A
|
|
+ as_rldcl(dest.reg, src.reg, ScratchRegister, 0); // "rotrd"
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Condition functions
|
|
+
|
|
+template <typename T1, typename T2>
|
|
+void
|
|
+MacroAssembler::cmpPtrSet(Condition cond, T1 lhs, T2 rhs, Register dest)
|
|
+{
|
|
+ ma_cmp_set(dest, lhs, rhs, cond, /* useCmpw */ false);
|
|
+}
|
|
+
|
|
+// Also see below for specializations of cmpPtrSet.
|
|
+
|
|
+template <typename T1, typename T2>
|
|
+void
|
|
+MacroAssembler::cmp32Set(Condition cond, T1 lhs, T2 rhs, Register dest)
|
|
+{
|
|
+ ma_cmp_set(dest, lhs, rhs, cond, /* useCmpw */ true);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::cmp64Set(Condition cond, Address lhs, Imm64 rhs, Register dest)
|
|
+{
|
|
+ ma_cmp_set(dest, lhs, rhs, cond, /* useCmpw */ false);
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+void
|
|
+MacroAssembler::testStringSet(Condition cond, const T& value, Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ splitTag(value, SecondScratchReg);
|
|
+ ma_cmp_set(dest, SecondScratchReg, ImmTag(JSVAL_TAG_STRING), cond);
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+void
|
|
+MacroAssembler::testBooleanSet(Condition cond, const T& value, Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ splitTag(value, SecondScratchReg);
|
|
+ ma_cmp_set(dest, SecondScratchReg, ImmTag(JSVAL_TAG_BOOLEAN), cond);
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+void
|
|
+MacroAssembler::testSymbolSet(Condition cond, const T& value, Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ splitTag(value, SecondScratchReg);
|
|
+ ma_cmp_set(dest, SecondScratchReg, ImmTag(JSVAL_TAG_SYMBOL), cond);
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+void
|
|
+MacroAssembler::testBigIntSet(Condition cond, const T& value, Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ splitTag(value, SecondScratchReg);
|
|
+ ma_cmp_set(dest, SecondScratchReg, ImmTag(JSVAL_TAG_BIGINT), cond);
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+void
|
|
+MacroAssembler::testNumberSet(Condition cond, const T& value, Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ splitTag(value, SecondScratchReg);
|
|
+ ma_cmp_set(dest, SecondScratchReg,
|
|
+ ImmTag(JS::detail::ValueUpperInclNumberTag),
|
|
+ cond == Equal ? BelowOrEqual : Above);
|
|
+}
|
|
+
|
|
+
|
|
+// ===============================================================
|
|
+// Bit counting functions
|
|
+
|
|
+void
|
|
+MacroAssembler::clz64(Register64 src, Register dest)
|
|
+{
|
|
+ as_cntlzd(dest, src.reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::ctz64(Register64 src, Register dest)
|
|
+{
|
|
+ if (HasPPCISA3()) {
|
|
+ as_cnttzd(dest, src.reg);
|
|
+ } else {
|
|
+ MOZ_CRASH("NYI for pre-POWER9");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::popcnt64(Register64 input, Register64 output, Register tmp)
|
|
+{
|
|
+#if (1)
|
|
+ as_popcntd(output.reg, input.reg);
|
|
+#else
|
|
+ // Prior to ISA 2.06. Not tested.
|
|
+ ma_move(output.reg, input.reg);
|
|
+ as_sradi(tmp, input.reg, Imm32(1));
|
|
+ ma_li(ScratchRegister, ImmWord(0x5555555555555555UL));
|
|
+ ma_and(tmp, ScratchRegister);
|
|
+ ma_dsubu(output.reg, tmp);
|
|
+ as_sradi(tmp, output.reg, Imm32(2));
|
|
+ ma_li(ScratchRegister, ImmWord(0x3333333333333333UL));
|
|
+ ma_and(output.reg, ScratchRegister);
|
|
+ ma_and(tmp, ScratchRegister);
|
|
+ ma_add(output.reg, tmp);
|
|
+ ma_dsrl(tmp, output.reg, Imm32(4));
|
|
+ ma_add(output.reg, tmp);
|
|
+ ma_li(ScratchRegister, ImmWord(0xF0F0F0F0F0F0F0FUL));
|
|
+ ma_and(output.reg, ScratchRegister);
|
|
+ ma_dsll(tmp, output.reg, Imm32(8));
|
|
+ ma_add(output.reg, tmp);
|
|
+ ma_dsll(tmp, output.reg, Imm32(16));
|
|
+ ma_add(output.reg, tmp);
|
|
+ ma_dsll(tmp, output.reg, Imm32(32));
|
|
+ ma_add(output.reg, tmp);
|
|
+ as_sradi(output.reg, output.reg, Imm32(56));
|
|
+#endif
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Branch functions
|
|
+
|
|
+void
|
|
+MacroAssembler::branch64(Condition cond, Register64 lhs, Imm64 val, Label* success, Label* fail)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Assembler::NotEqual || cond == Assembler::Equal ||
|
|
+ cond == Assembler::LessThan || cond == Assembler::LessThanOrEqual ||
|
|
+ cond == Assembler::GreaterThan || cond == Assembler::GreaterThanOrEqual ||
|
|
+ cond == Assembler::Below || cond == Assembler::BelowOrEqual ||
|
|
+ cond == Assembler::Above || cond == Assembler::AboveOrEqual,
|
|
+ "other condition codes not supported");
|
|
+
|
|
+ branchPtr(cond, lhs.reg, ImmWord(val.value), success);
|
|
+ if (fail)
|
|
+ jump(fail);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branch64(Condition cond, Register64 lhs, Register64 rhs, Label* success, Label* fail)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Assembler::NotEqual || cond == Assembler::Equal ||
|
|
+ cond == Assembler::LessThan || cond == Assembler::LessThanOrEqual ||
|
|
+ cond == Assembler::GreaterThan || cond == Assembler::GreaterThanOrEqual ||
|
|
+ cond == Assembler::Below || cond == Assembler::BelowOrEqual ||
|
|
+ cond == Assembler::Above || cond == Assembler::AboveOrEqual,
|
|
+ "other condition codes not supported");
|
|
+
|
|
+ branchPtr(cond, lhs.reg, rhs.reg, success);
|
|
+ if (fail)
|
|
+ jump(fail);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Assembler::NotEqual,
|
|
+ "other condition codes not supported");
|
|
+
|
|
+ branchPtr(cond, lhs, ImmWord(val.value), label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branch64(Condition cond, const Address& lhs, Register64 reg, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(reg.reg != SecondScratchReg);
|
|
+ MOZ_ASSERT(reg.reg != ScratchRegister);
|
|
+
|
|
+ loadPtr(lhs, ScratchRegister);
|
|
+ branchPtr(cond, ScratchRegister, reg.reg, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch,
|
|
+ Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Assembler::NotEqual,
|
|
+ "other condition codes not supported");
|
|
+ MOZ_ASSERT(lhs.base != scratch);
|
|
+ MOZ_ASSERT(rhs.base != scratch);
|
|
+
|
|
+ loadPtr(rhs, scratch);
|
|
+ branchPtr(cond, lhs, scratch, label);
|
|
+}
|
|
+
|
|
+void MacroAssembler::branchNeg32(Condition cond, Register reg, Label* label) {
|
|
+ MOZ_ASSERT(cond == Overflow);
|
|
+ ma_negTestOverflow(reg, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label)
|
|
+{
|
|
+ if (rhs != ScratchRegister)
|
|
+ movePtr(rhs, ScratchRegister);
|
|
+ // Instead of unboxing lhs, box rhs and do direct comparison with lhs.
|
|
+ rshiftPtr(Imm32(1), ScratchRegister);
|
|
+ branchPtr(cond, lhs, ScratchRegister, label);
|
|
+}
|
|
+
|
|
+template <class L>
|
|
+void
|
|
+MacroAssembler::branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp,
|
|
+ L label)
|
|
+{
|
|
+ branchTestPtr(cond, lhs.reg, rhs.reg, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestUndefined(Condition cond, const ValueOperand& value, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ splitTag(value, scratch2);
|
|
+ branchTestUndefined(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestInt32(Condition cond, const ValueOperand& value, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ splitTag(value, scratch2);
|
|
+ branchTestInt32(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestInt32Truthy(bool b, const ValueOperand& value, Label* label)
|
|
+{
|
|
+ ScratchRegisterScope scratch(*this);
|
|
+ unboxBoolean(value, scratch);
|
|
+ ma_bc(scratch, scratch, label, b ? NonZero : Zero);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestDouble(Condition cond, Register tag, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ Condition actual = (cond == Equal) ? BelowOrEqual : Above;
|
|
+ ma_bc(tag, ImmTag(JSVAL_TAG_MAX_DOUBLE), label, actual);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestDouble(Condition cond, const ValueOperand& value, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ splitTag(value, scratch2);
|
|
+ branchTestDouble(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestNumber(Condition cond, const ValueOperand& value, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ splitTag(value, scratch2);
|
|
+ branchTestNumber(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestBoolean(Condition cond, const ValueOperand& value, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ splitTag(value, scratch2);
|
|
+ branchTestBoolean(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestBooleanTruthy(bool b, const ValueOperand& value, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ unboxBoolean(value, scratch2);
|
|
+ ma_bc(scratch2, scratch2, label, b ? NonZero : Zero);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestString(Condition cond, const ValueOperand& value, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ splitTag(value, scratch2);
|
|
+ branchTestString(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestStringTruthy(bool b, const ValueOperand& value, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ unboxString(value, scratch2);
|
|
+ ma_load(scratch2, Address(scratch2, JSString::offsetOfLength()), SizeWord, ZeroExtend);
|
|
+ ma_bc(scratch2, Imm32(0), label, b ? NotEqual : Equal);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestSymbol(Condition cond, const ValueOperand& value, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ splitTag(value, scratch2);
|
|
+ branchTestSymbol(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestNull(Condition cond, const ValueOperand& value, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ splitTag(value, scratch2);
|
|
+ branchTestNull(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestObject(Condition cond, const ValueOperand& value, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ splitTag(value, scratch2);
|
|
+ branchTestObject(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestPrimitive(Condition cond, const ValueOperand& value, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ splitTag(value, scratch2);
|
|
+ branchTestPrimitive(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+template <class L>
|
|
+void
|
|
+MacroAssembler::branchTestMagic(Condition cond, const ValueOperand& value, L label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ splitTag(value, scratch2);
|
|
+ ma_bc(scratch2, ImmTag(JSVAL_TAG_MAGIC), label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMagic why, Label* label)
|
|
+{
|
|
+ uint64_t magic = MagicValue(why).asRawBits();
|
|
+ SecondScratchRegisterScope scratch(*this);
|
|
+ loadPtr(valaddr, scratch);
|
|
+ ma_bc(scratch, ImmWord(magic), label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestBigInt(Condition cond, const BaseIndex& address,
|
|
+ Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ computeEffectiveAddress(address, scratch2);
|
|
+ splitTag(scratch2, scratch2);
|
|
+ branchTestBigInt(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestBigInt(Condition cond, const ValueOperand& value,
|
|
+ Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ splitTag(value, scratch2);
|
|
+ branchTestBigInt(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestBigInt(Condition cond, Register tag,
|
|
+ Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ ma_bc(tag, ImmTag(JSVAL_TAG_BIGINT), label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestBigIntTruthy(bool b, const ValueOperand& value,
|
|
+ Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ unboxBigInt(value, scratch2);
|
|
+ ma_load(scratch2, Address(scratch2, BigInt::offsetOfDigitLength()), SizeWord, ZeroExtend);
|
|
+ ma_bc(scratch2, Imm32(0), label, b ? NotEqual : Equal);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTruncateDoubleMaybeModUint32(FloatRegister src, Register dest, Label* fail)
|
|
+{
|
|
+ MOZ_ASSERT(src != ScratchDoubleReg);
|
|
+
|
|
+ // If the conversion is invalid (see Power ISA v3.1, page 173), fail.
|
|
+ as_mtfsb0(23); // whack VXCVI
|
|
+ as_fctiwz(ScratchDoubleReg, src);
|
|
+ as_mcrfs(cr0, 5); // reserved - VXSOFT - VXSQRT - VXCVI
|
|
+ moveFromDouble(ScratchDoubleReg, dest);
|
|
+ as_srawi(dest, dest, 0); // clear upper word and sign extend
|
|
+ ma_bc(Assembler::SOBit, fail);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail)
|
|
+{
|
|
+ branchTruncateDoubleMaybeModUint32(src, dest, fail);
|
|
+}
|
|
+
|
|
+//}}} check_macroassembler_style
|
|
+// ===============================================================
|
|
+
|
|
+// The specializations for cmpPtrSet are outside the braces because check_macroassembler_style can't yet
|
|
+// deal with specializations.
|
|
+
|
|
+template<>
|
|
+inline void
|
|
+MacroAssembler::cmpPtrSet(Assembler::Condition cond, Address lhs, ImmPtr rhs,
|
|
+ Register dest)
|
|
+{
|
|
+ loadPtr(lhs, SecondScratchReg);
|
|
+ cmpPtrSet(cond, SecondScratchReg, rhs, dest);
|
|
+}
|
|
+
|
|
+template<>
|
|
+inline void
|
|
+MacroAssembler::cmpPtrSet(Assembler::Condition cond, Register lhs, Address rhs,
|
|
+ Register dest)
|
|
+{
|
|
+ loadPtr(rhs, ScratchRegister);
|
|
+ cmpPtrSet(cond, lhs, ScratchRegister, dest);
|
|
+}
|
|
+
|
|
+template<>
|
|
+inline void
|
|
+MacroAssembler::cmp32Set(Assembler::Condition cond, Register lhs, Address rhs,
|
|
+ Register dest)
|
|
+{
|
|
+ load32(rhs, ScratchRegister);
|
|
+ cmp32Set(cond, lhs, ScratchRegister, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::incrementInt32Value(const Address& addr)
|
|
+{
|
|
+ asMasm().add32(Imm32(1), addr);
|
|
+}
|
|
+
|
|
+/*
|
|
+void
|
|
+MacroAssemblerPPC64Compat::computeEffectiveAddress(const BaseIndex& address, Register dest)
|
|
+{
|
|
+ computeScaledAddress(address, dest);
|
|
+ if (address.offset)
|
|
+ asMasm().addPtr(Imm32(address.offset), dest);
|
|
+}
|
|
+*/
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::retn(Imm32 n)
|
|
+{
|
|
+ // pc <- [sp]; sp += n
|
|
+ loadPtr(Address(StackPointer, 0), ScratchRegister);
|
|
+ asMasm().addPtr(n, StackPointer);
|
|
+ xs_mtlr(ScratchRegister);
|
|
+ as_blr();
|
|
+}
|
|
+
|
|
+void MacroAssembler::load32SignExtendToPtr(const Address& src, Register dest)
|
|
+{
|
|
+ load32(src, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::moveFloat32ToGPR(FloatRegister src, Register dest)
|
|
+{
|
|
+ moveFromFloat32(src, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::moveGPRToFloat32(Register src, FloatRegister dest)
|
|
+{
|
|
+ moveToFloat32(src, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::move8SignExtend(Register src, Register dest)
|
|
+{
|
|
+ as_extsb(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::move16SignExtend(Register src, Register dest)
|
|
+{
|
|
+ as_extsh(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::loadAbiReturnAddress(Register dest)
|
|
+{
|
|
+ xs_mflr(dest);
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Logical instructions
|
|
+
|
|
+void
|
|
+MacroAssembler::not32(Register reg)
|
|
+{
|
|
+ as_nor(reg, reg, reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::and32(Register src, Register dest)
|
|
+{
|
|
+ as_and(dest, dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::and32(Imm32 imm, Register dest)
|
|
+{
|
|
+ ma_and(dest, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::and32(Imm32 imm, const Address& dest)
|
|
+{
|
|
+ ma_load(SecondScratchReg, dest, SizeWord, ZeroExtend);
|
|
+ ma_and(SecondScratchReg, imm);
|
|
+ store32(SecondScratchReg, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::and32(const Address& src, Register dest)
|
|
+{
|
|
+ ma_load(SecondScratchReg, src, SizeWord, ZeroExtend);
|
|
+ ma_and(dest, SecondScratchReg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::or32(Register src, Register dest)
|
|
+{
|
|
+ ma_or(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::or32(Imm32 imm, Register dest)
|
|
+{
|
|
+ ma_or(dest, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::or32(Imm32 imm, const Address& dest)
|
|
+{
|
|
+ ma_load(SecondScratchReg, dest, SizeWord, ZeroExtend);
|
|
+ ma_or(SecondScratchReg, imm);
|
|
+ store32(SecondScratchReg, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::xor32(Register src, Register dest)
|
|
+{
|
|
+ ma_xor(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::xor32(Imm32 imm, Register dest)
|
|
+{
|
|
+ ma_xor(dest, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::xor32(Imm32 imm, const Address &dest)
|
|
+{
|
|
+ ma_load(SecondScratchReg, dest, SizeWord, ZeroExtend);
|
|
+ ma_xor(SecondScratchReg, imm);
|
|
+ store32(SecondScratchReg, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::xor32(const Address& src, Register dest)
|
|
+{
|
|
+ ma_load(SecondScratchReg, src, SizeWord, ZeroExtend);
|
|
+ as_xor(dest, dest, SecondScratchReg);
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Arithmetic instructions
|
|
+
|
|
+void
|
|
+MacroAssembler::add32(Register src, Register dest)
|
|
+{
|
|
+ as_add(dest, dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::add32(Imm32 imm, Register dest)
|
|
+{
|
|
+ ma_add(dest, dest, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::add32(Imm32 imm, const Address& dest)
|
|
+{
|
|
+ load32(dest, SecondScratchReg);
|
|
+ ma_add(SecondScratchReg, imm);
|
|
+ store32(SecondScratchReg, dest);
|
|
+}
|
|
+
|
|
+void MacroAssembler::add32(Imm32 imm, Register src, Register dest) {
|
|
+ ma_add(dest, src, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::addPtr(Imm32 imm, const Address& dest)
|
|
+{
|
|
+ MOZ_ASSERT(dest.base != SecondScratchReg);
|
|
+ loadPtr(dest, SecondScratchReg);
|
|
+ addPtr(imm, SecondScratchReg);
|
|
+ storePtr(SecondScratchReg, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::addPtr(const Address& src, Register dest)
|
|
+{
|
|
+ loadPtr(src, ScratchRegister);
|
|
+ addPtr(ScratchRegister, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::addDouble(FloatRegister src, FloatRegister dest)
|
|
+{
|
|
+ as_fadd(dest, dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::addFloat32(FloatRegister src, FloatRegister dest)
|
|
+{
|
|
+ as_fadds(dest, dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::sub32(Register src, Register dest)
|
|
+{
|
|
+ as_subf(dest, src, dest); // T = B - A
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::sub32(Imm32 imm, Register dest)
|
|
+{
|
|
+ ma_subu(dest, dest, imm); // switched at MA level
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::sub32(const Address& src, Register dest)
|
|
+{
|
|
+ load32(src, SecondScratchReg);
|
|
+ as_subf(dest, SecondScratchReg, dest); // T = B - A
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::subPtr(Register src, const Address& dest)
|
|
+{
|
|
+ loadPtr(dest, SecondScratchReg);
|
|
+ subPtr(src, SecondScratchReg);
|
|
+ storePtr(SecondScratchReg, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::subPtr(const Address& addr, Register dest)
|
|
+{
|
|
+ loadPtr(addr, SecondScratchReg);
|
|
+ subPtr(SecondScratchReg, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::subDouble(FloatRegister src, FloatRegister dest)
|
|
+{
|
|
+ as_fsub(dest, dest, src); // T = A - B
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::subFloat32(FloatRegister src, FloatRegister dest)
|
|
+{
|
|
+ as_fsubs(dest, dest, src); // T = A - B
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::mul32(Imm32 rhs, Register srcDest)
|
|
+{
|
|
+ if (Imm16::IsInSignedRange(rhs.value)) {
|
|
+ // See also ::mulBy3
|
|
+ xs_sr_mulli(srcDest, srcDest, (int16_t)rhs.value);
|
|
+ } else {
|
|
+ // Wouldn't be any of our strength-reduced shortcuts anyway.
|
|
+ MOZ_ASSERT(srcDest != ScratchRegister);
|
|
+ ma_li(ScratchRegister, rhs);
|
|
+ as_mullw(srcDest, srcDest, ScratchRegister);
|
|
+ }
|
|
+ // Clear the upper 32 bits. We wouldn't use this for an intermediate
|
|
+ // multiply anyway. Do not sign extend.
|
|
+ as_rldicl(srcDest, srcDest, 0, 32); // "clrldi"
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::mul32(Register rhs, Register srcDest)
|
|
+{
|
|
+ as_mullw(srcDest, srcDest, rhs);
|
|
+ // Clear the upper 32 bits. Do not sign extend.
|
|
+ as_rldicl(srcDest, srcDest, 0, 32); // "clrldi"
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::mulFloat32(FloatRegister src, FloatRegister dest)
|
|
+{
|
|
+ as_fmuls(dest, dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::mulDouble(FloatRegister src, FloatRegister dest)
|
|
+{
|
|
+ as_fmul(dest, dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest)
|
|
+{
|
|
+ movePtr(imm, SecondScratchReg);
|
|
+ loadDouble(Address(SecondScratchReg, 0), ScratchDoubleReg);
|
|
+ mulDouble(ScratchDoubleReg, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::remainder32(Register rhs, Register srcDest, bool isUnsigned)
|
|
+{
|
|
+ if (HasPPCISA3()) {
|
|
+ if (isUnsigned) {
|
|
+ as_moduw(srcDest, srcDest, rhs);
|
|
+ } else {
|
|
+ as_modsw(srcDest, srcDest, rhs);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (isUnsigned)
|
|
+ as_divwu(ScratchRegister, srcDest, rhs);
|
|
+ else
|
|
+ as_divw(ScratchRegister, srcDest, rhs);
|
|
+ // Recover remainder.
|
|
+ as_mullw(SecondScratchReg, ScratchRegister, rhs);
|
|
+ as_subf(srcDest, SecondScratchReg, srcDest); // T = B - A
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::divFloat32(FloatRegister src, FloatRegister dest)
|
|
+{
|
|
+ as_fdivs(dest, dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::divDouble(FloatRegister src, FloatRegister dest)
|
|
+{
|
|
+ as_fdiv(dest, dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::neg32(Register reg)
|
|
+{
|
|
+ as_neg(reg, reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::negateDouble(FloatRegister reg)
|
|
+{
|
|
+ as_fneg(reg, reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::negateFloat(FloatRegister reg)
|
|
+{
|
|
+ as_fneg(reg, reg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::abs32(Register src, Register dest)
|
|
+{
|
|
+ // PowerPC Compiler Writer's Guide page 50
|
|
+ as_srawi(ScratchRegister, src, 31);
|
|
+ as_xor(SecondScratchReg, ScratchRegister, src);
|
|
+ as_subf(dest, ScratchRegister, SecondScratchReg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::absFloat32(FloatRegister src, FloatRegister dest)
|
|
+{
|
|
+ as_fabs(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::absDouble(FloatRegister src, FloatRegister dest)
|
|
+{
|
|
+ as_fabs(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::sqrtFloat32(FloatRegister src, FloatRegister dest)
|
|
+{
|
|
+ as_fsqrts(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::sqrtDouble(FloatRegister src, FloatRegister dest)
|
|
+{
|
|
+ as_fsqrt(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::minFloat32(FloatRegister other, FloatRegister srcDest, bool handleNaN)
|
|
+{
|
|
+ minMaxDouble(srcDest, other, handleNaN, false);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::minDouble(FloatRegister other, FloatRegister srcDest, bool handleNaN)
|
|
+{
|
|
+ minMaxDouble(srcDest, other, handleNaN, false);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::maxFloat32(FloatRegister other, FloatRegister srcDest, bool handleNaN)
|
|
+{
|
|
+ minMaxDouble(srcDest, other, handleNaN, true);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::maxDouble(FloatRegister other, FloatRegister srcDest, bool handleNaN)
|
|
+{
|
|
+ minMaxDouble(srcDest, other, handleNaN, true);
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Shift functions
|
|
+
|
|
+void
|
|
+MacroAssembler::lshift32(Register src, Register dest)
|
|
+{
|
|
+ // slw will zero out any shift amount greater than 32, but JavaScript
|
|
+ // expects this to act like a modulo, so ...
|
|
+ MOZ_ASSERT(src != ScratchRegister);
|
|
+ MOZ_ASSERT(dest != ScratchRegister);
|
|
+
|
|
+ as_andi_rc(ScratchRegister, src, 31);
|
|
+ as_slw(dest, dest, ScratchRegister);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::lshift32(Imm32 imm, Register dest)
|
|
+{
|
|
+ // Mod the constant directly, et voila.
|
|
+ x_slwi(dest, dest, imm.value % 32);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::flexibleLshift32(Register src, Register dest)
|
|
+{
|
|
+ lshift32(src, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::rshift32(Register src, Register dest)
|
|
+{
|
|
+ // Same deal.
|
|
+ MOZ_ASSERT(src != ScratchRegister);
|
|
+ MOZ_ASSERT(dest != ScratchRegister);
|
|
+
|
|
+ as_andi_rc(ScratchRegister, src, 31);
|
|
+ as_srw(dest, dest, ScratchRegister);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::rshift32(Imm32 imm, Register dest)
|
|
+{
|
|
+ // Same deal with a twist:
|
|
+ // If imm.value is a multiple of 32, then n = 0, and we assert because
|
|
+ // the underlying rlwinm has to encode 32 in a 5-bit field. So, if the
|
|
+ // modulo is zero, do nothing or load zero instead (based on what srawi
|
|
+ // does on 64-bit systems, minus the sign extension).
|
|
+ if (!(imm.value % 32)) {
|
|
+ if (imm.value == 0) {
|
|
+ // No-op // as_rldicl(dest, dest, 0, 32); // "clrldi"
|
|
+ } else {
|
|
+ // Right-shifting a 32-bit quantity 32 bits is zero.
|
|
+ xs_li(dest, 0);
|
|
+ }
|
|
+ } else {
|
|
+ x_srwi(dest, dest, imm.value % 32);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::flexibleRshift32(Register src, Register dest)
|
|
+{
|
|
+ rshift32(src, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::rshift32Arithmetic(Register src, Register dest)
|
|
+{
|
|
+ // Same deal.
|
|
+ MOZ_ASSERT(src != ScratchRegister);
|
|
+ as_andi_rc(ScratchRegister, src, 31);
|
|
+ as_sraw(dest, dest, ScratchRegister);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::rshift32Arithmetic(Imm32 imm, Register dest)
|
|
+{
|
|
+ as_srawi(dest, dest, imm.value % 32);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::flexibleRshift32Arithmetic(Register src, Register dest)
|
|
+{
|
|
+ rshift32Arithmetic(src, dest);
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Rotation functions (32-bit unless specified otherwise)
|
|
+void
|
|
+MacroAssembler::rotateLeft(Imm32 count, Register input, Register dest)
|
|
+{
|
|
+ if (count.value) {
|
|
+ as_rlwinm(dest, input, count.value % 32, 0, 31); // "rotlwi"
|
|
+ } else {
|
|
+ if (input != dest)
|
|
+ ma_move(dest, input);
|
|
+ }
|
|
+}
|
|
+void
|
|
+MacroAssembler::rotateLeft(Register count, Register input, Register dest)
|
|
+{
|
|
+ as_rlwnm(dest, input, count, 0, 31); // "rotlw"
|
|
+}
|
|
+void
|
|
+MacroAssembler::rotateRight(Imm32 count, Register input, Register dest)
|
|
+{
|
|
+ if (count.value) {
|
|
+ as_rlwinm(dest, input, 32 - (count.value % 32), 0, 31); // "rotrwi"
|
|
+ } else {
|
|
+ if (input != dest)
|
|
+ ma_move(dest, input);
|
|
+ }
|
|
+}
|
|
+void
|
|
+MacroAssembler::rotateRight(Register count, Register input, Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(input != ScratchRegister);
|
|
+ MOZ_ASSERT(count != ScratchRegister);
|
|
+ MOZ_ASSERT(input != SecondScratchReg);
|
|
+ MOZ_ASSERT(count != SecondScratchReg);
|
|
+
|
|
+ xs_li(ScratchRegister, 32);
|
|
+ as_andi_rc(SecondScratchReg, count, 31);
|
|
+ as_subf(ScratchRegister, SecondScratchReg, ScratchRegister); // T = B - A
|
|
+ as_rlwnm(dest, input, ScratchRegister, 0, 31); // "rotrw"
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Bit counting functions
|
|
+
|
|
+void
|
|
+MacroAssembler::clz32(Register src, Register dest, bool knownNotZero)
|
|
+{
|
|
+ as_cntlzw(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::ctz32(Register src, Register dest, bool knownNotZero)
|
|
+{
|
|
+ as_cnttzw(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::popcnt32(Register input, Register output, Register tmp)
|
|
+{
|
|
+ // Sing to the tune of Revolution No. 9:
|
|
+ // POWER9
|
|
+ // POWER9
|
|
+ // POWER9
|
|
+ // POWER9 etc.
|
|
+ as_popcntw(output, input);
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Condition functions
|
|
+
|
|
+void MacroAssembler::cmp8Set(Condition cond, Address lhs, Imm32 rhs,
|
|
+ Register dest) {
|
|
+ MOZ_ASSERT(lhs.base != SecondScratchReg);
|
|
+
|
|
+ // Automatically generates a 32-bit compare.
|
|
+ switch (cond) {
|
|
+ case Assembler::Equal:
|
|
+ case Assembler::NotEqual:
|
|
+ case Assembler::Above:
|
|
+ case Assembler::AboveOrEqual:
|
|
+ case Assembler::Below:
|
|
+ case Assembler::BelowOrEqual:
|
|
+ load8ZeroExtend(lhs, SecondScratchReg);
|
|
+ ma_cmp_set(dest, SecondScratchReg, Imm32(uint8_t(rhs.value)), cond);
|
|
+ break;
|
|
+
|
|
+ case Assembler::GreaterThan:
|
|
+ case Assembler::GreaterThanOrEqual:
|
|
+ case Assembler::LessThan:
|
|
+ case Assembler::LessThanOrEqual:
|
|
+ load8SignExtend(lhs, SecondScratchReg);
|
|
+ ma_cmp_set(dest, SecondScratchReg, Imm32(int8_t(rhs.value)), cond);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ MOZ_CRASH("unexpected condition");
|
|
+ }
|
|
+}
|
|
+
|
|
+void MacroAssembler::cmp16Set(Condition cond, Address lhs, Imm32 rhs,
|
|
+ Register dest) {
|
|
+ MOZ_ASSERT(lhs.base != SecondScratchReg);
|
|
+
|
|
+ // Automatically generates a 32-bit compare.
|
|
+ switch (cond) {
|
|
+ case Assembler::Equal:
|
|
+ case Assembler::NotEqual:
|
|
+ case Assembler::Above:
|
|
+ case Assembler::AboveOrEqual:
|
|
+ case Assembler::Below:
|
|
+ case Assembler::BelowOrEqual:
|
|
+ load16ZeroExtend(lhs, SecondScratchReg);
|
|
+ ma_cmp_set(dest, SecondScratchReg, Imm32(uint16_t(rhs.value)), cond);
|
|
+ break;
|
|
+
|
|
+ case Assembler::GreaterThan:
|
|
+ case Assembler::GreaterThanOrEqual:
|
|
+ case Assembler::LessThan:
|
|
+ case Assembler::LessThanOrEqual:
|
|
+ load16SignExtend(lhs, SecondScratchReg);
|
|
+ ma_cmp_set(dest, SecondScratchReg, Imm32(int16_t(rhs.value)), cond);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ MOZ_CRASH("unexpected condition");
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+// ===============================================================
|
|
+// Branch functions
|
|
+
|
|
+void MacroAssembler::branch8(Condition cond, const Address& lhs, Imm32 rhs,
|
|
+ Label* label) {
|
|
+ MOZ_ASSERT(lhs.base != SecondScratchReg);
|
|
+
|
|
+ switch (cond) {
|
|
+ case Assembler::Equal:
|
|
+ case Assembler::NotEqual:
|
|
+ case Assembler::Above:
|
|
+ case Assembler::AboveOrEqual:
|
|
+ case Assembler::Below:
|
|
+ case Assembler::BelowOrEqual:
|
|
+ load8ZeroExtend(lhs, SecondScratchReg);
|
|
+ branch32(cond, SecondScratchReg, Imm32(uint8_t(rhs.value)), label);
|
|
+ break;
|
|
+
|
|
+ case Assembler::GreaterThan:
|
|
+ case Assembler::GreaterThanOrEqual:
|
|
+ case Assembler::LessThan:
|
|
+ case Assembler::LessThanOrEqual:
|
|
+ load8SignExtend(lhs, SecondScratchReg);
|
|
+ branch32(cond, SecondScratchReg, Imm32(int8_t(rhs.value)), label);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ MOZ_CRASH("unexpected condition");
|
|
+ }
|
|
+}
|
|
+
|
|
+void MacroAssembler::branch8(Condition cond, const BaseIndex& lhs, Register rhs,
|
|
+ Label* label) {
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ MOZ_ASSERT(scratch2 != lhs.base);
|
|
+
|
|
+ computeScaledAddress(lhs, scratch2);
|
|
+
|
|
+ switch (cond) {
|
|
+ case Assembler::Equal:
|
|
+ case Assembler::NotEqual:
|
|
+ case Assembler::Above:
|
|
+ case Assembler::AboveOrEqual:
|
|
+ case Assembler::Below:
|
|
+ case Assembler::BelowOrEqual:
|
|
+ load8ZeroExtend(Address(scratch2, lhs.offset), scratch2);
|
|
+ branch32(cond, scratch2, rhs, label);
|
|
+ break;
|
|
+
|
|
+ case Assembler::GreaterThan:
|
|
+ case Assembler::GreaterThanOrEqual:
|
|
+ case Assembler::LessThan:
|
|
+ case Assembler::LessThanOrEqual:
|
|
+ load8SignExtend(Address(scratch2, lhs.offset), scratch2);
|
|
+ branch32(cond, scratch2, rhs, label);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ MOZ_CRASH("unexpected condition");
|
|
+ }
|
|
+}
|
|
+
|
|
+void MacroAssembler::branch16(Condition cond, const Address& lhs, Imm32 rhs,
|
|
+ Label* label) {
|
|
+ MOZ_ASSERT(lhs.base != SecondScratchReg);
|
|
+
|
|
+ switch (cond) {
|
|
+ case Assembler::Equal:
|
|
+ case Assembler::NotEqual:
|
|
+ case Assembler::Above:
|
|
+ case Assembler::AboveOrEqual:
|
|
+ case Assembler::Below:
|
|
+ case Assembler::BelowOrEqual:
|
|
+ load16ZeroExtend(lhs, SecondScratchReg);
|
|
+ branch32(cond, SecondScratchReg, Imm32(uint16_t(rhs.value)), label);
|
|
+ break;
|
|
+
|
|
+ case Assembler::GreaterThan:
|
|
+ case Assembler::GreaterThanOrEqual:
|
|
+ case Assembler::LessThan:
|
|
+ case Assembler::LessThanOrEqual:
|
|
+ load16SignExtend(lhs, SecondScratchReg);
|
|
+ branch32(cond, SecondScratchReg, Imm32(int16_t(rhs.value)), label);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ MOZ_CRASH("unexpected condition");
|
|
+ }
|
|
+}
|
|
+
|
|
+// XXX: need 32-bit hints here
|
|
+template <class L>
|
|
+void
|
|
+MacroAssembler::branch32(Condition cond, Register lhs, Register rhs, L label)
|
|
+{
|
|
+ ma_bc(lhs, rhs, label, cond);
|
|
+}
|
|
+
|
|
+template <class L>
|
|
+void
|
|
+MacroAssembler::branch32(Condition cond, Register lhs, Imm32 imm, L label)
|
|
+{
|
|
+ ma_bc(lhs, imm, label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branch32(Condition cond, const Address& lhs, Register rhs, Label* label)
|
|
+{
|
|
+ load32(lhs, SecondScratchReg);
|
|
+ ma_bc(SecondScratchReg, rhs, label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branch32(Condition cond, const Address& lhs, Imm32 rhs, Label* label)
|
|
+{
|
|
+ load32(lhs, SecondScratchReg);
|
|
+ ma_bc(SecondScratchReg, rhs, label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branch32(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label)
|
|
+{
|
|
+ load32(lhs, SecondScratchReg);
|
|
+ ma_bc(SecondScratchReg, rhs, label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branch32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, Label* label)
|
|
+{
|
|
+ load32(lhs, SecondScratchReg);
|
|
+ ma_bc(SecondScratchReg, rhs, label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branch32(Condition cond, const BaseIndex& lhs, Imm32 rhs, Label* label)
|
|
+{
|
|
+ load32(lhs, SecondScratchReg);
|
|
+ ma_bc(SecondScratchReg, rhs, label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress addr, Imm32 imm, Label* label)
|
|
+{
|
|
+ load32(addr, SecondScratchReg);
|
|
+ ma_bc(SecondScratchReg, imm, label, cond);
|
|
+}
|
|
+
|
|
+template <class L>
|
|
+void
|
|
+MacroAssembler::branchPtr(Condition cond, Register lhs, Register rhs, L label)
|
|
+{
|
|
+ ma_bc(lhs, rhs, label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label)
|
|
+{
|
|
+ // Need to hint the MacroAssembler that this needs a 64-bit compare.
|
|
+ ma_bc64(lhs, rhs, label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchPtr(Condition cond, Register lhs, ImmPtr rhs, Label* label)
|
|
+{
|
|
+ ma_bc(lhs, rhs, label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchPtr(Condition cond, Register lhs, ImmGCPtr rhs, Label* label)
|
|
+{
|
|
+ ma_bc(lhs, rhs, label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchPtr(Condition cond, Register lhs, ImmWord rhs, Label* label)
|
|
+{
|
|
+ ma_bc(lhs, rhs, label, cond);
|
|
+}
|
|
+
|
|
+template <class L>
|
|
+void
|
|
+MacroAssembler::branchPtr(Condition cond, const Address& lhs, Register rhs, L label)
|
|
+{
|
|
+ MOZ_ASSERT(rhs != SecondScratchReg);
|
|
+ loadPtr(lhs, SecondScratchReg);
|
|
+ branchPtr(cond, SecondScratchReg, rhs, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmPtr rhs, Label* label)
|
|
+{
|
|
+ loadPtr(lhs, SecondScratchReg);
|
|
+ branchPtr(cond, SecondScratchReg, rhs, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmGCPtr rhs, Label* label)
|
|
+{
|
|
+ loadPtr(lhs, SecondScratchReg);
|
|
+ branchPtr(cond, SecondScratchReg, rhs, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmWord rhs, Label* label)
|
|
+{
|
|
+ loadPtr(lhs, SecondScratchReg);
|
|
+ branchPtr(cond, SecondScratchReg, rhs, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchPtr(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(rhs != SecondScratchReg);
|
|
+ loadPtr(lhs, SecondScratchReg);
|
|
+ branchPtr(cond, SecondScratchReg, rhs, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchPtr(Condition cond, const AbsoluteAddress& lhs, ImmWord rhs, Label* label)
|
|
+{
|
|
+ loadPtr(lhs, SecondScratchReg);
|
|
+ branchPtr(cond, SecondScratchReg, rhs, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchPtr(Condition cond, wasm::SymbolicAddress lhs, Register rhs, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(rhs != SecondScratchReg);
|
|
+ loadPtr(lhs, SecondScratchReg);
|
|
+ branchPtr(cond, SecondScratchReg, rhs, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchPtr(Condition cond, const BaseIndex& lhs, ImmWord rhs, Label* label)
|
|
+{
|
|
+ loadPtr(lhs, SecondScratchReg);
|
|
+ branchPtr(cond, SecondScratchReg, rhs, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchPtr(Condition cond, const BaseIndex& lhs, Register rhs, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(rhs != SecondScratchReg);
|
|
+ loadPtr(lhs, SecondScratchReg);
|
|
+ branchPtr(cond, SecondScratchReg, rhs, label);
|
|
+}
|
|
+
|
|
+void MacroAssembler::branchTestValue(Condition cond, const BaseIndex& lhs,
|
|
+ const ValueOperand& rhs, Label* label) {
|
|
+ MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
|
|
+ branchPtr(cond, lhs, rhs.valueReg(), label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchFloat(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
|
|
+ Label* label)
|
|
+{
|
|
+ ma_bc(cond, lhs, rhs, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTruncateFloat32ToInt32(FloatRegister src, Register dest, Label* fail)
|
|
+{
|
|
+ truncDoubleToInt32(src, dest, fail);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
|
|
+ Label* label)
|
|
+{
|
|
+ ma_bc(cond, lhs, rhs, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail)
|
|
+{
|
|
+ truncDoubleToInt32(src, dest, fail);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchMulPtr(Condition cond, Register src, Register dest, Label *overflow)
|
|
+{
|
|
+ as_mulldo_rc(dest, src, dest);
|
|
+ ma_bc(cond, overflow);
|
|
+}
|
|
+
|
|
+// The src type will autodetermine 32-bit mode.
|
|
+template <typename T>
|
|
+void
|
|
+MacroAssembler::branchAdd32(Condition cond, T src, Register dest, Label* overflow)
|
|
+{
|
|
+ switch (cond) {
|
|
+ case Overflow:
|
|
+ ma_addTestOverflow(dest, dest, src, overflow);
|
|
+ break;
|
|
+ case CarryClear:
|
|
+ case CarrySet:
|
|
+ ma_addTestCarry(cond, dest, dest, src, overflow);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("NYI");
|
|
+ }
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+void
|
|
+MacroAssembler::branchSub32(Condition cond, T src, Register dest, Label* overflow)
|
|
+{
|
|
+ switch (cond) {
|
|
+ case Overflow:
|
|
+ ma_subTestOverflow(dest, dest, src, overflow);
|
|
+ break;
|
|
+ case NonZero:
|
|
+ case Zero:
|
|
+ case NotSigned:
|
|
+ case Signed:
|
|
+ ma_subu(dest, src);
|
|
+ ma_bc(dest, dest, overflow, cond);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("NYI");
|
|
+ }
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+void
|
|
+MacroAssembler::branchAddPtr(Condition cond, T src, Register dest, Label* overflow)
|
|
+{
|
|
+ switch (cond) {
|
|
+ case Overflow:
|
|
+ ma_addTestOverflow(dest, dest, src, overflow, /* is32 */ false);
|
|
+ break;
|
|
+ case CarryClear:
|
|
+ case CarrySet:
|
|
+ ma_addTestCarry(cond, dest, dest, src, overflow, /* is32 */ false);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("NYI");
|
|
+ }
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+void
|
|
+MacroAssembler::branchSubPtr(Condition cond, T src, Register dest, Label* overflow)
|
|
+{
|
|
+ switch (cond) {
|
|
+ case Overflow:
|
|
+ ma_subTestOverflow(dest, dest, src, overflow, /* is32 */ false);
|
|
+ break;
|
|
+ case NonZero:
|
|
+ case Zero:
|
|
+ case NotSigned:
|
|
+ case Signed:
|
|
+ ma_subu(dest, src);
|
|
+ ma_bc(dest, dest, overflow, cond);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("NYI");
|
|
+ }
|
|
+}
|
|
+
|
|
+template <>
|
|
+inline void
|
|
+MacroAssembler::branchMul32(Condition cond, Register src, Register dest, Label* label)
|
|
+{
|
|
+ as_mullwo_rc(dest, dest, src);
|
|
+ ma_bc(cond, label);
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+void
|
|
+MacroAssembler::branchMul32(Condition cond, T src, Register dest, Label* label)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+template <>
|
|
+inline void
|
|
+MacroAssembler::branchRshift32(Condition cond, Imm32 shift, Register srcDest,
|
|
+ Label *label)
|
|
+{
|
|
+ as_rlwinm_rc(srcDest, srcDest, 32 - shift.value, shift.value, 31);
|
|
+ ma_bc(cond, label);
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+void
|
|
+MacroAssembler::branchRshift32(Condition cond, T shift, Register srcDest,
|
|
+ Label *label)
|
|
+{
|
|
+ MOZ_CRASH("No default implementation");
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::decBranchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label)
|
|
+{
|
|
+ subPtr(rhs, lhs);
|
|
+ branchPtr(cond, lhs, Imm32(0), label);
|
|
+}
|
|
+
|
|
+template <class L>
|
|
+void
|
|
+MacroAssembler::branchTest32(Condition cond, Register lhs, Register rhs, L label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
|
|
+ if (cond == Signed || cond == NotSigned) {
|
|
+ MOZ_ASSERT(lhs == rhs);
|
|
+ // Sign extend first.
|
|
+ as_extsw(lhs, lhs);
|
|
+ }
|
|
+ if (lhs == rhs) {
|
|
+ ma_bc(lhs, rhs, label, cond);
|
|
+ } else {
|
|
+ // XXX: trim to 32 bits?
|
|
+ as_and(ScratchRegister, lhs, rhs);
|
|
+ ma_bc(ScratchRegister, ScratchRegister, label, cond);
|
|
+ }
|
|
+}
|
|
+
|
|
+template <class L>
|
|
+void
|
|
+MacroAssembler::branchTest32(Condition cond, Register lhs, Imm32 rhs, L label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Zero || cond == NonZero);
|
|
+ // XXX: trim to 32 bits?
|
|
+ ma_and(ScratchRegister, lhs, rhs);
|
|
+ ma_bc(ScratchRegister, ScratchRegister, label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTest32(Condition cond, const Address& lhs, Imm32 rhs, Label* label)
|
|
+{
|
|
+ load32(lhs, SecondScratchReg);
|
|
+ branchTest32(cond, SecondScratchReg, rhs, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTest32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, Label* label)
|
|
+{
|
|
+ load32(lhs, SecondScratchReg);
|
|
+ branchTest32(cond, SecondScratchReg, rhs, label);
|
|
+}
|
|
+
|
|
+// XXX: Improve branchTest* to use |and.| and test results directly, as noted:
|
|
+template <class L>
|
|
+void
|
|
+MacroAssembler::branchTestPtr(Condition cond, Register lhs, Register rhs, L label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
|
|
+ if (lhs == rhs) {
|
|
+ ma_bc(lhs, rhs, label, cond);
|
|
+ } else {
|
|
+ as_and(ScratchRegister, lhs, rhs);
|
|
+ ma_bc(ScratchRegister, ScratchRegister, label, cond);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestPtr(Condition cond, Register lhs, Imm32 rhs, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
|
|
+ ma_and(ScratchRegister, lhs, rhs);
|
|
+ ma_bc(ScratchRegister, ScratchRegister, label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestPtr(Condition cond, const Address& lhs, Imm32 rhs, Label* label)
|
|
+{
|
|
+ loadPtr(lhs, SecondScratchReg);
|
|
+ branchTestPtr(cond, SecondScratchReg, rhs, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestUndefined(Condition cond, Register tag, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ ma_bc(tag, ImmTag(JSVAL_TAG_UNDEFINED), label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestUndefined(Condition cond, const Address& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestUndefined(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestUndefined(Condition cond, const BaseIndex& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestUndefined(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestInt32(Condition cond, Register tag, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ ma_bc(tag, ImmTag(JSVAL_TAG_INT32), label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestInt32(Condition cond, const Address& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestInt32(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestInt32(Condition cond, const BaseIndex& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestInt32(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestDouble(Condition cond, const Address& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestDouble(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestDouble(Condition cond, const BaseIndex& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestDouble(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestDoubleTruthy(bool b, FloatRegister value, Label* label)
|
|
+{
|
|
+ ma_lid(ScratchDoubleReg, 0.0);
|
|
+ DoubleCondition cond = b ? DoubleNotEqual : DoubleEqualOrUnordered;
|
|
+ ma_bc(cond, value, ScratchDoubleReg, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestNumber(Condition cond, Register tag, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ Condition actual = cond == Equal ? BelowOrEqual : Above;
|
|
+ ma_bc(tag, ImmTag(JS::detail::ValueUpperInclNumberTag), label, actual);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestBoolean(Condition cond, Register tag, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ ma_bc(tag, ImmTag(JSVAL_TAG_BOOLEAN), label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestBoolean(Condition cond, const Address& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestBoolean(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestBoolean(Condition cond, const BaseIndex& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestBoolean(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestString(Condition cond, Register tag, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ ma_bc(tag, ImmTag(JSVAL_TAG_STRING), label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestString(Condition cond, const Address& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestString(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestString(Condition cond, const BaseIndex& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestString(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestSymbol(Condition cond, Register tag, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ ma_bc(tag, ImmTag(JSVAL_TAG_SYMBOL), label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestSymbol(Condition cond, const BaseIndex& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestSymbol(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestNull(Condition cond, Register tag, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ ma_bc(tag, ImmTag(JSVAL_TAG_NULL), label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestNull(Condition cond, const Address& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestNull(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestNull(Condition cond, const BaseIndex& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestNull(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestObject(Condition cond, Register tag, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ ma_bc(tag, ImmTag(JSVAL_TAG_OBJECT), label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestObject(Condition cond, const Address& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestObject(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestObject(Condition cond, const BaseIndex& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestObject(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestGCThing(Condition cond, const Address& address, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ ma_bc(scratch2, ImmTag(JS::detail::ValueLowerInclGCThingTag), label,
|
|
+ (cond == Equal) ? AboveOrEqual : Below);
|
|
+}
|
|
+void
|
|
+MacroAssembler::branchTestGCThing(Condition cond, const BaseIndex& address, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ ma_bc(scratch2, ImmTag(JS::detail::ValueLowerInclGCThingTag), label,
|
|
+ (cond == Equal) ? AboveOrEqual : Below);
|
|
+}
|
|
+void
|
|
+MacroAssembler::branchTestGCThing(Condition cond, const ValueOperand& address, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ ma_bc(scratch2, ImmTag(JS::detail::ValueLowerInclGCThingTag), label,
|
|
+ (cond == Equal) ? AboveOrEqual : Below);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestPrimitive(Condition cond, Register tag, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ ma_bc(tag, ImmTag(JS::detail::ValueUpperExclPrimitiveTag), label,
|
|
+ (cond == Equal) ? Below : AboveOrEqual);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestMagic(Condition cond, Register tag, Label* label)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ ma_bc(tag, ImmTag(JSVAL_TAG_MAGIC), label, cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestMagic(Condition cond, const Address& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestMagic(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestMagic(Condition cond, const BaseIndex& address, Label* label)
|
|
+{
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ extractTag(address, scratch2);
|
|
+ branchTestMagic(cond, scratch2, label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchToComputedAddress(const BaseIndex& addr)
|
|
+{
|
|
+ // TODO: We should just be able to call loadPtr here, but we get values
|
|
+ // with the high word still set.
|
|
+ //loadPtr(addr, ScratchRegister);
|
|
+
|
|
+ int32_t shift = Imm32::ShiftOf(addr.scale).value;
|
|
+ MOZ_ASSERT(addr.base != ScratchRegister);
|
|
+ MOZ_ASSERT(addr.index != ScratchRegister);
|
|
+ MOZ_ASSERT(addr.base != SecondScratchReg);
|
|
+ MOZ_ASSERT(addr.index != SecondScratchReg);
|
|
+
|
|
+ // Ass-U-Me that addr.index is always positive.
|
|
+ if (shift) {
|
|
+ ma_dsll(ScratchRegister, addr.index, Imm32(shift));
|
|
+ as_rldicl(ScratchRegister, ScratchRegister, 0, 32); // "clrldi"
|
|
+ as_add(SecondScratchReg, addr.base, ScratchRegister);
|
|
+ } else {
|
|
+ // XXX: merge with following ld into ldx
|
|
+ as_add(SecondScratchReg, addr.base, addr.index);
|
|
+ }
|
|
+ as_ld(SecondScratchReg, SecondScratchReg, 0);
|
|
+ xs_mtctr(SecondScratchReg);
|
|
+ as_bctr(DontLinkB);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::cmp32Move32(Condition cond, Register lhs, Register rhs, Register src,
|
|
+ Register dest)
|
|
+{
|
|
+ ma_cmp32(lhs, rhs, cond);
|
|
+ // Ass-U-Me that ma_cmp32 selected the correct compare, and mask off any
|
|
+ // synthetic bits. isel will assert on any conditions it can't encode.
|
|
+ as_isel(dest, src, dest, (cond & 0xff));
|
|
+}
|
|
+
|
|
+void MacroAssembler::cmp32Move32(Condition cond, Register lhs, Imm32 rhs,
|
|
+ Register src, Register dest) {
|
|
+ MOZ_ASSERT(lhs != SecondScratchReg && src != SecondScratchReg &&
|
|
+ dest != SecondScratchReg);
|
|
+ ma_li(SecondScratchReg, rhs); // XXX: probably could be r0
|
|
+ cmp32Move32(cond, lhs, SecondScratchReg, src, dest);
|
|
+}
|
|
+
|
|
+// better version? below
|
|
+#if(0)
|
|
+void MacroAssembler::cmp32Move32(Condition cond, Register lhs,
|
|
+ const Address& rhs, Register src,
|
|
+ Register dest) {
|
|
+ MOZ_ASSERT(lhs != ScratchRegister && src != ScratchRegister &&
|
|
+ dest != ScratchRegister);
|
|
+ load32(rhs, ScratchRegister); // XXX: is r12 live?
|
|
+ cmp32Move32(cond, lhs, ScratchRegister, src, dest);
|
|
+}
|
|
+#endif
|
|
+
|
|
+void
|
|
+MacroAssembler::cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs, Register src,
|
|
+ Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ ma_cmp32(lhs, rhs, cond);
|
|
+ // isel cannot test for the absence of a bit.
|
|
+ if (cond == Equal) {
|
|
+ as_isel(dest, src, dest, Equal);
|
|
+ } else {
|
|
+ // Flip the order.
|
|
+ as_isel(dest, dest, src, Equal);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::cmp32Move32(Condition cond, Register lhs, const Address& rhs,
|
|
+ Register src,
|
|
+ Register dest)
|
|
+{
|
|
+ if (cond == Below) {
|
|
+ ma_cmp32(lhs, rhs, cond);
|
|
+ as_isel(dest, src, dest, LessThan);
|
|
+ return;
|
|
+ }
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ ma_cmp32(lhs, rhs, cond);
|
|
+ if (cond == Equal) {
|
|
+ as_isel(dest, src, dest, Equal);
|
|
+ } else {
|
|
+ as_isel(dest, dest, src, Equal);
|
|
+ }
|
|
+}
|
|
+
|
|
+void MacroAssembler::cmpPtrMovePtr(Condition cond, Register lhs, Register rhs,
|
|
+ Register src, Register dest) {
|
|
+ if (cond == Below) {
|
|
+ as_cmpld(lhs, rhs);
|
|
+ as_isel(dest, src, dest, LessThan);
|
|
+ return;
|
|
+ }
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ as_cmpd(lhs, rhs);
|
|
+ if (cond == Equal) {
|
|
+ as_isel(dest, src, dest, Equal);
|
|
+ } else {
|
|
+ as_isel(dest, dest, src, Equal);
|
|
+ }
|
|
+}
|
|
+
|
|
+void MacroAssembler::cmpPtrMovePtr(Condition cond, Register lhs,
|
|
+ const Address& rhs, Register src,
|
|
+ Register dest) {
|
|
+ MOZ_ASSERT(lhs != ScratchRegister);
|
|
+ MOZ_ASSERT(src != ScratchRegister);
|
|
+ MOZ_ASSERT(lhs != SecondScratchReg); // may be used by ma_load
|
|
+ MOZ_ASSERT(src != SecondScratchReg);
|
|
+ MOZ_ASSERT(dest != ScratchRegister);
|
|
+ MOZ_ASSERT(dest != SecondScratchReg);
|
|
+
|
|
+ ma_load(ScratchRegister, rhs, SizeDouble);
|
|
+ cmpPtrMovePtr(cond, lhs, ScratchRegister, src, dest);
|
|
+}
|
|
+
|
|
+void MacroAssembler::cmp32Load32(Condition cond, Register lhs,
|
|
+ const Address& rhs, const Address& src,
|
|
+ Register dest) {
|
|
+ MOZ_ASSERT(lhs != ScratchRegister && dest != ScratchRegister);
|
|
+ load32(rhs, ScratchRegister);
|
|
+ cmp32Load32(cond, lhs, ScratchRegister, src, dest);
|
|
+}
|
|
+
|
|
+void MacroAssembler::cmp32Load32(Condition cond, Register lhs, Register rhs,
|
|
+ const Address& src, Register dest) {
|
|
+ Label skip;
|
|
+ // XXX: turn into a load and isel
|
|
+ branch32(Assembler::InvertCondition(cond), lhs, rhs, &skip);
|
|
+ load32(src, dest);
|
|
+ bind(&skip);
|
|
+}
|
|
+
|
|
+void MacroAssembler::cmp32Load32(Condition cond, Register lhs, Imm32 rhs,
|
|
+ const Address& src, Register dest) {
|
|
+ Label skip;
|
|
+ // XXX: turn into a load and isel
|
|
+ branch32(Assembler::InvertCondition(cond), lhs, rhs, &skip);
|
|
+ load32(src, dest);
|
|
+ bind(&skip);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::cmp32LoadPtr(Condition cond, const Address& lhs, Imm32 rhs,
|
|
+ const Address& src, Register dest)
|
|
+{
|
|
+ Label skip;
|
|
+ // XXX: turn into a load and isel
|
|
+ branch32(Assembler::InvertCondition(cond), lhs, rhs, &skip);
|
|
+ loadPtr(src, dest);
|
|
+ bind(&skip);
|
|
+}
|
|
+
|
|
+// Constant-time conditional moves (basically isel all the things, because
|
|
+// it is not subject to branch prediction).
|
|
+void
|
|
+MacroAssembler::test32LoadPtr(Condition cond, const Address& addr, Imm32 mask,
|
|
+ const Address& src, Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Zero || cond == NonZero);
|
|
+ const uint32_t mm = mask.value;
|
|
+
|
|
+ ma_load(ScratchRegister, addr, SizeWord, ZeroExtend);
|
|
+ if (Imm16::IsInUnsignedRange(mm)) {
|
|
+ as_andi_rc(ScratchRegister, ScratchRegister, mm); // -> CR0[EQ]
|
|
+ } else {
|
|
+ ma_li(SecondScratchReg, mm);
|
|
+ as_and_rc(ScratchRegister, ScratchRegister, SecondScratchReg);
|
|
+ }
|
|
+ ma_load(SecondScratchReg, src, SizeDouble); // pointer-sized
|
|
+ // If the condition is true, set dest to src. However, isel cannot
|
|
+ // test for the absence of a bit, and it cannot test for multiple bits, so
|
|
+ // footwork is required.
|
|
+ if (cond == Zero) {
|
|
+ MOZ_ASSERT(cond == (Equal | ConditionZero));
|
|
+ as_isel(dest, SecondScratchReg, dest, Assembler::Equal);
|
|
+ } else {
|
|
+ // Flip the order.
|
|
+ MOZ_ASSERT(cond == (NotEqual | ConditionZero));
|
|
+ as_isel(dest, dest, SecondScratchReg, Assembler::Equal);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::test32MovePtr(Condition cond, const Address& addr, Imm32 mask,
|
|
+ Register src, Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Zero || cond == NonZero);
|
|
+ MOZ_ASSERT(src != ScratchRegister);
|
|
+ MOZ_ASSERT(src != SecondScratchReg);
|
|
+ const uint32_t mm = mask.value;
|
|
+
|
|
+ ma_load(ScratchRegister, addr, SizeWord, ZeroExtend);
|
|
+ if (Imm16::IsInUnsignedRange(mm)) {
|
|
+ as_andi_rc(ScratchRegister, ScratchRegister, mm); // -> CR0[EQ]
|
|
+ } else {
|
|
+ ma_li(SecondScratchReg, mm);
|
|
+ as_and_rc(ScratchRegister, ScratchRegister, SecondScratchReg);
|
|
+ }
|
|
+ if (cond == Zero) {
|
|
+ MOZ_ASSERT(cond == (Equal | ConditionZero));
|
|
+ as_isel(dest, src, dest, Assembler::Equal);
|
|
+ } else {
|
|
+ // Flip the order.
|
|
+ MOZ_ASSERT(cond == (NotEqual | ConditionZero));
|
|
+ as_isel(dest, dest, src, Assembler::Equal);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::spectreBoundsCheck32(Register index, Register length,
|
|
+ Register maybeScratch, Label* failure)
|
|
+{
|
|
+ branch32(Assembler::BelowOrEqual, length, index, failure);
|
|
+ if (JitOptions.spectreIndexMasking) {
|
|
+ // The result of the compare is still in cr0, and the compare was
|
|
+ // already done unsigned, so we just generate an iselgt. The second
|
|
+ // register is unimportant, because we know this will always be true.
|
|
+ as_isel(index, index, length, Assembler::GreaterThan);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::spectreBoundsCheck32(Register index, const Address& length,
|
|
+ Register maybeScratch, Label* failure)
|
|
+{
|
|
+ branch32(Assembler::BelowOrEqual, length, index, failure);
|
|
+ if (JitOptions.spectreIndexMasking) {
|
|
+ // r12 will likely have |length| in it anyway from the above
|
|
+ // operation, but it doesn't matter anyhow; see above.
|
|
+ as_isel(index, index, SecondScratchReg, Assembler::GreaterThan);
|
|
+ }
|
|
+}
|
|
+
|
|
+// Same as above
|
|
+void
|
|
+MacroAssembler::spectreBoundsCheckPtr(Register index, Register length,
|
|
+ Register maybeScratch, Label* failure)
|
|
+{
|
|
+ branchPtr(Assembler::BelowOrEqual, length, index, failure);
|
|
+ if (JitOptions.spectreIndexMasking) {
|
|
+ as_isel(index, index, length, Assembler::GreaterThan);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::spectreBoundsCheckPtr(Register index, const Address& length,
|
|
+ Register maybeScratch, Label* failure)
|
|
+{
|
|
+ branchPtr(Assembler::BelowOrEqual, length, index, failure);
|
|
+ if (JitOptions.spectreIndexMasking) {
|
|
+ as_isel(index, index, SecondScratchReg, Assembler::GreaterThan);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::spectreMovePtr(Condition cond, Register src, Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+
|
|
+ // isel cannot test for the non-existence of a bit.
|
|
+ if (cond == Equal) {
|
|
+ as_isel(dest, src, dest, Assembler::Equal);
|
|
+ } else {
|
|
+ // Flip the order.
|
|
+ as_isel(dest, dest, src, Assembler::Equal);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::spectreZeroRegister(Condition cond, Register scratch, Register dest)
|
|
+{
|
|
+ // Zero the register if *true*. Hold my beer.
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+
|
|
+ if (cond == NotEqual) {
|
|
+ xs_li(ScratchRegister, 0);
|
|
+ as_isel(dest, dest, ScratchRegister, Assembler::Equal);
|
|
+ } else {
|
|
+ as_isel0(dest, ScratchRegister, dest, Assembler::Equal); // mscdfr0
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::fallibleUnboxPtr(const ValueOperand& src, Register dest,
|
|
+ JSValueType type, Label* fail)
|
|
+{
|
|
+ MOZ_ASSERT(type == JSVAL_TYPE_OBJECT || type == JSVAL_TYPE_STRING ||
|
|
+ type == JSVAL_TYPE_SYMBOL || type == JSVAL_TYPE_BIGINT);
|
|
+ // dest := src XOR mask
|
|
+ // scratch := dest >> JSVAL_TAG_SHIFT
|
|
+ // fail if scratch != 0
|
|
+ //
|
|
+ // Note: src and dest can be the same register
|
|
+ ScratchRegisterScope scratch(asMasm());
|
|
+ mov(ImmWord(JSVAL_TYPE_TO_SHIFTED_TAG(type)), scratch);
|
|
+ ma_xor(scratch, src.valueReg());
|
|
+ ma_move(dest, scratch);
|
|
+ x_srdi(scratch, scratch, JSVAL_TAG_SHIFT);
|
|
+ ma_bc(scratch, Imm32(0), fail, Assembler::NotEqual);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::fallibleUnboxPtr(const Address& src, Register dest,
|
|
+ JSValueType type, Label* fail)
|
|
+{
|
|
+ loadValue(src, ValueOperand(dest));
|
|
+ fallibleUnboxPtr(ValueOperand(dest), dest, type, fail);
|
|
+}
|
|
+
|
|
+void MacroAssembler::fallibleUnboxPtr(const BaseIndex& src, Register dest,
|
|
+ JSValueType type, Label* fail)
|
|
+{
|
|
+ loadValue(src, ValueOperand(dest));
|
|
+ fallibleUnboxPtr(ValueOperand(dest), dest, type, fail);
|
|
+}
|
|
+
|
|
+// ========================================================================
|
|
+// Memory access primitives.
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr)
|
|
+{
|
|
+ return ma_sd(src, addr);
|
|
+}
|
|
+FaultingCodeOffset
|
|
+MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& addr)
|
|
+{
|
|
+ return ma_sd(src, addr);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr)
|
|
+{
|
|
+ return ma_ss(src, addr);
|
|
+}
|
|
+FaultingCodeOffset
|
|
+MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr)
|
|
+{
|
|
+ return ma_ss(src, addr);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::memoryBarrier(MemoryBarrierBits barrier)
|
|
+{
|
|
+ xs_lwsync();
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Clamping functions.
|
|
+
|
|
+void
|
|
+MacroAssembler::clampIntToUint8(Register reg)
|
|
+{
|
|
+ // If reg is < 0, then we want to clamp to 0.
|
|
+ // If reg is >= 255, then we want to clamp to 255.
|
|
+ // Essentially, compute max(reg,0), then min(reg,255).
|
|
+ // This is pretty much what isel was designed for.
|
|
+ ma_li(ScratchRegister, (int64_t)0); // make gcc happy just make it happy
|
|
+ ma_li(SecondScratchReg, 255);
|
|
+ as_cmpd(reg, ScratchRegister); // emit to CR0
|
|
+ as_cmpd(cr1, reg, SecondScratchReg); // emit to CR1
|
|
+ // Naughtiness: since ScratchRegister is r0, the load is
|
|
+ // zero anyway (this is a "mscdfr0" instruction). I just
|
|
+ // wanted to point out to you how clever I am.
|
|
+ as_isel0(reg, ScratchRegister, reg, (uint16_t)Assembler::LessThan); // CR0[LT]
|
|
+ as_isel(reg, SecondScratchReg, reg, (uint16_t)Assembler::GreaterThan, cr1); // CR1[GT]
|
|
+}
|
|
+
|
|
+//}}} check_macroassembler_style
|
|
+// ===============================================================
|
|
+
|
|
+} // namespace jit
|
|
+} // namespace js
|
|
+
|
|
+#endif /* jit_ppc64le_MacroAssembler_ppc64le_inl_h */
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/MacroAssembler-ppc64.cpp
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/MacroAssembler-ppc64.cpp Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,5904 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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/. */
|
|
+
|
|
+#include "jit/ppc64/MacroAssembler-ppc64.h"
|
|
+
|
|
+#include "mozilla/CheckedInt.h"
|
|
+#include "mozilla/DebugOnly.h"
|
|
+#include "mozilla/MathAlgorithms.h"
|
|
+
|
|
+#include "jsmath.h"
|
|
+#include "jit/Bailouts.h"
|
|
+#include "jit/BaselineFrame.h"
|
|
+#include "jit/JitFrames.h"
|
|
+#include "jit/JitRuntime.h"
|
|
+#include "jit/MacroAssembler.h"
|
|
+#include "jit/MoveEmitter.h"
|
|
+#include "jit/SharedICRegisters.h"
|
|
+#include "util/Memory.h"
|
|
+
|
|
+#include "vm/JitActivation.h"
|
|
+#include "vm/JSContext.h"
|
|
+#include "wasm/WasmStubs.h"
|
|
+#include "jit/MacroAssembler-inl.h"
|
|
+
|
|
+using namespace js;
|
|
+using namespace jit;
|
|
+
|
|
+using mozilla::Abs;
|
|
+using mozilla::CheckedInt;
|
|
+
|
|
+#if DEBUG
|
|
+#define spew(...) JitSpew(JitSpew_Codegen, __VA_ARGS__)
|
|
+#else
|
|
+#define spew(...)
|
|
+#endif
|
|
+
|
|
+#if DEBUG
|
|
+
|
|
+/* Useful class to print visual guard blocks. */
|
|
+class MASMAutoDeBlock
|
|
+{
|
|
+ private:
|
|
+ const char *blockname;
|
|
+
|
|
+ public:
|
|
+ MASMAutoDeBlock(const char *name, int line) {
|
|
+ blockname = name;
|
|
+ JitSpew(JitSpew_Codegen, "[[ CGPPC line %d: %s", line, blockname);
|
|
+ }
|
|
+
|
|
+ ~MASMAutoDeBlock() {
|
|
+ JitSpew(JitSpew_Codegen, " CGPPC: %s ]]", blockname);
|
|
+ }
|
|
+};
|
|
+#undef ADBlock
|
|
+#define ADBlock() MASMAutoDeBlock _adbx(__PRETTY_FUNCTION__, __LINE__)
|
|
+#else
|
|
+
|
|
+/* Useful macro to completely elide visual guard blocks. */
|
|
+#define ADBlock() ;
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
+static_assert(sizeof(intptr_t) == 8, "Not 64-bit clean.");
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::convertBoolToInt32(Register src, Register dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ // Note that C++ bool is only 1 byte, so zero extend it to clear the
|
|
+ // higher-order bits.
|
|
+ ma_and(dest, src, Imm32(0xff));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::convertInt32ToDouble(Register src, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ moveToDouble(src, dest);
|
|
+ as_fcfid(dest, dest); // easy!
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::convertUInt64ToDouble(Register src, FloatRegister dest)
|
|
+{
|
|
+ // Approximately the same as above, but using fcfidu.
|
|
+ ADBlock();
|
|
+
|
|
+ moveToDouble(src, dest);
|
|
+ as_fcfidu(dest, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::convertInt32ToDouble(const Address& src, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(src.base != SecondScratchReg);
|
|
+
|
|
+ load32(src, SecondScratchReg);
|
|
+ convertInt32ToDouble(SecondScratchReg, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::convertInt32ToDouble(const BaseIndex& src, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(src.base != SecondScratchReg);
|
|
+
|
|
+ computeScaledAddress(src, SecondScratchReg);
|
|
+ load32(Address(SecondScratchReg, src.offset), ScratchRegister);
|
|
+ convertInt32ToDouble(ScratchRegister, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::convertUInt32ToDouble(Register src, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ // Clear any tag and sign.
|
|
+ as_rldicl(ScratchRegister, src, 0, 32); // "clrldi"
|
|
+ asMasm().convertUInt64ToDouble(Register64(ScratchRegister), dest, InvalidReg);
|
|
+}
|
|
+
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::convertUInt32ToFloat32(Register src, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ as_rldicl(ScratchRegister, src, 0, 32); // "clrldi"
|
|
+ asMasm().convertUInt64ToFloat32(Register64(ScratchRegister), dest, InvalidReg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::convertDoubleToFloat32(FloatRegister src, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ as_frsp(dest, src);
|
|
+}
|
|
+
|
|
+// Checks whether a double is representable as a 32-bit integer. If so, the
|
|
+// integer is written to the output register. Otherwise, a bailout is taken to
|
|
+// the given snapshot. This function overwrites the scratch float register.
|
|
+void
|
|
+MacroAssemblerPPC64Compat::convertDoubleToInt32(FloatRegister src, Register dest,
|
|
+ Label* fail, bool negativeZeroCheck)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(src != ScratchDoubleReg);
|
|
+
|
|
+ // Throw an failure if the integer conversion is invalid (VXCVI) or
|
|
+ // inexact (FI).
|
|
+ as_mtfsb0(23); // whack VXCVI; FI is not sticky
|
|
+ as_fctiwz(ScratchDoubleReg, src);
|
|
+ as_mcrfs(cr0, 3); // VXVC - FR - FI - FPRF[C]
|
|
+ as_mcrfs(cr1, 5); // resv'd - VXSOFT - VXSQRT - VXCVI
|
|
+ moveFromDouble(ScratchDoubleReg, dest);
|
|
+ as_cror(0, 2, 7); // CR0[LT] = FI->CR0[EQ] | VXCVI->CR1[SO]
|
|
+ as_srawi(dest, dest, 0); // clear upper word and sign extend
|
|
+ ma_bc(Assembler::LessThan, fail);
|
|
+
|
|
+ if (negativeZeroCheck) {
|
|
+ // If we need to check negative 0, then grab the original FPR
|
|
+ // and look at the sign bit.
|
|
+ // The MIPS version happily clobbers dest from the beginning, so
|
|
+ // no worries doing this check here to save some work.
|
|
+ Label done;
|
|
+ MOZ_ASSERT(dest != ScratchRegister);
|
|
+
|
|
+ // Don't bother if the result was not zero.
|
|
+ as_cmpldi(dest, 0);
|
|
+ ma_bc(Assembler::NotEqual, &done, ShortJump);
|
|
+
|
|
+ // Damn, the result was zero. Treat as a 64-bit int and check sign.
|
|
+ moveFromDouble(src, ScratchRegister);
|
|
+ as_cmpdi(ScratchRegister, 0);
|
|
+ ma_bc(Assembler::LessThan, fail);
|
|
+
|
|
+ bind(&done);
|
|
+ }
|
|
+}
|
|
+
|
|
+// Checks whether a float32 is representable as a 32-bit integer.
|
|
+void
|
|
+MacroAssemblerPPC64Compat::convertFloat32ToInt32(FloatRegister src, Register dest,
|
|
+ Label* fail, bool negativeZeroCheck)
|
|
+{
|
|
+ // Since 32-bit and 64-bit FPRs are the same registers, use the same
|
|
+ // routine above.
|
|
+ ADBlock();
|
|
+ convertDoubleToInt32(src, dest, fail, negativeZeroCheck);
|
|
+}
|
|
+
|
|
+// Same, but 64-bit. XXX: consolidate with 32-bit version, minimal difference
|
|
+void
|
|
+MacroAssemblerPPC64Compat::convertDoubleToPtr(FloatRegister src, Register dest,
|
|
+ Label* fail, bool negativeZeroCheck)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(src != ScratchDoubleReg);
|
|
+
|
|
+ // Throw an failure if the integer conversion is invalid (VXCVI) or
|
|
+ // inexact (FI).
|
|
+ as_mtfsb0(23); // whack VXCVI; FI is not sticky
|
|
+ as_fctidz(ScratchDoubleReg, src);
|
|
+ as_mcrfs(cr0, 3); // VXVC - FR - FI - FPRF[C]
|
|
+ as_mcrfs(cr1, 5); // resv'd - VXSOFT - VXSQRT - VXCVI
|
|
+ // Don't clear the upper word or sign extend as we do for 32-bit.
|
|
+ moveFromDouble(ScratchDoubleReg, dest);
|
|
+ as_cror(0, 2, 7); // CR0[LT] = FI->CR0[EQ] | VXCVI->CR1[SO]
|
|
+ ma_bc(Assembler::LessThan, fail);
|
|
+
|
|
+ if (negativeZeroCheck) {
|
|
+ // If we need to check negative 0, then grab the original FPR
|
|
+ // and look at the sign bit.
|
|
+ // The MIPS version happily clobbers dest from the beginning, so
|
|
+ // no worries doing this check here to save some work.
|
|
+ Label done;
|
|
+ MOZ_ASSERT(dest != ScratchRegister);
|
|
+
|
|
+ // Don't bother if the result was not zero.
|
|
+ as_cmpldi(dest, 0);
|
|
+ ma_bc(Assembler::NotEqual, &done, ShortJump);
|
|
+
|
|
+ // Damn, the result was zero. Treat as a 64-bit int and check sign.
|
|
+ moveFromDouble(src, ScratchRegister);
|
|
+ as_cmpdi(ScratchRegister, 0);
|
|
+ ma_bc(Assembler::LessThan, fail);
|
|
+
|
|
+ bind(&done);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::convertFloat32ToDouble(FloatRegister src, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ // Although the ISA allows us to treat single and double FPRs mostly
|
|
+ // interchangeably, the Wasm spec requires that NaNs be canonicalized,
|
|
+ // essentially making them qNaNs. frsp does this in the other direction
|
|
+ // but VSX conversion does no such cleanup in this direction. The most
|
|
+ // straightforward workaround is to just frsp again: the value is already
|
|
+ // supposed to be 32-bit, and the next 64-bit operation will upconvert.
|
|
+ // Unfortunately in opt builds we don't know if we're in Wasm code or not,
|
|
+ // so we have to do this everywhere (grumpf); it's not necessary in
|
|
+ // non-Wasm code (i.e., can't check IsCompilingWasm()). This may also not
|
|
+ // be needed on non-VSX CPUs, which is some consolation for their heavier
|
|
+ // float-GPR conversions.
|
|
+ as_frsp(dest, src); // even if dest == src
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::convertInt32ToFloat32(Register src, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ moveToDouble(src, dest);
|
|
+ // Enforce rounding mode 0b00 (round-to-nearest ties-to-even).
|
|
+// XXX: make this into a round followed by fcfids
|
|
+ as_mtfsfi(7, 0);
|
|
+ as_fcfids(dest, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::convertInt32ToFloat32(const Address& src, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+MOZ_CRASH("NYI convertInt32ToFloat32");
|
|
+xs_trap();
|
|
+ ma_li(ScratchRegister, ImmWord(src.offset));
|
|
+ as_lfiwax(dest, src.base, ScratchRegister);
|
|
+ as_fcfid(dest, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::movq(Register rs, Register rd)
|
|
+{
|
|
+ ma_move(rd, rs);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_li(Register dest, CodeLabel* label)
|
|
+{
|
|
+ BufferOffset bo = m_buffer.nextOffset();
|
|
+ ma_liPatchable(dest, ImmWord(/* placeholder */ 0));
|
|
+ label->patchAt()->bind(bo.getOffset());
|
|
+ label->setLinkMode(CodeLabel::MoveImmediate);
|
|
+}
|
|
+
|
|
+// Generate an optimized sequence to load a 64-bit immediate.
|
|
+void
|
|
+MacroAssemblerPPC64::ma_li(Register dest, int64_t value)
|
|
+{
|
|
+#if(1)
|
|
+ // Shamelessly cribbed from MIPS64 because I like its style.
|
|
+
|
|
+ // Handle low-short only values first.
|
|
+ if (-1 == (value >> 15) || 0 == (value >> 15)) {
|
|
+ // Sign extension OK!
|
|
+ xs_li(dest, value);
|
|
+ return;
|
|
+ }
|
|
+ if (0 == (value >> 16)) {
|
|
+ // Sign extension NOT OK!
|
|
+ xs_li(dest, 0);
|
|
+ as_ori(dest, dest, value);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (-1 == (value >> 31) || 0 == (value >> 31)) {
|
|
+ // Sign extension OK!
|
|
+ xs_lis(dest, uint16_t(value >> 16));
|
|
+ } else if (0 == (value >> 32)) {
|
|
+ // Sign extension NOT OK!
|
|
+ xs_lis(dest, uint16_t(value >> 16));
|
|
+ as_rldicl(dest, dest, 0, 32); // "clrldi" ~== "dinsu" for mips $zero
|
|
+ } else if (-1 == (value >> 47) || 0 == (value >> 47)) {
|
|
+ // Sign extension OK!
|
|
+ xs_lis(dest, uint16_t(value >> 32));
|
|
+ if (uint16_t(value >> 16)) {
|
|
+ as_ori(dest, dest, uint16_t(value >> 16));
|
|
+ }
|
|
+ as_rldicr(dest, dest, 16, 47); // "sld" == "dsll"
|
|
+ } else if (0 == (value >> 48)) {
|
|
+ // Sign extension NOT OK!
|
|
+ xs_lis(dest, uint16_t(value >> 32));
|
|
+ as_rldicl(dest, dest, 0, 32); // "clrldi" ~== "dinsu" for mips $zero
|
|
+ if (uint16_t(value >> 16)) {
|
|
+ as_ori(dest, dest, uint16_t(value >> 16));
|
|
+ }
|
|
+ as_rldicr(dest, dest, 16, 47); // "sld" == "dsll"
|
|
+ } else {
|
|
+ // Sign extension ... IRRELEVANT!
|
|
+ xs_lis(dest, uint16_t(value >> 48));
|
|
+ if (uint16_t(value >> 32)) {
|
|
+ as_ori(dest, dest, uint16_t(value >> 32));
|
|
+ }
|
|
+ as_rldicr(dest, dest, 32, 31);
|
|
+ if (uint16_t(value >> 16)) {
|
|
+ as_oris(dest, dest, uint16_t(value >> 16));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Lowest short handled separately.
|
|
+ if (uint16_t(value)) {
|
|
+ as_ori(dest, dest, uint16_t(value));
|
|
+ }
|
|
+#else
|
|
+ uint64_t bits = (uint64_t)value;
|
|
+ bool loweronly = true;
|
|
+
|
|
+ // Handle trivial 16-bit quantities.
|
|
+ if (value > -32769 && value < 32768) {
|
|
+ // fits in 16 low bits
|
|
+ xs_li(dest, value); // mscdfr0 asserts
|
|
+ return;
|
|
+ }
|
|
+ if ((bits & 0xffffffff8000ffff) == 0 || // sign extension!
|
|
+ (bits & 0xffffffff8000ffff) == 0xffffffff80000000) {
|
|
+ // fits in 16 high bits
|
|
+ xs_lis(dest, value >> 16); // mscdfr0 asserts
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Emit optimized sequence based on occupied bits.
|
|
+ if (bits & 0xffff000000000000) {
|
|
+ // Need to set upper word and shift.
|
|
+ xs_lis(dest, bits >> 48);
|
|
+ if (bits & 0x0000ffff00000000) {
|
|
+ as_ori(dest, dest, (bits >> 32) & 0xffff);
|
|
+ }
|
|
+ as_rldicr(dest, dest, 32, 31);
|
|
+ loweronly = false;
|
|
+ } else if (bits & 0x0000ffff00000000) {
|
|
+ if (bits & 0x0000800000000000) { // sign extension!
|
|
+ xs_li(dest, 0);
|
|
+ as_ori(dest, dest, (bits >> 32) & 0xffff);
|
|
+ } else {
|
|
+ xs_li(dest, (bits >> 32) & 0xffff);
|
|
+ }
|
|
+ as_rldicr(dest, dest, 32, 31);
|
|
+ loweronly = false;
|
|
+ } else if ((bits & 0x80000000) || (bits & 0x00008000)) {
|
|
+ // No upper bits were set, so we can't use addi(s) for the lower word
|
|
+ // or it will improperly sign-extend.
|
|
+ xs_li(dest, 0);
|
|
+ loweronly = false;
|
|
+ }
|
|
+
|
|
+ // Now the lower word. Don't clobber the upper word!
|
|
+ bits &= 0x00000000ffffffff;
|
|
+ if (bits & 0xffff0000) {
|
|
+ if (loweronly) {
|
|
+ xs_lis(dest, bits >> 16);
|
|
+ } else {
|
|
+ as_oris(dest, dest, bits >> 16);
|
|
+ }
|
|
+ if (bits & 0x0000ffff) {
|
|
+ as_ori(dest, dest, bits & 0xffff);
|
|
+ }
|
|
+ } else if (bits & 0x0000ffff) {
|
|
+ if (loweronly) {
|
|
+ xs_li(dest, bits & 0xffff);
|
|
+ } else {
|
|
+ as_ori(dest, dest, bits & 0xffff);
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+}
|
|
+void
|
|
+MacroAssemblerPPC64::ma_li(Register dest, ImmWord imm)
|
|
+{
|
|
+ ADBlock();
|
|
+ ma_li(dest, (int64_t)imm.value);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_li(Register dest, Imm64 imm)
|
|
+{
|
|
+ ADBlock();
|
|
+ ma_li(dest, (int64_t)imm.value);
|
|
+}
|
|
+
|
|
+// This generates immediate loads as well, but always in the
|
|
+// long form so that they can be patched.
|
|
+void
|
|
+MacroAssemblerPPC64::ma_liPatchable(Register dest, ImmPtr imm)
|
|
+{
|
|
+ ma_liPatchable(dest, ImmWord(uintptr_t(imm.value)));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_liPatchable(Register dest, ImmWord imm)
|
|
+{
|
|
+ // 64-bit load.
|
|
+ m_buffer.ensureSpace(5 * sizeof(uint32_t));
|
|
+ xs_lis(dest, Imm16::Upper(Imm32(imm.value >> 32)).encode());
|
|
+ as_ori(dest, dest, Imm16::Lower(Imm32(imm.value >> 32)).encode());
|
|
+ as_rldicr(dest, dest, 32, 31);
|
|
+ as_oris(dest, dest, Imm16::Upper(Imm32(imm.value)).encode());
|
|
+ as_ori(dest, dest, Imm16::Lower(Imm32(imm.value)).encode());
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_dnegu(Register rd, Register rs)
|
|
+{
|
|
+ as_neg(rd, rs);
|
|
+}
|
|
+
|
|
+// Shifts
|
|
+void
|
|
+MacroAssemblerPPC64::ma_dsll(Register rd, Register rt, Imm32 shift)
|
|
+{
|
|
+ MOZ_ASSERT(shift.value < 64);
|
|
+ as_rldicr(rd, rt, shift.value, 63-(shift.value)); // "sldi"
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_dsrl(Register rd, Register rt, Imm32 shift)
|
|
+{
|
|
+ MOZ_ASSERT(shift.value < 64);
|
|
+ as_rldicl(rd, rt, 64-(shift.value), shift.value); // "srdi"
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_dsll(Register rd, Register rt, Register shift)
|
|
+{
|
|
+ as_sld(rd, rt, shift);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_dsrl(Register rd, Register rt, Register shift)
|
|
+{
|
|
+ as_srd(rd, rt, shift);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_dins(Register rt, Register rs, Imm32 pos, Imm32 size)
|
|
+{
|
|
+ as_rldimi(rt, rs, 64-(pos.value + size.value), pos.value); // "insrdi"
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_dext(Register rt, Register rs, Imm32 pos, Imm32 size)
|
|
+{
|
|
+ // MIPS dext is right-justified, so use rldicl to simulate.
|
|
+MOZ_CRASH("ma_dext whyyyy");
|
|
+xs_trap(); // not sure if trap
|
|
+ as_rldicl(rt, rs, (pos.value + size.value), 64 - (size.value)); // "extrdi"
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_dctz(Register rd, Register rs)
|
|
+{
|
|
+ as_cnttzd(rd, rs);
|
|
+}
|
|
+
|
|
+// Arithmetic-based ops.
|
|
+
|
|
+// Add.
|
|
+void
|
|
+MacroAssemblerPPC64::ma_add(Register rd, Register rs, Imm32 imm)
|
|
+{
|
|
+ MOZ_ASSERT(rs != ScratchRegister);
|
|
+ if (Imm16::IsInSignedRange(imm.value)) {
|
|
+ as_addi(rd, rs, imm.value);
|
|
+ } else {
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ as_add(rd, rs, ScratchRegister);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_add(Register rd, Register rs)
|
|
+{
|
|
+ as_add(rd, rd, rs);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_add(Register rd, Imm32 imm)
|
|
+{
|
|
+ ma_add(rd, rd, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_addTestOverflow(Register rd, Register rs, Register rt, Label* overflow, bool is32)
|
|
+{
|
|
+ // MIPS clobbers rd, so we can too.
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(rs != ScratchRegister);
|
|
+ MOZ_ASSERT(rt != ScratchRegister);
|
|
+ // This is testing a 32-bit overflow, so we need to whack and test
|
|
+ // XER[OV32].
|
|
+ xs_li(ScratchRegister, 0);
|
|
+ xs_mtxer(ScratchRegister);
|
|
+ as_addo(rd, rs, rt);
|
|
+ if (is32) {
|
|
+ // Handled in assembler.
|
|
+ ma_bc(Assembler::Overflow, overflow);
|
|
+ } else {
|
|
+ // Do it manually. (XXX: Hoist into assembler too?)
|
|
+ as_mcrxrx(cr0);
|
|
+ ma_bc(Assembler::LessThan, overflow); // OV bit
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_addTestOverflow(Register rd, Register rs, Imm32 imm, Label* overflow, bool is32)
|
|
+{
|
|
+ // There is no addio, daddy-o, so use the regular overflow, yo.
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(rs != SecondScratchReg);
|
|
+
|
|
+ ma_li(SecondScratchReg, imm);
|
|
+ ma_addTestOverflow(rd, rs, SecondScratchReg, overflow, is32);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_addTestOverflow(Register rd, Register rs, ImmWord imm, Label* overflow, bool is32)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(rs != SecondScratchReg);
|
|
+ // This can only come in from a 64-bit routine, so assert that.
|
|
+ MOZ_ASSERT(!is32);
|
|
+
|
|
+ ma_li(SecondScratchReg, imm);
|
|
+ ma_addTestOverflow(rd, rs, SecondScratchReg, overflow, is32);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_negTestOverflow(Register rd, Label* overflow)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(rd != ScratchRegister);
|
|
+
|
|
+ xs_li(ScratchRegister, 0);
|
|
+ xs_mtxer(ScratchRegister);
|
|
+ as_nego(rd, rd);
|
|
+ ma_bc(Assembler::Overflow, overflow);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_not(Register rd, Register ra)
|
|
+{
|
|
+ ADBlock();
|
|
+ as_nor(rd, ra, ra); // OPPCC p.540
|
|
+}
|
|
+
|
|
+// Subtract.
|
|
+// ma_* subtraction functions invert operand order for as_subf.
|
|
+void
|
|
+MacroAssemblerPPC64::ma_dsubu(Register rd, Register rs, Imm32 imm)
|
|
+{
|
|
+ MOZ_ASSERT(rs != ScratchRegister);
|
|
+ if (Imm16::IsInSignedRange(-imm.value)) {
|
|
+ as_addi(rd, rs, -imm.value);
|
|
+ } else {
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ as_subf(rd, ScratchRegister, rs); // T = B - A
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_dsubu(Register rd, Register rs)
|
|
+{
|
|
+ as_subf(rd, rs, rd); // T = B - A
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_dsubu(Register rd, Imm32 imm)
|
|
+{
|
|
+ ma_dsubu(rd, rd, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_subTestOverflow(Register rd, Register rs, Register rt, Label* overflow, bool is32)
|
|
+{
|
|
+ // MIPS clobbers rd, so we can too.
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(rs != ScratchRegister);
|
|
+ MOZ_ASSERT(rt != ScratchRegister);
|
|
+ xs_li(ScratchRegister, 0);
|
|
+ xs_mtxer(ScratchRegister);
|
|
+ as_subfo(rd, rt, rs); // T = B - A
|
|
+ if (is32) {
|
|
+ // This is a 32-bit operation, so we need to whack and test XER[OV32].
|
|
+ // Handled in assembler.
|
|
+ ma_bc(Assembler::Overflow, overflow);
|
|
+ } else {
|
|
+ // Do it manually. (XXX: Hoist into assembler too?)
|
|
+ as_mcrxrx(cr0);
|
|
+ ma_bc(Assembler::LessThan, overflow); // OV bit
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_subTestOverflow(Register rd, Register rs, Imm32 imm, Label* overflow, bool is32)
|
|
+{
|
|
+ ADBlock();
|
|
+ if (imm.value != INT32_MIN) {
|
|
+ asMasm().ma_addTestOverflow(rd, rs, Imm32(-imm.value), overflow, is32);
|
|
+ } else {
|
|
+ ma_li(ScratchRegister, Imm32(imm.value));
|
|
+ asMasm().ma_subTestOverflow(rd, rs, ScratchRegister, overflow, is32);
|
|
+ }
|
|
+}
|
|
+
|
|
+// Memory.
|
|
+// In these master GPR load/store routines, return a FaultingCodeOffset for
|
|
+// future proofing, since our wasmLoad* and wasmStore* routines consume them
|
|
+// too.
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::ma_load(Register dest, Address address,
|
|
+ LoadStoreSize size, LoadStoreExtension extension)
|
|
+{
|
|
+ // ADBlock(); // spammy
|
|
+ FaultingCodeOffset fco;
|
|
+ int16_t encodedOffset;
|
|
+ //uint32_t loadInst;
|
|
+ Register base;
|
|
+ MOZ_ASSERT(extension == ZeroExtend || extension == SignExtend);
|
|
+ MOZ_ASSERT(address.base != ScratchRegister);
|
|
+ Register t = (dest == ScratchRegister && base == SecondScratchReg) ? ThirdScratchReg : (dest == ScratchRegister) ? SecondScratchReg : ScratchRegister;
|
|
+
|
|
+ // TODO: Some of this logic is repeated.
|
|
+ if (!Imm16::IsInSignedRange(address.offset)) {
|
|
+ MOZ_ASSERT(address.base != SecondScratchReg);
|
|
+ ma_li(SecondScratchReg, Imm32(address.offset));
|
|
+ as_add(SecondScratchReg, address.base, SecondScratchReg);
|
|
+ base = SecondScratchReg;
|
|
+ encodedOffset = 0;
|
|
+ } else {
|
|
+ encodedOffset = Imm16(address.offset).encode();
|
|
+ base = address.base;
|
|
+ }
|
|
+
|
|
+ // It is entirely possible for the encodedOffset to trigger an
|
|
+ // unaligned load (for example, this regularly occurs with the Baseline
|
|
+ // Interpreter). We should see if there is potential for optimization,
|
|
+ // but the operation is deliberate, and we will fall back on the hardware
|
|
+ // to do the unaligned load as necessary.
|
|
+ switch (size) {
|
|
+ case SizeByte:
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_lbz(dest, base, encodedOffset);
|
|
+ if (SignExtend == extension)
|
|
+ as_extsb(dest, dest);
|
|
+ break;
|
|
+ case SizeHalfWord:
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ if (SignExtend == extension)
|
|
+ as_lha(dest, base, encodedOffset);
|
|
+ else
|
|
+ as_lhz(dest, base, encodedOffset);
|
|
+ break;
|
|
+ case SizeWord:
|
|
+ if (ZeroExtend == extension) {
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_lwz(dest, base, encodedOffset);
|
|
+ } else {
|
|
+ // lwa only valid if the offset is word-aligned.
|
|
+ if (encodedOffset & 0x03) {
|
|
+ MOZ_ASSERT(base != t);
|
|
+ ma_li(t, encodedOffset);
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_lwax(dest, base, t);
|
|
+ } else {
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_lwa(dest, base, encodedOffset);
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ case SizeDouble:
|
|
+ // ld only valid if the offset is word-aligned.
|
|
+ if (encodedOffset & 0x03) {
|
|
+ MOZ_ASSERT(base != t);
|
|
+ ma_li(t, encodedOffset);
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_ldx(dest, base, t);
|
|
+ } else {
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_ld(dest, base, encodedOffset);
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("Invalid argument for ma_load");
|
|
+ }
|
|
+
|
|
+ return fco;
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::ma_store(Register data, Address address, LoadStoreSize size)
|
|
+{
|
|
+ //ADBlock(); // spammy
|
|
+ int16_t encodedOffset;
|
|
+ //uint32_t loadInst = 0;
|
|
+ FaultingCodeOffset fco;
|
|
+ Register base;
|
|
+ MOZ_ASSERT(address.base != ScratchRegister);
|
|
+
|
|
+ if (!Imm16::IsInSignedRange(address.offset)) {
|
|
+ MOZ_ASSERT(address.base != SecondScratchReg);
|
|
+ ma_li(SecondScratchReg, Imm32(address.offset));
|
|
+ as_add(SecondScratchReg, address.base, SecondScratchReg);
|
|
+ base = SecondScratchReg;
|
|
+ encodedOffset = 0;
|
|
+ } else {
|
|
+ encodedOffset = Imm16(address.offset).encode();
|
|
+ base = address.base;
|
|
+ }
|
|
+
|
|
+ switch (size) {
|
|
+ case SizeByte:
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_stb(data, base, encodedOffset);
|
|
+ break;
|
|
+ case SizeHalfWord:
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_sth(data, base, encodedOffset);
|
|
+ break;
|
|
+ case SizeWord:
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_stw(data, base, encodedOffset);
|
|
+ break;
|
|
+ case SizeDouble:
|
|
+ // std only valid if the offset is word-aligned.
|
|
+ if (encodedOffset & 0x03) {
|
|
+ Register t = (data == ScratchRegister && base == SecondScratchReg) ? ThirdScratchReg : (data == ScratchRegister) ? SecondScratchReg : ScratchRegister;
|
|
+ MOZ_ASSERT(base != t);
|
|
+ MOZ_ASSERT(data != t);
|
|
+
|
|
+ ma_li(t, encodedOffset);
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_stdx(data, base, t);
|
|
+ } else {
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_std(data, base, encodedOffset);
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("Invalid argument for ma_store");
|
|
+ }
|
|
+
|
|
+ return fco;
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::computeScaledAddress(const BaseIndex& address, Register dest)
|
|
+{
|
|
+ // TODO: The non-shift case can be essentially an ldx or stdx (or whatever
|
|
+ // size), so we should figure out a way to signal the caller that this
|
|
+ // optimization is possible.
|
|
+ int32_t shift = Imm32::ShiftOf(address.scale).value;
|
|
+ if (shift) {
|
|
+ MOZ_ASSERT(address.base != ScratchRegister);
|
|
+ ma_dsll(ScratchRegister, address.index, Imm32(shift)); // 64-bit needed
|
|
+ as_add(dest, address.base, ScratchRegister);
|
|
+ } else {
|
|
+ as_add(dest, address.base, address.index);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_pop(Register r)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(sizeof(uintptr_t) == 8);
|
|
+ as_ld(r, StackPointer, 0);
|
|
+ as_addi(StackPointer, StackPointer, sizeof(uintptr_t));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_push(Register r)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(sizeof(uintptr_t) == 8);
|
|
+ as_stdu(r, StackPointer, (int32_t)-sizeof(intptr_t));
|
|
+}
|
|
+
|
|
+// Branches when done from within PPC-specific code.
|
|
+void
|
|
+MacroAssemblerPPC64::ma_bc(Condition c, Label* l, JumpKind jumpKind)
|
|
+{
|
|
+ // Shorthand for a branch based on CR0.
|
|
+ ma_bc(cr0, c, l, jumpKind);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_bc(DoubleCondition c, Label *l, JumpKind jumpKind)
|
|
+{
|
|
+ ma_bc(cr0, c, l, jumpKind);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_bc(DoubleCondition c, FloatRegister lhs,
|
|
+ FloatRegister rhs, Label *label, JumpKind jumpKind)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ compareFloatingPoint(lhs, rhs, c);
|
|
+ ma_bc(c, label, jumpKind);
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+void
|
|
+MacroAssemblerPPC64::ma_bc(CRegisterID cr, T c, Label* label, JumpKind jumpKind)
|
|
+{
|
|
+ ADBlock();
|
|
+ // Branch on the condition bit in the specified condition register.
|
|
+ // TODO: Support likely bits.
|
|
+ spew("bc .Llabel %p @ %08x", label, currentOffset());
|
|
+ if (label->bound()) {
|
|
+ int64_t offset = label->offset() - m_buffer.nextOffset().getOffset();
|
|
+ spew("# target offset: %08x (diff: %ld)\n", label->offset(), offset);
|
|
+
|
|
+ if (BOffImm16::IsInSignedRange(offset))
|
|
+ jumpKind = ShortJump;
|
|
+
|
|
+ if (jumpKind == ShortJump) {
|
|
+ MOZ_ASSERT(BOffImm16::IsInSignedRange(offset));
|
|
+ as_bc(BOffImm16(offset).encode(), c, cr, NotLikelyB, DontLinkB);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Generate a long branch stanza, but invert the sense so that we
|
|
+ // can run a short branch, assuming the "real" branch is not taken.
|
|
+ // However, overflow doesn't do reversed sense, so we do "footwork."
|
|
+ m_buffer.ensureSpace(12 * sizeof(uint32_t)); // Worst case + CR ops
|
|
+ if (c & ConditionOnlyXER) {
|
|
+ // This is why we don't need to account for the mcrxrx in
|
|
+ // Assembler-ppc64, because we generate this:
|
|
+ //
|
|
+ // bc cond .+8 (DO NOT ADJUST)
|
|
+ // b .+32
|
|
+ // long jump
|
|
+ as_bc(2 * sizeof(uint32_t), c, cr, NotLikelyB, DontLinkB);
|
|
+ as_b(8 * sizeof(uint32_t));
|
|
+ } else {
|
|
+ as_bc(8 * sizeof(uint32_t), InvertCondition(c), cr, NotLikelyB, DontLinkB);
|
|
+ }
|
|
+ addLongJump(nextOffset(), BufferOffset(label->offset()));
|
|
+ ma_liPatchable(SecondScratchReg, ImmWord(LabelBase::INVALID_OFFSET)); // 5
|
|
+ xs_mtctr(SecondScratchReg); // 6
|
|
+ as_bctr(); // 7
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Generate open jump and link it to a label.
|
|
+ // Second word holds a pointer to the next branch in label's chain.
|
|
+ uint32_t nextInChain = label->used() ? label->offset() : LabelBase::INVALID_OFFSET;
|
|
+
|
|
+ if (jumpKind == ShortJump) {
|
|
+ // Store the condition with a dummy branch, plus the next in chain.
|
|
+ // Unfortunately there is no way to make this take up less than two
|
|
+ // instructions, so we end up burning a nop at link time. Make the
|
|
+ // whole branch continuous in the buffer.
|
|
+ m_buffer.ensureSpace(4 * sizeof(uint32_t));
|
|
+
|
|
+ // Use a dummy short jump. This includes all the branch encoding, so
|
|
+ // we just have to change the offset at link time.
|
|
+ BufferOffset bo = as_bc(4, c, cr, NotLikelyB, DontLinkB);
|
|
+ spew(".long %08x ; next in chain", nextInChain);
|
|
+ writeInst(nextInChain);
|
|
+ if (!oom())
|
|
+ label->use(bo.getOffset());
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // As above with a reverse-sense long stanza.
|
|
+ BufferOffset bo;
|
|
+ m_buffer.ensureSpace(12 * sizeof(uint32_t)); // Worst case, with CR ops
|
|
+ if (c & ConditionOnlyXER) {
|
|
+ // bc cond .+8 (DO NOT ADJUST: see above)
|
|
+ // b .+32
|
|
+ // long jump
|
|
+ as_bc(2 * sizeof(uint32_t), c, cr, NotLikelyB, DontLinkB);
|
|
+ as_b(8 * sizeof(uint32_t));
|
|
+ bo = xs_trap_tagged(BTag); // don't try to flip sense when optimizing
|
|
+ } else {
|
|
+ as_bc(8 * sizeof(uint32_t), InvertCondition(c), cr, NotLikelyB, DontLinkB);
|
|
+ bo = xs_trap_tagged(BCTag);
|
|
+ }
|
|
+ spew(".long %08x ; next in chain", nextInChain);
|
|
+ // The tagged trap must be the offset, not the leading bc. See Assembler::bind and
|
|
+ // Assembler::retarget for why.
|
|
+ writeInst(nextInChain);
|
|
+ if (!oom())
|
|
+ label->use(bo.getOffset());
|
|
+ // Leave space for potential long jump.
|
|
+ as_nop(); // rldicr
|
|
+ as_nop(); // oris
|
|
+ as_nop(); // ori
|
|
+ as_nop(); // mtctr
|
|
+ as_nop(); // bctr
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_bc(Register lhs, ImmWord imm, Label* label, Condition c, JumpKind jumpKind)
|
|
+{
|
|
+ if (imm.value <= INT32_MAX) {
|
|
+ ma_bc64(lhs, Imm32(uint32_t(imm.value)), label, c, jumpKind);
|
|
+ } else {
|
|
+ MOZ_ASSERT(lhs != ScratchRegister);
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ ma_bc(lhs, ScratchRegister, label, c, jumpKind);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_bc(Register lhs, Address addr, Label* label, Condition c, JumpKind jumpKind)
|
|
+{
|
|
+ MOZ_ASSERT(lhs != ScratchRegister);
|
|
+ ma_load(ScratchRegister, addr, SizeDouble);
|
|
+ ma_bc(lhs, ScratchRegister, label, c, jumpKind);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_bc(Address addr, Imm32 imm, Label* label, Condition c, JumpKind jumpKind)
|
|
+{
|
|
+ ma_load(SecondScratchReg, addr, SizeDouble);
|
|
+ ma_bc(SecondScratchReg, imm, label, c, jumpKind);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_bc(Address addr, ImmGCPtr imm, Label* label, Condition c, JumpKind jumpKind)
|
|
+{
|
|
+ ma_load(SecondScratchReg, addr, SizeDouble);
|
|
+ ma_bc(SecondScratchReg, imm, label, c, jumpKind);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_bal(Label* label) // The whole world has gone MIPS, I tell ya.
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ // Branch to a subroutine.
|
|
+ spew("bl .Llabel %p", label);
|
|
+ if (label->bound()) {
|
|
+ // An entire 7-instruction stanza must be generated so that no matter how this
|
|
+ // is patched, the return address is the same (i.e., the instruction after the
|
|
+ // stanza). If this is a short branch, then it's 6 nops with the bl at the end.
|
|
+ BufferOffset b(label->offset());
|
|
+ m_buffer.ensureSpace(7 * sizeof(uint32_t));
|
|
+ BufferOffset dest = nextOffset();
|
|
+ int64_t offset = label->offset() - (dest.getOffset() + 6*sizeof(uint32_t));
|
|
+ if (JOffImm26::IsInRange(offset)) {
|
|
+ JOffImm26 j(offset);
|
|
+
|
|
+ as_nop();
|
|
+ as_nop();
|
|
+ as_nop();
|
|
+ as_nop(); // Yawn.
|
|
+ as_nop();
|
|
+ as_nop(); // Sigh.
|
|
+ as_b(j, RelativeBranch, LinkB);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Although this is to Ion code, use r12 to keep calls "as expected."
|
|
+ addLongJump(dest, b);
|
|
+ ma_liPatchable(SecondScratchReg, ImmWord(LabelBase::INVALID_OFFSET));
|
|
+ xs_mtctr(SecondScratchReg);
|
|
+ as_bctr(LinkB); // bctrl
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Second word holds a pointer to the next branch in label's chain.
|
|
+ uint32_t nextInChain = label->used() ? label->offset() : LabelBase::INVALID_OFFSET;
|
|
+ // Keep the whole branch stanza continuous in the buffer.
|
|
+ m_buffer.ensureSpace(7 * sizeof(uint32_t));
|
|
+ // Insert a tagged trap so the patcher knows what this is supposed to be.
|
|
+ BufferOffset bo = xs_trap_tagged(CallTag);
|
|
+ writeInst(nextInChain);
|
|
+ if (!oom())
|
|
+ label->use(bo.getOffset());
|
|
+ // Leave space for long jump.
|
|
+ as_nop(); // rldicr
|
|
+ as_nop(); // oris
|
|
+ as_nop(); // ori
|
|
+ as_nop(); // mtctr
|
|
+ as_nop(); // bctrl
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_cmp_set(Register rd, Register rs, ImmWord imm, Condition c, bool useCmpw)
|
|
+{
|
|
+ ADBlock();
|
|
+ if (imm.value <= INT16_MAX) { // unsigned
|
|
+ ma_cmp_set(rd, rs, Imm16(imm.value), c, useCmpw);
|
|
+ } else if (imm.value <= INT32_MAX) { // unsigned
|
|
+ ma_cmp_set(rd, rs, Imm32(imm.value), c, useCmpw);
|
|
+ } else {
|
|
+ MOZ_ASSERT(rd != ScratchRegister);
|
|
+ MOZ_ASSERT(rs != ScratchRegister);
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ ma_cmp_set(rd, rs, ScratchRegister, c, useCmpw);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_cmp_set(Register rd, Register rs, ImmPtr imm, Condition c, bool useCmpw)
|
|
+{
|
|
+ ma_cmp_set(rd, rs, ImmWord(uintptr_t(imm.value)), c, useCmpw);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_cmp_set(Register rd, Address addr, Register rs, Condition c, bool useCmpw)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(rd != ScratchRegister);
|
|
+ MOZ_ASSERT(rs != ScratchRegister);
|
|
+
|
|
+ asMasm().loadPtr(addr, ScratchRegister);
|
|
+ ma_cmp_set(rd, ScratchRegister, rs, c, useCmpw);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_cmp_set(Register rd, Address addr, Imm32 imm, Condition c, bool useCmpw)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(rd != ScratchRegister);
|
|
+ MOZ_ASSERT(rd != SecondScratchReg);
|
|
+
|
|
+ asMasm().loadPtr(addr, ScratchRegister);
|
|
+ ma_li(SecondScratchReg, imm);
|
|
+ ma_cmp_set(rd, ScratchRegister, SecondScratchReg, c, useCmpw);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_cmp_set(Register rd, Address addr, Imm64 imm, Condition c, bool useCmpw)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(rd != ScratchRegister);
|
|
+ MOZ_ASSERT(rd != SecondScratchReg);
|
|
+ MOZ_ASSERT(!useCmpw);
|
|
+
|
|
+ asMasm().loadPtr(addr, ScratchRegister);
|
|
+ ma_li(SecondScratchReg, imm);
|
|
+ ma_cmp_set(rd, ScratchRegister, SecondScratchReg, c, useCmpw);
|
|
+}
|
|
+
|
|
+// fp instructions
|
|
+void
|
|
+MacroAssemblerPPC64::ma_lid(FloatRegister dest, double value)
|
|
+{
|
|
+ ADBlock();
|
|
+ ImmWord imm(mozilla::BitwiseCast<uint64_t>(value));
|
|
+
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ asMasm().moveToDouble(ScratchRegister, dest);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::ma_ls(FloatRegister ft, Address address)
|
|
+{
|
|
+ FaultingCodeOffset fco;
|
|
+ if (Imm16::IsInSignedRange(address.offset)) {
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_lfs(ft, address.base, address.offset);
|
|
+ } else {
|
|
+ MOZ_ASSERT(address.base != ScratchRegister);
|
|
+ ma_li(ScratchRegister, Imm32(address.offset));
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_lfsx(ft, address.base, ScratchRegister);
|
|
+ }
|
|
+ return fco;
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::ma_ld(FloatRegister ft, Address address)
|
|
+{
|
|
+ FaultingCodeOffset fco;
|
|
+ if (Imm16::IsInSignedRange(address.offset)) {
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_lfd(ft, address.base, address.offset);
|
|
+ } else {
|
|
+ MOZ_ASSERT(address.base != ScratchRegister);
|
|
+ ma_li(ScratchRegister, Imm32(address.offset));
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_lfdx(ft, address.base, ScratchRegister);
|
|
+ }
|
|
+ return fco;
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::ma_sd(FloatRegister ft, Address address)
|
|
+{
|
|
+ FaultingCodeOffset fco;
|
|
+ if (Imm16::IsInSignedRange(address.offset)) {
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_stfd(ft, address.base, address.offset);
|
|
+ } else {
|
|
+ MOZ_ASSERT(address.base != ScratchRegister);
|
|
+ ma_li(ScratchRegister, Imm32(address.offset));
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_stfdx(ft, address.base, ScratchRegister);
|
|
+ }
|
|
+ return fco;
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::ma_ss(FloatRegister ft, Address address)
|
|
+{
|
|
+ FaultingCodeOffset fco;
|
|
+ if (Imm16::IsInSignedRange(address.offset)) {
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_stfs(ft, address.base, address.offset);
|
|
+ } else {
|
|
+ MOZ_ASSERT(address.base != ScratchRegister);
|
|
+ ma_li(ScratchRegister, Imm32(address.offset));
|
|
+ fco = FaultingCodeOffset(currentOffset());
|
|
+ as_stfsx(ft, address.base, ScratchRegister);
|
|
+ }
|
|
+ return fco;
|
|
+}
|
|
+
|
|
+// Keep pushes and pops of floats and doubles aligned to float.
|
|
+void
|
|
+MacroAssemblerPPC64::ma_pop(FloatRegister f)
|
|
+{
|
|
+ if (f.isSingle()) {
|
|
+ as_lfs(f, StackPointer, 0);
|
|
+ } else {
|
|
+ as_lfd(f, StackPointer, 0);
|
|
+ }
|
|
+ as_addi(StackPointer, StackPointer, sizeof(double));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_push(FloatRegister f)
|
|
+{
|
|
+ if (f.isSingle())
|
|
+ as_stfsu(f, StackPointer, (int32_t)-sizeof(double));
|
|
+ else
|
|
+ as_stfdu(f, StackPointer, (int32_t)-sizeof(double));
|
|
+}
|
|
+
|
|
+bool
|
|
+MacroAssemblerPPC64Compat::buildOOLFakeExitFrame(void* fakeReturnAddr)
|
|
+{
|
|
+ asMasm().PushFrameDescriptor(FrameType::IonJS); // descriptor_
|
|
+ asMasm().Push(ImmPtr(fakeReturnAddr));
|
|
+ asMasm().Push(FramePointer);
|
|
+ return true;
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::move32(Imm32 imm, Register dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ //uint64_t bits = (uint64_t)((int64_t)imm.value & 0x00000000ffffffff);
|
|
+ //ma_li(dest, bits);
|
|
+// XXX: why not just cast to int64_t now that we're fixed?
|
|
+ ma_li(dest, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::move32(Register src, Register dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ ma_move(dest, src);
|
|
+ // Ass-U-Me we explicitly want the upper 32-bits cleared.
|
|
+ //as_rldicl(dest, src, 0, 32); // "clrldi"
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::movePtr(Register src, Register dest)
|
|
+{
|
|
+ ma_move(dest, src);
|
|
+}
|
|
+void
|
|
+MacroAssemblerPPC64Compat::movePtr(ImmWord imm, Register dest)
|
|
+{
|
|
+ ma_li(dest, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::movePtr(ImmGCPtr imm, Register dest)
|
|
+{
|
|
+ ma_li(dest, imm); // tags the pointer for us
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::movePtr(ImmPtr imm, Register dest)
|
|
+{
|
|
+ movePtr(ImmWord(uintptr_t(imm.value)), dest);
|
|
+}
|
|
+void
|
|
+MacroAssemblerPPC64Compat::movePtr(wasm::SymbolicAddress imm, Register dest)
|
|
+{
|
|
+ append(wasm::SymbolicAccess(CodeOffset(nextOffset().getOffset()), imm));
|
|
+ ma_liPatchable(dest, ImmWord(-1));
|
|
+}
|
|
+
|
|
+CodeOffset MacroAssembler::moveNearAddressWithPatch(Register dest) {
|
|
+ return movWithPatch(ImmPtr(nullptr), dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::patchNearAddressMove(CodeLocationLabel loc,
|
|
+ CodeLocationLabel target)
|
|
+{
|
|
+ PatchDataWithValueCheck(loc, ImmPtr(target.raw()), ImmPtr(nullptr));
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::load8ZeroExtend(const Address& address, Register dest)
|
|
+{
|
|
+ return ma_load(dest, address, SizeByte, ZeroExtend);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::load8ZeroExtend(const BaseIndex& src, Register dest)
|
|
+{
|
|
+ return ma_load(dest, src, SizeByte, ZeroExtend);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::load8SignExtend(const Address& address, Register dest)
|
|
+{
|
|
+ return ma_load(dest, address, SizeByte, SignExtend);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::load8SignExtend(const BaseIndex& src, Register dest)
|
|
+{
|
|
+ return ma_load(dest, src, SizeByte, SignExtend);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::load16ZeroExtend(const Address& address, Register dest)
|
|
+{
|
|
+ return ma_load(dest, address, SizeHalfWord, ZeroExtend);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::load16ZeroExtend(const BaseIndex& src, Register dest)
|
|
+{
|
|
+ return ma_load(dest, src, SizeHalfWord, ZeroExtend);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::load16SignExtend(const Address& address, Register dest)
|
|
+{
|
|
+ return ma_load(dest, address, SizeHalfWord, SignExtend);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::load16SignExtend(const BaseIndex& src, Register dest)
|
|
+{
|
|
+ return ma_load(dest, src, SizeHalfWord, SignExtend);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::load32(const Address& address, Register dest)
|
|
+{
|
|
+ // This must sign-extend for arithmetic to function correctly.
|
|
+ return ma_load(dest, address, SizeWord);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::load32(const BaseIndex& address, Register dest)
|
|
+{
|
|
+ // This must sign-extend for arithmetic to function correctly.
|
|
+ return ma_load(dest, address, SizeWord);
|
|
+}
|
|
+
|
|
+// Zero-extend versions, mostly for wasm.
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::load32ZeroExtend(const Address& address, Register dest)
|
|
+{
|
|
+ // This must sign-extend for arithmetic to function correctly.
|
|
+ return ma_load(dest, address, SizeWord, ZeroExtend);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::load32ZeroExtend(const BaseIndex& address, Register dest)
|
|
+{
|
|
+ // This must sign-extend for arithmetic to function correctly.
|
|
+ return ma_load(dest, address, SizeWord, ZeroExtend);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::load32(AbsoluteAddress address, Register dest)
|
|
+{
|
|
+ movePtr(ImmPtr(address.addr), SecondScratchReg);
|
|
+ return load32(Address(SecondScratchReg, 0), dest);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::load32(wasm::SymbolicAddress address, Register dest)
|
|
+{
|
|
+ movePtr(address, SecondScratchReg);
|
|
+ return load32(Address(SecondScratchReg, 0), dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::loadPtr(Register src, Register dest)
|
|
+{
|
|
+ if (src != dest)
|
|
+ xs_mr(dest, src);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::loadPtr(const Address& address, Register dest)
|
|
+{
|
|
+ return ma_load(dest, address, SizeDouble);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::loadPtr(const BaseIndex& src, Register dest)
|
|
+{
|
|
+ return ma_load(dest, src, SizeDouble);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::loadPtr(AbsoluteAddress address, Register dest)
|
|
+{
|
|
+ movePtr(ImmPtr(address.addr), SecondScratchReg);
|
|
+ return loadPtr(Address(SecondScratchReg, 0), dest);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::loadPtr(wasm::SymbolicAddress address, Register dest)
|
|
+{
|
|
+ movePtr(address, SecondScratchReg);
|
|
+ return loadPtr(Address(SecondScratchReg, 0), dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::loadPrivate(const Address& address, Register dest)
|
|
+{
|
|
+ loadPtr(address, dest);
|
|
+}
|
|
+
|
|
+/*
|
|
+void
|
|
+MacroAssemblerPPC64Compat::loadUnalignedDouble(const wasm::MemoryAccessDesc& access,
|
|
+ const BaseIndex& src, Register temp, FloatRegister dest)
|
|
+{
|
|
+ loadDouble(src, dest);
|
|
+ asMasm().append(access, asMasm().size() - 4); // lfd terminates
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::loadUnalignedFloat32(const wasm::MemoryAccessDesc& access,
|
|
+ const BaseIndex& src, Register temp, FloatRegister dest)
|
|
+{
|
|
+ loadFloat32(src, dest);
|
|
+ asMasm().append(access, asMasm().size() - 4); // lfs terminates
|
|
+}
|
|
+*/
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::store8(Imm32 imm, const Address& address)
|
|
+{
|
|
+ MOZ_ASSERT(address.base != ScratchRegister);
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ return ma_store(ScratchRegister, address, SizeByte);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::store8(Register src, const Address& address)
|
|
+{
|
|
+ return ma_store(src, address, SizeByte);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::store8(Imm32 imm, const BaseIndex& dest)
|
|
+{
|
|
+ return ma_store(imm, dest, SizeByte);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::store8(Register src, const BaseIndex& dest)
|
|
+{
|
|
+ return ma_store(src, dest, SizeByte);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::store16(Imm32 imm, const Address& address)
|
|
+{
|
|
+ MOZ_ASSERT(address.base != ScratchRegister);
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ return ma_store(ScratchRegister, address, SizeHalfWord);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::store16(Register src, const Address& address)
|
|
+{
|
|
+ return ma_store(src, address, SizeHalfWord);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::store16(Imm32 imm, const BaseIndex& dest)
|
|
+{
|
|
+ return ma_store(imm, dest, SizeHalfWord);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::store16(Register src, const BaseIndex& address)
|
|
+{
|
|
+ return ma_store(src, address, SizeHalfWord);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::store32(Register src, AbsoluteAddress address)
|
|
+{
|
|
+ MOZ_ASSERT(src != SecondScratchReg);
|
|
+ movePtr(ImmPtr(address.addr), SecondScratchReg);
|
|
+ return store32(src, Address(SecondScratchReg, 0));
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::store32(Register src, const Address& address)
|
|
+{
|
|
+ return ma_store(src, address, SizeWord);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::store32(Imm32 src, const Address& address)
|
|
+{
|
|
+ MOZ_ASSERT(address.base != ScratchRegister);
|
|
+ move32(src, ScratchRegister);
|
|
+ return ma_store(ScratchRegister, address, SizeWord);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::store32(Imm32 imm, const BaseIndex& dest)
|
|
+{
|
|
+ return ma_store(imm, dest, SizeWord);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::store32(Register src, const BaseIndex& dest)
|
|
+{
|
|
+ return ma_store(src, dest, SizeWord);
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::storePtr(ImmWord imm, T address)
|
|
+{
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ return ma_store(ScratchRegister, address, SizeDouble);
|
|
+}
|
|
+
|
|
+template FaultingCodeOffset MacroAssemblerPPC64Compat::storePtr<Address>(ImmWord imm, Address address);
|
|
+template FaultingCodeOffset MacroAssemblerPPC64Compat::storePtr<BaseIndex>(ImmWord imm, BaseIndex address);
|
|
+
|
|
+template <typename T>
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::storePtr(ImmPtr imm, T address)
|
|
+{
|
|
+ return storePtr(ImmWord(uintptr_t(imm.value)), address);
|
|
+}
|
|
+
|
|
+template FaultingCodeOffset MacroAssemblerPPC64Compat::storePtr<Address>(ImmPtr imm, Address address);
|
|
+template FaultingCodeOffset MacroAssemblerPPC64Compat::storePtr<BaseIndex>(ImmPtr imm, BaseIndex address);
|
|
+
|
|
+template <typename T>
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::storePtr(ImmGCPtr imm, T address)
|
|
+{
|
|
+ movePtr(imm, ScratchRegister);
|
|
+ return storePtr(ScratchRegister, address);
|
|
+}
|
|
+
|
|
+template FaultingCodeOffset MacroAssemblerPPC64Compat::storePtr<Address>(ImmGCPtr imm, Address address);
|
|
+template FaultingCodeOffset MacroAssemblerPPC64Compat::storePtr<BaseIndex>(ImmGCPtr imm, BaseIndex address);
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::storePtr(Register src, const Address& address)
|
|
+{
|
|
+ return ma_store(src, address, SizeDouble);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::storePtr(Register src, const BaseIndex& address)
|
|
+{
|
|
+ return ma_store(src, address, SizeDouble);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64Compat::storePtr(Register src, AbsoluteAddress dest)
|
|
+{
|
|
+ MOZ_ASSERT(src != SecondScratchReg);
|
|
+ movePtr(ImmPtr(dest.addr), SecondScratchReg);
|
|
+ return storePtr(src, Address(SecondScratchReg, 0));
|
|
+}
|
|
+
|
|
+/*
|
|
+void
|
|
+MacroAssemblerPPC64Compat::storeUnalignedFloat32(const wasm::MemoryAccessDesc& access,
|
|
+ FloatRegister src, Register temp, const BaseIndex& dest)
|
|
+{
|
|
+ computeScaledAddress(dest, SecondScratchReg);
|
|
+
|
|
+ as_stfs(src, SecondScratchReg, 0);
|
|
+ append(access, asMasm().size() - 4);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::storeUnalignedDouble(const wasm::MemoryAccessDesc& access,
|
|
+ FloatRegister src, Register temp, const BaseIndex& dest)
|
|
+{
|
|
+ computeScaledAddress(dest, SecondScratchReg);
|
|
+
|
|
+ as_stfd(src, SecondScratchReg, 0);
|
|
+ append(access, asMasm().size() - 4);
|
|
+}
|
|
+*/
|
|
+
|
|
+void
|
|
+MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(input != ScratchDoubleReg);
|
|
+
|
|
+ // Default rounding mode 0b00 (round nearest)
|
|
+ as_fctid(ScratchDoubleReg, input);
|
|
+ moveFromDouble(ScratchDoubleReg, output);
|
|
+ clampIntToUint8(output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::testNullSet(Condition cond, const ValueOperand& value, Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ splitTag(value, SecondScratchReg);
|
|
+ ma_cmp_set(dest, SecondScratchReg, ImmTag(JSVAL_TAG_NULL), cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::testObjectSet(Condition cond, const ValueOperand& value, Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ splitTag(value, SecondScratchReg);
|
|
+ ma_cmp_set(dest, SecondScratchReg, ImmTag(JSVAL_TAG_OBJECT), cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::testUndefinedSet(Condition cond, const ValueOperand& value, Register dest)
|
|
+{
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ splitTag(value, SecondScratchReg);
|
|
+ ma_cmp_set(dest, SecondScratchReg, ImmTag(JSVAL_TAG_UNDEFINED), cond);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxInt32(const ValueOperand& operand, Register dest)
|
|
+{
|
|
+ unboxInt32(operand.valueReg(), dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxInt32(Register src, Register dest)
|
|
+{
|
|
+ // Ensure there is no tag. This erases bits 32-63 and sign extends
|
|
+ // in one single operation.
|
|
+ as_srawi(dest, src, 0);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxInt32(const Address& src, Register dest)
|
|
+{
|
|
+ load32(Address(src.base, src.offset), dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxInt32(const BaseIndex& src, Register dest)
|
|
+{
|
|
+ computeScaledAddress(src, SecondScratchReg);
|
|
+ load32(Address(SecondScratchReg, src.offset), dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxBoolean(const ValueOperand& operand, Register dest)
|
|
+{
|
|
+ unboxBoolean(operand.valueReg(), dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxBoolean(Register src, Register dest)
|
|
+{
|
|
+ // Clear upper 32 bits. srawi would also work.
|
|
+ as_rldicl(dest, src, 0, 32); // "clrldi"
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxBoolean(const Address& src, Register dest)
|
|
+{
|
|
+ ma_load(dest, Address(src.base, src.offset), SizeWord, ZeroExtend);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxBoolean(const BaseIndex& src, Register dest)
|
|
+{
|
|
+ computeScaledAddress(src, SecondScratchReg);
|
|
+ ma_load(dest, Address(SecondScratchReg, src.offset), SizeWord, ZeroExtend);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxDouble(const ValueOperand& operand, FloatRegister dest)
|
|
+{
|
|
+ moveToDouble(operand.valueReg(), dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxDouble(const Address& src, FloatRegister dest)
|
|
+{
|
|
+ ma_ld(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxDouble(const BaseIndex& src, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(src.base != SecondScratchReg);
|
|
+
|
|
+ computeScaledAddress(src, SecondScratchReg);
|
|
+ ma_ld(dest, Address(SecondScratchReg, src.offset));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxString(const ValueOperand& operand, Register dest)
|
|
+{
|
|
+ unboxNonDouble(operand, dest, JSVAL_TYPE_STRING);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxString(Register src, Register dest)
|
|
+{
|
|
+ unboxNonDouble(src, dest, JSVAL_TYPE_STRING);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxString(const Address& src, Register dest)
|
|
+{
|
|
+ unboxNonDouble(src, dest, JSVAL_TYPE_STRING);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxSymbol(const ValueOperand& operand, Register dest)
|
|
+{
|
|
+ unboxNonDouble(operand, dest, JSVAL_TYPE_SYMBOL);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxSymbol(Register src, Register dest)
|
|
+{
|
|
+ unboxNonDouble(src, dest, JSVAL_TYPE_SYMBOL);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxSymbol(const Address& src, Register dest)
|
|
+{
|
|
+ unboxNonDouble(src, dest, JSVAL_TYPE_SYMBOL);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxObject(const ValueOperand& src, Register dest)
|
|
+{
|
|
+ unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxObject(Register src, Register dest)
|
|
+{
|
|
+ unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxObject(const Address& src, Register dest)
|
|
+{
|
|
+ unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxValue(const ValueOperand& src, AnyRegister dest, JSValueType type)
|
|
+{
|
|
+ if (dest.isFloat()) {
|
|
+ Label notInt32, end;
|
|
+ asMasm().branchTestInt32(Assembler::NotEqual, src, ¬Int32);
|
|
+ convertInt32ToDouble(src.valueReg(), dest.fpu());
|
|
+ ma_b(&end, ShortJump);
|
|
+ bind(¬Int32);
|
|
+ unboxDouble(src, dest.fpu());
|
|
+ bind(&end);
|
|
+ } else {
|
|
+ unboxNonDouble(src, dest.gpr(), type);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::unboxPrivate(const ValueOperand& src, Register dest)
|
|
+{
|
|
+ ma_dsll(dest, src.valueReg(), Imm32(1));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::boxDouble(FloatRegister src, const ValueOperand& dest, FloatRegister)
|
|
+{
|
|
+ moveFromDouble(src, dest.valueReg());
|
|
+}
|
|
+
|
|
+void MacroAssemblerPPC64Compat::unboxBigInt(const ValueOperand& operand,
|
|
+ Register dest) {
|
|
+ unboxNonDouble(operand, dest, JSVAL_TYPE_BIGINT);
|
|
+}
|
|
+
|
|
+void MacroAssemblerPPC64Compat::unboxBigInt(Register src, Register dest) {
|
|
+ unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT);
|
|
+}
|
|
+
|
|
+void MacroAssemblerPPC64Compat::unboxBigInt(const Address& src,
|
|
+ Register dest) {
|
|
+ unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::boxNonDouble(JSValueType type, Register src,
|
|
+ const ValueOperand& dest)
|
|
+{
|
|
+ boxValue(type, src, dest.valueReg());
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::boolValueToDouble(const ValueOperand& operand, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ convertBoolToInt32(operand.valueReg(), ScratchRegister);
|
|
+ convertInt32ToDouble(ScratchRegister, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::int32ValueToDouble(const ValueOperand& operand,
|
|
+ FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ // Use the lower bits of the operand's value register.
|
|
+ MOZ_ASSERT(operand.valueReg() != ScratchRegister);
|
|
+ as_srawi(ScratchRegister, operand.valueReg(), 0);
|
|
+ convertInt32ToDouble(ScratchRegister, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::boolValueToFloat32(const ValueOperand& operand,
|
|
+ FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ convertBoolToInt32(operand.valueReg(), ScratchRegister);
|
|
+ convertInt32ToFloat32(ScratchRegister, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::int32ValueToFloat32(const ValueOperand& operand,
|
|
+ FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ // Use the lower bits of the operand's value register.
|
|
+ MOZ_ASSERT(operand.valueReg() != ScratchRegister);
|
|
+ as_srawi(ScratchRegister, operand.valueReg(), 0);
|
|
+ convertInt32ToFloat32(ScratchRegister, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::loadConstantFloat32(float f, FloatRegister dest)
|
|
+{
|
|
+ if (f == 0.0 && !signbit(f)) zeroDouble(dest);
|
|
+ else ma_lis(dest, f);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::loadInt32OrDouble(const Address& src, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ Label notInt32, end;
|
|
+
|
|
+MOZ_CRASH("NYI loadInt32OrDouble");
|
|
+xs_trap();
|
|
+ // If it's an int, convert it to double.
|
|
+ loadPtr(Address(src.base, src.offset), ScratchRegister);
|
|
+ ma_dsrl(SecondScratchReg, ScratchRegister, Imm32(JSVAL_TAG_SHIFT));
|
|
+ asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, ¬Int32);
|
|
+ loadPtr(Address(src.base, src.offset), SecondScratchReg);
|
|
+ convertInt32ToDouble(SecondScratchReg, dest);
|
|
+ ma_b(&end, ShortJump);
|
|
+
|
|
+ // Not an int, just load as double.
|
|
+ bind(¬Int32);
|
|
+ ma_ld(dest, src);
|
|
+ bind(&end);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::loadInt32OrDouble(const BaseIndex& addr, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ Label notInt32, end;
|
|
+
|
|
+MOZ_CRASH("NYI loadInt32OrDouble BI");
|
|
+xs_trap();
|
|
+ // If it's an int, convert it to double.
|
|
+ computeScaledAddress(addr, SecondScratchReg);
|
|
+ // Since we only have one scratch, we need to stomp over it with the tag.
|
|
+ loadPtr(Address(SecondScratchReg, 0), ScratchRegister);
|
|
+ ma_dsrl(SecondScratchReg, ScratchRegister, Imm32(JSVAL_TAG_SHIFT));
|
|
+ asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, ¬Int32);
|
|
+
|
|
+ computeScaledAddress(addr, SecondScratchReg);
|
|
+ loadPtr(Address(SecondScratchReg, 0), SecondScratchReg);
|
|
+ convertInt32ToDouble(SecondScratchReg, dest);
|
|
+ ma_b(&end, ShortJump);
|
|
+
|
|
+ // Not an int, just load as double.
|
|
+ bind(¬Int32);
|
|
+ // First, recompute the offset that had been stored in the scratch register
|
|
+ // since the scratch register was overwritten loading in the type.
|
|
+ computeScaledAddress(addr, SecondScratchReg);
|
|
+ loadDouble(Address(SecondScratchReg, 0), dest);
|
|
+ bind(&end);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::loadConstantDouble(double dp, FloatRegister dest)
|
|
+{
|
|
+ if (dp == 0.0 && !signbit(dp)) zeroDouble(dest);
|
|
+ else ma_lid(dest, dp);
|
|
+}
|
|
+
|
|
+Register
|
|
+MacroAssemblerPPC64Compat::extractObject(const Address& address, Register scratch)
|
|
+{
|
|
+ loadPtr(Address(address.base, address.offset), scratch);
|
|
+ as_rldicl(scratch, scratch, 0, 64-JSVAL_TAG_SHIFT); // clrldi the tag
|
|
+ return scratch;
|
|
+}
|
|
+
|
|
+Register
|
|
+MacroAssemblerPPC64Compat::extractTag(const Address& address, Register scratch)
|
|
+{
|
|
+ loadPtr(Address(address.base, address.offset), scratch);
|
|
+ as_rldicl(scratch, scratch, 64-JSVAL_TAG_SHIFT, JSVAL_TAG_SHIFT); // "srdi"
|
|
+ return scratch;
|
|
+}
|
|
+
|
|
+Register
|
|
+MacroAssemblerPPC64Compat::extractTag(const BaseIndex& address, Register scratch)
|
|
+{
|
|
+ computeScaledAddress(address, scratch);
|
|
+ return extractTag(Address(scratch, address.offset), scratch);
|
|
+}
|
|
+
|
|
+/////////////////////////////////////////////////////////////////
|
|
+// X86/X64-common/ARM/MIPS interface.
|
|
+/////////////////////////////////////////////////////////////////
|
|
+void
|
|
+MacroAssemblerPPC64Compat::storeValue(ValueOperand val, Operand dst)
|
|
+{
|
|
+ storeValue(val, Address(Register::FromCode(dst.base()), dst.disp()));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::storeValue(ValueOperand val, const BaseIndex& dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ computeScaledAddress(dest, SecondScratchReg);
|
|
+
|
|
+ int32_t offset = dest.offset;
|
|
+ if (!Imm16::IsInSignedRange(offset)) {
|
|
+ ma_li(ScratchRegister, Imm32(offset));
|
|
+ as_add(SecondScratchReg, ScratchRegister, SecondScratchReg);
|
|
+ offset = 0;
|
|
+ }
|
|
+
|
|
+ storeValue(val, Address(SecondScratchReg, offset));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::storeValue(JSValueType type, Register reg, BaseIndex dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ computeScaledAddress(dest, SecondScratchReg);
|
|
+
|
|
+ int32_t offset = dest.offset;
|
|
+ if (!Imm16::IsInSignedRange(offset)) {
|
|
+ ma_li(ScratchRegister, Imm32(offset));
|
|
+ as_add(SecondScratchReg, ScratchRegister, SecondScratchReg);
|
|
+ offset = 0;
|
|
+ }
|
|
+
|
|
+ storeValue(type, reg, Address(SecondScratchReg, offset));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::storeValue(ValueOperand val, const Address& dest)
|
|
+{
|
|
+ storePtr(val.valueReg(), dest);
|
|
+}
|
|
+
|
|
+static_assert(JSVAL_TAG_SHIFT == 47); // a lot would break if this changed ...
|
|
+void
|
|
+MacroAssemblerPPC64Compat::storeValue(JSValueType type, Register reg, Address dest)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
|
|
+ // This isn't elegant, but saves us some scratch register contention.
|
|
+ MOZ_ASSERT(reg != ScratchRegister);
|
|
+ MOZ_ASSERT(dest.base != ScratchRegister);
|
|
+ MOZ_ASSERT(Imm16::IsInSignedRange(dest.offset + 4));
|
|
+
|
|
+ store32(reg, dest);
|
|
+ uint32_t utag = (uint32_t)JSVAL_TYPE_TO_TAG(type) << (JSVAL_TAG_SHIFT - 32);
|
|
+ // Hardcode this as ma_li doesn't generate good code here. It will
|
|
+ // never fit into a 16-bit immediate.
|
|
+ xs_lis(ScratchRegister, (utag >> 16));
|
|
+ as_ori(ScratchRegister, ScratchRegister, utag & 0x0000ffff);
|
|
+ // ENDIAN!!!
|
|
+ store32(ScratchRegister, Address(dest.base, dest.offset + 4));
|
|
+ } else {
|
|
+ MOZ_ASSERT(reg != SecondScratchReg);
|
|
+ if (dest.base == SecondScratchReg) {
|
|
+ MOZ_ASSERT(reg != ScratchRegister);
|
|
+
|
|
+ // The destination register for boxValue can't be r0, so we need
|
|
+ // the third scratch register.
|
|
+ boxValue(type, reg, ThirdScratchReg);
|
|
+ storePtr(ThirdScratchReg, dest);
|
|
+ } else {
|
|
+ boxValue(type, reg, SecondScratchReg);
|
|
+ storePtr(SecondScratchReg, dest);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::storeValue(const Value& val, Address dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ if (val.isGCThing()) {
|
|
+ writeDataRelocation(val);
|
|
+ movWithPatch(ImmWord(val.asRawBits()), ScratchRegister);
|
|
+ } else {
|
|
+ ma_li(ScratchRegister, ImmWord(val.asRawBits()));
|
|
+ }
|
|
+ storePtr(ScratchRegister, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::storeValue(const Value& val, BaseIndex dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ computeScaledAddress(dest, SecondScratchReg);
|
|
+
|
|
+ int32_t offset = dest.offset;
|
|
+ if (!Imm16::IsInSignedRange(offset)) {
|
|
+ ma_li(ScratchRegister, Imm32(offset));
|
|
+ as_add(SecondScratchReg, ScratchRegister, SecondScratchReg);
|
|
+ offset = 0;
|
|
+ }
|
|
+ storeValue(val, Address(SecondScratchReg, offset));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::loadValue(const BaseIndex& addr, ValueOperand val)
|
|
+{
|
|
+ computeScaledAddress(addr, SecondScratchReg);
|
|
+ loadValue(Address(SecondScratchReg, addr.offset), val);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::loadValue(Address src, ValueOperand val)
|
|
+{
|
|
+ loadPtr(src, val.valueReg());
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::boxValue(JSValueType type, Register src, Register dest) {
|
|
+ MOZ_ASSERT(dest != ScratchRegister);
|
|
+ if (dest != src)
|
|
+ ma_move(dest, src);
|
|
+ ma_li(ScratchRegister, ImmTag(JSVAL_TYPE_TO_TAG(type)));
|
|
+ // It is possible we sign extended too far, and this matters to code
|
|
+ // working directly with values, so clear the bits for int32 and bool.
|
|
+ if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
|
|
+ as_rldicl(dest, dest, 0, 32); // "clrldi"
|
|
+ }
|
|
+ // Shift the tag left and mask in the value, which is pretty much
|
|
+ // what rldimi/rlwimi were created for.
|
|
+ as_rldimi(dest, ScratchRegister, JSVAL_TAG_SHIFT, 0);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::tagValue(JSValueType type, Register payload, ValueOperand dest)
|
|
+{
|
|
+ ADBlock();
|
|
+ boxValue(type, payload, dest.valueReg());
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::pushValue(ValueOperand val)
|
|
+{
|
|
+ as_stdu(val.valueReg(), StackPointer, -8);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::pushValue(const Address& addr)
|
|
+{
|
|
+ // Load value before allocating stack: addr.base may be SP.
|
|
+ loadPtr(Address(addr.base, addr.offset), ScratchRegister);
|
|
+ ma_dsubu(StackPointer, StackPointer, Imm32(sizeof(Value)));
|
|
+ storePtr(ScratchRegister, Address(StackPointer, 0));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::popValue(ValueOperand val)
|
|
+{
|
|
+ as_ld(val.valueReg(), StackPointer, 0);
|
|
+ as_addi(StackPointer, StackPointer, sizeof(Value));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::breakpoint()
|
|
+{
|
|
+ xs_trap();
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::ensureDouble(const ValueOperand& source, FloatRegister dest,
|
|
+ Label* failure)
|
|
+{
|
|
+ Label isDouble, done;
|
|
+ {
|
|
+ ScratchTagScope tag(asMasm(), source);
|
|
+ splitTagForTest(source, tag);
|
|
+ asMasm().branchTestDouble(Assembler::Equal, tag, &isDouble);
|
|
+ asMasm().branchTestInt32(Assembler::NotEqual, tag, failure);
|
|
+ }
|
|
+
|
|
+ unboxInt32(source, ScratchRegister);
|
|
+ convertInt32ToDouble(ScratchRegister, dest);
|
|
+ jump(&done);
|
|
+
|
|
+ bind(&isDouble);
|
|
+ unboxDouble(source, dest);
|
|
+
|
|
+ bind(&done);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::checkStackAlignment()
|
|
+{
|
|
+#ifdef DEBUG
|
|
+ Label aligned;
|
|
+ as_andi_rc(ScratchRegister, sp, StackAlignment - 1);
|
|
+ ma_bc(ScratchRegister, ScratchRegister, &aligned, Zero, ShortJump);
|
|
+ xs_trap(); /* untagged so we know it's a bug */
|
|
+ bind(&aligned);
|
|
+#endif
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::handleFailureWithHandlerTail(Label* profilerExitTail, Label* bailoutTail)
|
|
+{
|
|
+ // Reserve space for exception information.
|
|
+ int size = (sizeof(ResumeFromException) + ABIStackAlignment) & ~(ABIStackAlignment - 1);
|
|
+ asMasm().subPtr(Imm32(size), StackPointer);
|
|
+ ma_move(r3, StackPointer); // Use r3 since it is a first function argument
|
|
+
|
|
+ // Call the handler.
|
|
+ using Fn = void (*)(ResumeFromException * rfe);
|
|
+ asMasm().setupUnalignedABICall(r4);
|
|
+ asMasm().passABIArg(r3);
|
|
+ asMasm().callWithABI<Fn, HandleException>(ABIType::General,
|
|
+ CheckUnsafeCallWithABI::DontCheckHasExitFrame);
|
|
+
|
|
+ Label entryFrame;
|
|
+ Label catch_;
|
|
+ Label finally;
|
|
+ Label returnBaseline;
|
|
+ Label returnIon;
|
|
+ Label bailout;
|
|
+ Label wasm;
|
|
+ Label wasmCatch;
|
|
+
|
|
+ // Already clobbered r3, so use it...
|
|
+ load32(Address(StackPointer, ResumeFromException::offsetOfKind()), r3);
|
|
+ asMasm().branch32(Assembler::Equal, r3,
|
|
+ Imm32(ExceptionResumeKind::EntryFrame), &entryFrame);
|
|
+ asMasm().branch32(Assembler::Equal, r3, Imm32(ExceptionResumeKind::Catch),
|
|
+ &catch_);
|
|
+ asMasm().branch32(Assembler::Equal, r3, Imm32(ExceptionResumeKind::Finally),
|
|
+ &finally);
|
|
+ asMasm().branch32(Assembler::Equal, r3,
|
|
+ Imm32(ExceptionResumeKind::ForcedReturnBaseline),
|
|
+ &returnBaseline);
|
|
+ asMasm().branch32(Assembler::Equal, r3,
|
|
+ Imm32(ExceptionResumeKind::ForcedReturnIon), &returnIon);
|
|
+ asMasm().branch32(Assembler::Equal, r3, Imm32(ExceptionResumeKind::Bailout),
|
|
+ &bailout);
|
|
+ asMasm().branch32(Assembler::Equal, r3, Imm32(ExceptionResumeKind::Wasm),
|
|
+ &wasm);
|
|
+ asMasm().branch32(Assembler::Equal, r3, Imm32(ExceptionResumeKind::WasmCatch),
|
|
+ &wasmCatch);
|
|
+
|
|
+ xs_trap(); // Invalid kind.
|
|
+
|
|
+ // No exception handler. Load the error value, restore state
|
|
+ // and return from the entry frame.
|
|
+ bind(&entryFrame);
|
|
+ asMasm().moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
|
|
+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
|
|
+ FramePointer);
|
|
+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
|
|
+ StackPointer);
|
|
+
|
|
+ // We're going to be returning by the ion calling convention
|
|
+ ma_pop(ScratchRegister);
|
|
+ xs_mtlr(ScratchRegister);
|
|
+ as_blr();
|
|
+
|
|
+ // If we found a catch handler, this must be a baseline frame. Restore
|
|
+ // state and jump to the catch block.
|
|
+ bind(&catch_);
|
|
+ // Use r12 here to save a register swap later in case we jump to ABI code.
|
|
+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfTarget()), r12);
|
|
+ xs_mtctr(r12); // mtspr immediately, could be clobbered by subsequent loads
|
|
+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
|
|
+ FramePointer);
|
|
+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
|
|
+ StackPointer);
|
|
+ as_bctr();
|
|
+
|
|
+ // If we found a finally block, this must be a baseline frame. Push
|
|
+ // two values expected by the finally block: the exception and
|
|
+ // BooleanValue(true).
|
|
+ bind(&finally);
|
|
+ ValueOperand exception = ValueOperand(r4);
|
|
+ loadValue(Address(sp, ResumeFromException::offsetOfException()), exception);
|
|
+
|
|
+ ValueOperand exceptionStack = ValueOperand(r5);
|
|
+ loadValue(Address(sp, ResumeFromException::offsetOfExceptionStack()),
|
|
+ exceptionStack);
|
|
+
|
|
+ loadPtr(Address(sp, ResumeFromException::offsetOfTarget()), r12);
|
|
+ xs_mtctr(r12);
|
|
+ loadPtr(Address(sp, ResumeFromException::offsetOfFramePointer()), FramePointer);
|
|
+ loadPtr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp);
|
|
+
|
|
+ pushValue(exception);
|
|
+ pushValue(exceptionStack);
|
|
+ pushValue(BooleanValue(true));
|
|
+ as_bctr();
|
|
+
|
|
+ // Return BaselineFrame->returnValue() to the caller.
|
|
+ // Used in debug mode and for GeneratorReturn.
|
|
+ Label profilingInstrumentation;
|
|
+ bind(&returnBaseline);
|
|
+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
|
|
+ FramePointer);
|
|
+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
|
|
+ StackPointer);
|
|
+ loadValue(Address(FramePointer, BaselineFrame::reverseOffsetOfReturnValue()),
|
|
+ JSReturnOperand);
|
|
+ jump(&profilingInstrumentation);
|
|
+
|
|
+ // Return the given value to the caller.
|
|
+ bind(&returnIon);
|
|
+ loadValue(Address(StackPointer, ResumeFromException::offsetOfException()),
|
|
+ JSReturnOperand);
|
|
+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
|
|
+ FramePointer);
|
|
+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
|
|
+ StackPointer);
|
|
+
|
|
+ // If profiling is enabled, then update the lastProfilingFrame to refer to
|
|
+ // caller frame before returning. This code is shared by ForcedReturnIon
|
|
+ // and ForcedReturnBaseline.
|
|
+ bind(&profilingInstrumentation);
|
|
+ {
|
|
+ Label skipProfilingInstrumentation;
|
|
+ // Test if profiler enabled.
|
|
+ AbsoluteAddress addressOfEnabled(
|
|
+ asMasm().runtime()->geckoProfiler().addressOfEnabled());
|
|
+ asMasm().branch32(Assembler::Equal, addressOfEnabled, Imm32(0),
|
|
+ &skipProfilingInstrumentation);
|
|
+ jump(profilerExitTail);
|
|
+ bind(&skipProfilingInstrumentation);
|
|
+ }
|
|
+
|
|
+ ma_move(StackPointer, FramePointer);
|
|
+ pop(FramePointer);
|
|
+ ret();
|
|
+
|
|
+ // If we are bailing out to baseline to handle an exception, jump to
|
|
+ // the bailout tail stub. Load 1 (true) in ReturnReg to indicate
|
|
+ // success.
|
|
+ bind(&bailout);
|
|
+ loadPtr(Address(sp, ResumeFromException::offsetOfBailoutInfo()), r5);
|
|
+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
|
|
+ StackPointer);
|
|
+ ma_li(ReturnReg, Imm32(1));
|
|
+ jump(bailoutTail);
|
|
+
|
|
+ // If we are throwing and the innermost frame was a wasm frame, reset SP and
|
|
+ // FP; SP is pointing to the unwound return address to the wasm entry, so
|
|
+ // we can just ret().
|
|
+ bind(&wasm);
|
|
+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()), FramePointer);
|
|
+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), StackPointer);
|
|
+ ma_li(InstanceReg, ImmWord(wasm::FailInstanceReg));
|
|
+ ret();
|
|
+
|
|
+ // Found a wasm catch handler, restore state and jump to it.
|
|
+ bind(&wasmCatch);
|
|
+xs_trap(); // XXX I'm suspicious of this code
|
|
+ wasm::GenerateJumpToCatchHandler(asMasm(), sp, r4, r5);
|
|
+#if(0)
|
|
+ loadPtr(Address(sp, ResumeFromException::offsetOfTarget()), r12);
|
|
+ xs_mtctr(r12);
|
|
+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()),
|
|
+ FramePointer);
|
|
+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()),
|
|
+ StackPointer);
|
|
+ as_bctr();
|
|
+#endif
|
|
+}
|
|
+
|
|
+// Toggled jumps and calls consist of oris r0,r0,0 followed by a full 7-instruction stanza.
|
|
+// This distinguishes it from other kinds of nops and is unusual enough to be noticed.
|
|
+// The leading oris 0,0,0 gets patched to a b .+32 when disabled.
|
|
+CodeOffset
|
|
+MacroAssemblerPPC64Compat::toggledJump(Label* label)
|
|
+{
|
|
+ ADBlock();
|
|
+ CodeOffset ret(nextOffset().getOffset());
|
|
+ as_oris(r0, r0, 0);
|
|
+ ma_b(label);
|
|
+ // The b() may emit a varying number of instructions, so add enough nops to pad.
|
|
+ while((nextOffset().getOffset() - ret.offset()) < ToggledCallSize(nullptr)) {
|
|
+ as_nop();
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+CodeOffset
|
|
+MacroAssemblerPPC64Compat::toggledCall(JitCode* target, bool enabled)
|
|
+{
|
|
+ ADBlock();
|
|
+ BufferOffset bo = nextOffset();
|
|
+ CodeOffset offset(bo.getOffset());
|
|
+ if (enabled) {
|
|
+ as_oris(r0, r0, 0);
|
|
+ } else {
|
|
+ as_b(32, RelativeBranch, DontLinkB);
|
|
+ }
|
|
+ addPendingJump(nextOffset(), ImmPtr(target->raw()), RelocationKind::JITCODE);
|
|
+ ma_liPatchable(SecondScratchReg, ImmPtr(target->raw()));
|
|
+ xs_mtctr(SecondScratchReg);
|
|
+ as_bctr(LinkB);
|
|
+ MOZ_ASSERT_IF(!oom(), nextOffset().getOffset() - offset.offset() == ToggledCallSize(nullptr));
|
|
+ return offset;
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::profilerEnterFrame(Register framePtr, Register scratch)
|
|
+{
|
|
+ asMasm().loadJSContext(scratch);
|
|
+ loadPtr(Address(scratch, offsetof(JSContext, profilingActivation_)), scratch);
|
|
+ storePtr(framePtr, Address(scratch, JitActivation::offsetOfLastProfilingFrame()));
|
|
+ storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite()));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::profilerExitFrame()
|
|
+{
|
|
+ jump(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail());
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::subFromStackPtr(Imm32 imm32)
|
|
+{
|
|
+ if (imm32.value)
|
|
+ asMasm().subPtr(imm32, StackPointer);
|
|
+}
|
|
+
|
|
+//{{{ check_macroassembler_style
|
|
+// ===============================================================
|
|
+// Stack manipulation functions.
|
|
+
|
|
+// XXX: Check usage of this routine in Ion and see what assumes LR is a GPR. If so, then
|
|
+// maybe we need to find a way to abstract away SPRs vs GPRs after all.
|
|
+void
|
|
+MacroAssembler::PushRegsInMask(LiveRegisterSet set)
|
|
+{
|
|
+ int32_t diff = PushRegsInMaskSizeInBytes(set);
|
|
+ const int32_t reserved = diff;
|
|
+
|
|
+ reserveStack(reserved);
|
|
+ for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) {
|
|
+ diff -= sizeof(intptr_t);
|
|
+ storePtr(*iter, Address(StackPointer, diff));
|
|
+ }
|
|
+ for (FloatRegisterBackwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); ++iter) {
|
|
+ diff -= sizeof(double);
|
|
+ storeDouble(*iter, Address(StackPointer, diff));
|
|
+ }
|
|
+ MOZ_ASSERT(diff == 0);
|
|
+}
|
|
+size_t
|
|
+MacroAssembler::PushRegsInMaskSizeInBytes(LiveRegisterSet set)
|
|
+{
|
|
+ return (set.gprs().size() * sizeof(intptr_t) + set.fpus().getPushSizeInBytes());
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore)
|
|
+{
|
|
+ int32_t diff = set.gprs().size() * sizeof(intptr_t) +
|
|
+ set.fpus().getPushSizeInBytes();
|
|
+ const int32_t reserved = diff;
|
|
+
|
|
+ for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) {
|
|
+ diff -= sizeof(intptr_t);
|
|
+ if (!ignore.has(*iter))
|
|
+ loadPtr(Address(StackPointer, diff), *iter);
|
|
+ }
|
|
+ for (FloatRegisterBackwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); ++iter) {
|
|
+ diff -= sizeof(double);
|
|
+ if (!ignore.has(*iter))
|
|
+ loadDouble(Address(StackPointer, diff), *iter);
|
|
+ }
|
|
+ MOZ_ASSERT(diff == 0);
|
|
+ freeStack(reserved);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::storeRegsInMask(LiveRegisterSet set, Address dest, Register)
|
|
+{
|
|
+ FloatRegisterSet fpuSet(set.fpus().reduceSetForPush());
|
|
+ unsigned numFpu = fpuSet.size();
|
|
+ int32_t diffF = fpuSet.getPushSizeInBytes();
|
|
+ int32_t diffG = set.gprs().size() * sizeof(intptr_t);
|
|
+
|
|
+ MOZ_ASSERT(dest.offset >= diffG + diffF);
|
|
+
|
|
+ for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) {
|
|
+ diffG -= sizeof(intptr_t);
|
|
+ dest.offset -= sizeof(intptr_t);
|
|
+ storePtr(*iter, dest);
|
|
+ }
|
|
+ MOZ_ASSERT(diffG == 0);
|
|
+
|
|
+ for (FloatRegisterBackwardIterator iter(fpuSet); iter.more(); ++iter) {
|
|
+ FloatRegister reg = *iter;
|
|
+ diffF -= reg.size();
|
|
+ numFpu -= 1;
|
|
+ dest.offset -= reg.size();
|
|
+ if (reg.isDouble())
|
|
+ storeDouble(reg, dest);
|
|
+ else if (reg.isSingle())
|
|
+ storeFloat32(reg, dest);
|
|
+ else
|
|
+ MOZ_CRASH("Unknown register type.");
|
|
+ }
|
|
+ MOZ_ASSERT(numFpu == 0);
|
|
+ diffF -= diffF % sizeof(uintptr_t);
|
|
+ MOZ_ASSERT(diffF == 0);
|
|
+}
|
|
+// ===============================================================
|
|
+// ABI function calls.
|
|
+
|
|
+void
|
|
+MacroAssembler::setupUnalignedABICall(Register scratch)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(!IsCompilingWasm(), "wasm should only use aligned ABI calls"); // XXX?? arm doesn't do this
|
|
+ MOZ_ASSERT(scratch != ScratchRegister);
|
|
+ MOZ_ASSERT(scratch != SecondScratchReg);
|
|
+
|
|
+ setupNativeABICall();
|
|
+ dynamicAlignment_ = true;
|
|
+
|
|
+ // Even though this is ostensibly an ABI-compliant call, save both LR
|
|
+ // and SP; Baseline ICs assume that LR isn't modified.
|
|
+ xs_mflr(SecondScratchReg); // ma_and may clobber r0.
|
|
+ ma_move(scratch, StackPointer);
|
|
+ asMasm().subPtr(Imm32(sizeof(uintptr_t)*2), StackPointer);
|
|
+ ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1)));
|
|
+ as_std(SecondScratchReg, StackPointer, sizeof(uintptr_t));
|
|
+ storePtr(scratch, Address(StackPointer, 0));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromWasm)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(inCall_);
|
|
+ uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar();
|
|
+
|
|
+ if (dynamicAlignment_) {
|
|
+ stackForCall += ComputeByteAlignment(stackForCall, ABIStackAlignment);
|
|
+ } else {
|
|
+ uint32_t alignmentAtPrologue = callFromWasm ? sizeof(wasm::Frame) : 0;
|
|
+ stackForCall += ComputeByteAlignment(stackForCall + framePushed() + alignmentAtPrologue,
|
|
+ ABIStackAlignment);
|
|
+ }
|
|
+
|
|
+ // The callee save area must minimally include room for the SP back chain
|
|
+ // pointer plus CR and LR. For 16-byte alignment we'll just ask for 32
|
|
+ // bytes. This guarantees nothing we're trying to keep on the stack will
|
|
+ // get overwritten.
|
|
+ stackForCall += 32;
|
|
+
|
|
+ *stackAdjust = stackForCall;
|
|
+ reserveStack(stackForCall);
|
|
+
|
|
+ // Position all arguments.
|
|
+ {
|
|
+ enoughMemory_ &= moveResolver_.resolve();
|
|
+ if (!enoughMemory_)
|
|
+ return;
|
|
+
|
|
+ MoveEmitter emitter(*this);
|
|
+ emitter.emit(moveResolver_);
|
|
+ emitter.finish();
|
|
+ }
|
|
+
|
|
+ assertStackAlignment(ABIStackAlignment);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::callWithABIPost(uint32_t stackAdjust, ABIType result, bool callFromWasm)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ if (dynamicAlignment_) {
|
|
+ // Restore LR and SP (as stored in setupUnalignedABICall).
|
|
+ as_ld(ScratchRegister, StackPointer, stackAdjust+sizeof(uintptr_t));
|
|
+ xs_mtlr(ScratchRegister);
|
|
+ loadPtr(Address(StackPointer, stackAdjust), StackPointer);
|
|
+ // Use adjustFrame instead of freeStack because we already restored SP.
|
|
+ adjustFrame(-stackAdjust);
|
|
+ } else {
|
|
+ // LR isn't stored in this instance.
|
|
+ freeStack(stackAdjust);
|
|
+ }
|
|
+
|
|
+#ifdef DEBUG
|
|
+ MOZ_ASSERT(inCall_);
|
|
+ inCall_ = false;
|
|
+#endif
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::callWithABINoProfiler(Register fun, ABIType result)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ uint32_t stackAdjust;
|
|
+ callWithABIPre(&stackAdjust);
|
|
+ call(fun);
|
|
+ callWithABIPost(stackAdjust, result);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::callWithABINoProfiler(const Address& fun, ABIType result)
|
|
+{
|
|
+ uint32_t stackAdjust;
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(fun.base != SecondScratchReg);
|
|
+
|
|
+ // This requires a bit of fancy dancing: the address base could be one
|
|
+ // of the argregs and the MoveEmitter might clobber it positioning the
|
|
+ // arguments. To avoid this problem we'll load CTR early, a great
|
|
+ // example of turning necessity into virtue since it's faster too.
|
|
+ MOZ_ASSERT(Imm16::IsInSignedRange(fun.offset));
|
|
+ loadPtr(Address(fun.base, fun.offset), SecondScratchReg); // must use r12
|
|
+ xs_mtctr(SecondScratchReg);
|
|
+
|
|
+ // It's now safe to call the MoveEmitter.
|
|
+ callWithABIPre(&stackAdjust);
|
|
+ as_bctr(LinkB);
|
|
+ callWithABIPost(stackAdjust, result);
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Move
|
|
+
|
|
+void
|
|
+MacroAssembler::moveValue(const TypedOrValueRegister& src, const ValueOperand& dest)
|
|
+{
|
|
+ if (src.hasValue()) {
|
|
+ moveValue(src.valueReg(), dest);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ MIRType type = src.type();
|
|
+ AnyRegister reg = src.typedReg();
|
|
+
|
|
+ if (!IsFloatingPointType(type)) {
|
|
+ boxNonDouble(ValueTypeFromMIRType(type), reg.gpr(), dest);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ FloatRegister scratch = ScratchDoubleReg;
|
|
+ FloatRegister freg = reg.fpu();
|
|
+ if (type == MIRType::Float32) {
|
|
+ convertFloat32ToDouble(freg, scratch);
|
|
+ freg = scratch;
|
|
+ }
|
|
+ boxDouble(freg, dest, scratch);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::moveValue(const ValueOperand& src, const ValueOperand& dest)
|
|
+{
|
|
+ if (src == dest)
|
|
+ return;
|
|
+ movePtr(src.valueReg(), dest.valueReg());
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::moveValue(const Value& src, const ValueOperand& dest)
|
|
+{
|
|
+ if(!src.isGCThing()) {
|
|
+ ma_li(dest.valueReg(), ImmWord(src.asRawBits()));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ writeDataRelocation(src);
|
|
+ movWithPatch(ImmWord(src.asRawBits()), dest.valueReg());
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Branch functions
|
|
+
|
|
+// assumed by unboxGCThingForGCBarrier
|
|
+static_assert(JS::detail::ValueGCThingPayloadMask == 0x0000'7FFF'FFFF'FFFF);
|
|
+
|
|
+void
|
|
+MacroAssembler::branchValueIsNurseryCell(Condition cond, const Address& address, Register temp,
|
|
+ Label* label)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(temp != InvalidReg);
|
|
+ loadValue(address, ValueOperand(temp));
|
|
+ branchValueIsNurseryCell(cond, ValueOperand(temp), InvalidReg, label);
|
|
+}
|
|
+void
|
|
+MacroAssembler::branchValueIsNurseryCell(Condition cond, ValueOperand value, Register temp,
|
|
+ Label* label)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
|
|
+ Label done;
|
|
+ branchTestGCThing(Assembler::NotEqual, value,
|
|
+ cond == Assembler::Equal ? &done : label);
|
|
+
|
|
+ getGCThingValueChunk(value, SecondScratchReg);
|
|
+ loadPtr(Address(SecondScratchReg, gc::ChunkStoreBufferOffset), ScratchRegister);
|
|
+ branchPtr(InvertCondition(cond), ScratchRegister, ImmWord(0), label);
|
|
+
|
|
+ bind(&done);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs,
|
|
+ const Value& rhs, Label* label)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
|
+ ScratchRegisterScope scratch(*this);
|
|
+ MOZ_ASSERT(lhs.valueReg() != scratch);
|
|
+ moveValue(rhs, ValueOperand(scratch));
|
|
+ ma_bc(lhs.valueReg(), scratch, label, cond);
|
|
+}
|
|
+
|
|
+// ========================================================================
|
|
+// Memory access primitives.
|
|
+template <typename T>
|
|
+void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value,
|
|
+ MIRType valueType, const T& dest) {
|
|
+ MOZ_ASSERT(valueType < MIRType::Value);
|
|
+
|
|
+ if (valueType == MIRType::Double) {
|
|
+ boxDouble(value.reg().typedReg().fpu(), dest);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (value.constant()) {
|
|
+ storeValue(value.value(), dest);
|
|
+ } else {
|
|
+ storeValue(ValueTypeFromMIRType(valueType), value.reg().typedReg().gpr(),
|
|
+ dest);
|
|
+ }
|
|
+}
|
|
+
|
|
+template void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value,
|
|
+ MIRType valueType,
|
|
+ const Address& dest);
|
|
+template void MacroAssembler::storeUnboxedValue(
|
|
+ const ConstantOrRegister& value, MIRType valueType,
|
|
+ const BaseObjectElementIndex& dest);
|
|
+
|
|
+void
|
|
+MacroAssembler::PushBoxed(FloatRegister reg)
|
|
+{
|
|
+ MOZ_ASSERT(reg.isDouble());
|
|
+ subFromStackPtr(Imm32(sizeof(double)));
|
|
+ boxDouble(reg, Address(getStackPointer(), 0));
|
|
+ adjustFrame(sizeof(double));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_spectre_isel(Condition cond, Register lhs, Register rhs) {
|
|
+ if (JitOptions.spectreIndexMasking) {
|
|
+ if (cond == NotEqual) {
|
|
+ as_isel(lhs, lhs, rhs, Assembler::Equal);
|
|
+ } else if (cond == Equal) {
|
|
+ as_isel(lhs, rhs, lhs, Assembler::Equal);
|
|
+ } else if (cond == LessThan || cond == Below) {
|
|
+ as_isel(lhs, rhs, lhs, Assembler::LessThan);
|
|
+ } else if (cond == GreaterThanOrEqual || cond == AboveOrEqual) {
|
|
+ as_isel(lhs, lhs, rhs, Assembler::LessThan);
|
|
+ } else if (cond == GreaterThan || cond == Above) {
|
|
+ as_isel(lhs, rhs, lhs, Assembler::GreaterThan);
|
|
+ } else if (cond == LessThanOrEqual || cond == BelowOrEqual) {
|
|
+ as_isel(lhs, lhs, rhs, Assembler::GreaterThan);
|
|
+ } else {
|
|
+ MOZ_CRASH("unhandled condition");
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmBoundsCheck32(Condition cond, Register index,
|
|
+ Register boundsCheckLimit, Label* label)
|
|
+{
|
|
+ ADBlock();
|
|
+ // We can safely clean the upper word out because this must be 32-bit.
|
|
+ as_rldicl(index, index, 0, 32); // "clrldi"
|
|
+ ma_bc32(index, boundsCheckLimit, label, cond);
|
|
+ ma_spectre_isel(cond, index, boundsCheckLimit);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmBoundsCheck32(Condition cond, Register index,
|
|
+ Address boundsCheckLimit, Label* label)
|
|
+{
|
|
+ ADBlock();
|
|
+ SecondScratchRegisterScope scratch2(*this);
|
|
+ // We can safely clean the upper word out because this must be 32-bit.
|
|
+ as_rldicl(index, index, 0, 32); // "clrldi"
|
|
+ load32ZeroExtend(boundsCheckLimit, SecondScratchReg);
|
|
+ ma_bc32(index, SecondScratchReg, label, cond);
|
|
+ ma_spectre_isel(cond, index, SecondScratchReg);
|
|
+}
|
|
+
|
|
+void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index,
|
|
+ Register64 boundsCheckLimit,
|
|
+ Label* label) {
|
|
+ ADBlock();
|
|
+ branchPtr(cond, index.reg, boundsCheckLimit.reg, label);
|
|
+ ma_spectre_isel(cond, index.reg, boundsCheckLimit.reg);
|
|
+}
|
|
+
|
|
+void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index,
|
|
+ Address boundsCheckLimit, Label* label) {
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(index.reg != SecondScratchReg);
|
|
+
|
|
+ Register64 limit(SecondScratchReg);
|
|
+ loadPtr(boundsCheckLimit, SecondScratchReg);
|
|
+ wasmBoundsCheck64(cond, index, limit, label);
|
|
+}
|
|
+
|
|
+void MacroAssembler::widenInt32(Register r) {
|
|
+ ADBlock();
|
|
+
|
|
+ move32To64SignExtend(r, Register64(r));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, bool isSaturating,
|
|
+ Label* oolEntry)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ // We only care if the conversion is invalid, not if it's inexact.
|
|
+ // Negative zero is treated like positive zero.
|
|
+ // Whack VXCVI.
|
|
+ as_mtfsb0(23);
|
|
+ as_fctiwuz(ScratchDoubleReg, input);
|
|
+
|
|
+ if (isSaturating) {
|
|
+ // There is no need to call the out-of-line routine: fctiwuz saturates
|
|
+ // NaN to 0 and all other values to their extents, so we're done. We
|
|
+ // ignore any bits that are set in the FPSCR.
|
|
+ } else {
|
|
+ // VXCVI is a failure (over/underflow, NaN, etc.)
|
|
+ as_mcrfs(cr0, 5); // reserved - VXSOFT - VXSQRT - VXCVI -> CR0[...SO]
|
|
+ // OutOfLineTruncateCheckF32/F64ToU32 -> outOfLineWasmTruncateToInt32Check
|
|
+ ma_bc(Assembler::SOBit, oolEntry);
|
|
+ }
|
|
+
|
|
+ moveFromDouble(ScratchDoubleReg, ScratchRegister);
|
|
+ // Don't sign extend.
|
|
+ as_rldicl(output, ScratchRegister, 0, 32); // "clrldi"
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, bool isSaturating,
|
|
+ Label* oolEntry)
|
|
+{
|
|
+ wasmTruncateDoubleToUInt32(input, output, isSaturating, oolEntry);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmLoadI64(const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr,
|
|
+ Register ptrScratch, Register64 output)
|
|
+{
|
|
+ wasmLoadI64Impl(access, memoryBase, ptr, ptrScratch, output, InvalidReg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmUnalignedLoadI64(const wasm::MemoryAccessDesc& access, Register memoryBase,
|
|
+ Register ptr, Register ptrScratch, Register64 output,
|
|
+ Register tmp)
|
|
+{
|
|
+ wasmLoadI64Impl(access, memoryBase, ptr, ptrScratch, output, tmp);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmStoreI64(const wasm::MemoryAccessDesc& access, Register64 value,
|
|
+ Register memoryBase, Register ptr, Register ptrScratch)
|
|
+{
|
|
+ wasmStoreI64Impl(access, value, memoryBase, ptr, ptrScratch, InvalidReg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmUnalignedStoreI64(const wasm::MemoryAccessDesc& access, Register64 value,
|
|
+ Register memoryBase, Register ptr, Register ptrScratch,
|
|
+ Register tmp)
|
|
+{
|
|
+ wasmStoreI64Impl(access, value, memoryBase, ptr, ptrScratch, tmp);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmTruncateDoubleToInt64(FloatRegister input, Register64 output_,
|
|
+ bool isSaturating, Label* oolEntry,
|
|
+ Label* oolRejoin, FloatRegister tempDouble)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(tempDouble.isInvalid());
|
|
+ Register output = output_.reg;
|
|
+
|
|
+ // We only care if the conversion is invalid, not if it's inexact.
|
|
+ // Negative zero is treated like positive zero.
|
|
+ // Whack VXCVI.
|
|
+ as_mtfsb0(23);
|
|
+ as_fctidz(ScratchDoubleReg, input);
|
|
+ // VXCVI is a failure (over/underflow, NaN, etc.)
|
|
+ as_mcrfs(cr0, 5); // reserved - VXSOFT - VXSQRT - VXCVI -> CR0[...SO]
|
|
+ // OutOfLineTruncateCheckF32OrF64ToI64 -> outOfLineWasmTruncateToInt64Check
|
|
+ ma_bc(Assembler::SOBit, oolEntry);
|
|
+
|
|
+ moveFromDouble(ScratchDoubleReg, output);
|
|
+ bind(oolRejoin);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmTruncateDoubleToUInt64(FloatRegister input, Register64 output_,
|
|
+ bool isSaturating, Label* oolEntry,
|
|
+ Label* oolRejoin, FloatRegister tempDouble)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(tempDouble.isInvalid());
|
|
+ Register output = output_.reg;
|
|
+
|
|
+ // We only care if the conversion is invalid, not if it's inexact.
|
|
+ // Negative zero is treated like positive zero.
|
|
+ // Whack VXCVI.
|
|
+ as_mtfsb0(23);
|
|
+ as_fctiduz(ScratchDoubleReg, input);
|
|
+ if (isSaturating) {
|
|
+ // There is no need to call the out-of-line routine: fctiduz saturates
|
|
+ // NaN to 0 and all other values to their extents, so we're done. We
|
|
+ // ignore any bits that are set in the FPSCR.
|
|
+ } else {
|
|
+ // VXCVI is a failure (over/underflow, NaN, etc.)
|
|
+ as_mcrfs(cr0, 5); // reserved - VXSOFT - VXSQRT - VXCVI -> CR0[...SO]
|
|
+ // OutOfLineTruncateCheckF32OrF64ToI64 -> outOfLineWasmTruncateToInt64Check
|
|
+ ma_bc(Assembler::SOBit, oolEntry);
|
|
+ }
|
|
+
|
|
+ moveFromDouble(ScratchDoubleReg, output);
|
|
+ bind(oolRejoin);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmTruncateFloat32ToInt64(FloatRegister input, Register64 output,
|
|
+ bool isSaturating, Label* oolEntry,
|
|
+ Label* oolRejoin, FloatRegister tempFloat)
|
|
+{
|
|
+ wasmTruncateDoubleToInt64(input, output, isSaturating, oolEntry,
|
|
+ oolRejoin, tempFloat);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmTruncateFloat32ToUInt64(FloatRegister input, Register64 output,
|
|
+ bool isSaturating, Label* oolEntry,
|
|
+ Label* oolRejoin, FloatRegister tempFloat)
|
|
+{
|
|
+ wasmTruncateDoubleToUInt64(input, output, isSaturating, oolEntry,
|
|
+ oolRejoin, tempFloat);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::wasmLoadI64Impl(const wasm::MemoryAccessDesc& access,
|
|
+ Register memoryBase, Register ptr, Register ptrScratch,
|
|
+ Register64 output, Register tmp)
|
|
+{
|
|
+ access.assertOffsetInGuardPages();
|
|
+ uint32_t offset = access.offset();
|
|
+ MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
|
|
+
|
|
+ // Maybe add the offset.
|
|
+ if (offset) {
|
|
+ asMasm().addPtr(Imm32(offset), ptrScratch);
|
|
+ ptr = ptrScratch;
|
|
+ }
|
|
+
|
|
+ unsigned byteSize = access.byteSize();
|
|
+ bool isSigned;
|
|
+
|
|
+ switch (access.type()) {
|
|
+ case Scalar::Int8: isSigned = true; break;
|
|
+ case Scalar::Uint8: isSigned = false; break;
|
|
+ case Scalar::Int16: isSigned = true; break;
|
|
+ case Scalar::Uint16: isSigned = false; break;
|
|
+ case Scalar::Int32: isSigned = true; break;
|
|
+ case Scalar::Uint32: isSigned = false; break;
|
|
+ case Scalar::Int64: isSigned = true; break;
|
|
+ default: MOZ_CRASH("unexpected array type");
|
|
+ }
|
|
+
|
|
+ // threadsafe
|
|
+ BaseIndex address(memoryBase, ptr, TimesOne);
|
|
+ asMasm().memoryBarrierBefore(access.sync());
|
|
+ asMasm().append(access, js::wasm::TrapMachineInsnForLoad(access.byteSize()),
|
|
+asMasm().ma_load(output.reg, address, static_cast<LoadStoreSize>(8 * byteSize),
|
|
+ isSigned ? SignExtend : ZeroExtend));
|
|
+ asMasm().memoryBarrierAfter(access.sync());
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64Compat::wasmStoreI64Impl(const wasm::MemoryAccessDesc& access, Register64 value,
|
|
+ Register memoryBase, Register ptr, Register ptrScratch,
|
|
+ Register tmp)
|
|
+{
|
|
+ access.assertOffsetInGuardPages();
|
|
+ uint32_t offset = access.offset();
|
|
+ MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
|
|
+
|
|
+ // Maybe add the offset.
|
|
+ if (offset) {
|
|
+ asMasm().addPtr(Imm32(offset), ptrScratch);
|
|
+ ptr = ptrScratch;
|
|
+ }
|
|
+
|
|
+ unsigned byteSize = access.byteSize();
|
|
+ bool isSigned; // not used yet
|
|
+ switch (access.type()) {
|
|
+ case Scalar::Int8: isSigned = true; break;
|
|
+ case Scalar::Uint8: isSigned = false; break;
|
|
+ case Scalar::Int16: isSigned = true; break;
|
|
+ case Scalar::Uint16: isSigned = false; break;
|
|
+ case Scalar::Int32: isSigned = true; break;
|
|
+ case Scalar::Uint32: isSigned = false; break;
|
|
+ case Scalar::Int64: isSigned = true; break;
|
|
+ default: MOZ_CRASH("unexpected array type");
|
|
+ }
|
|
+
|
|
+ BaseIndex address(memoryBase, ptr, TimesOne);
|
|
+
|
|
+ // threadsafe
|
|
+ asMasm().memoryBarrierBefore(access.sync());
|
|
+ asMasm().append(access, js::wasm::TrapMachineInsnForLoad(access.byteSize()),
|
|
+asMasm().ma_store(value.reg, address, static_cast<LoadStoreSize>(8 * byteSize)));
|
|
+ asMasm().memoryBarrierAfter(access.sync());
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+static void
|
|
+CompareExchange64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, Synchronization sync, const T& mem,
|
|
+ Register64 expect, Register64 replace, Register64 output)
|
|
+{
|
|
+ // use Acquired Lock semantics, ISA v3.0B reference p915
|
|
+ masm.computeEffectiveAddress(mem, SecondScratchReg);
|
|
+
|
|
+ Label tryAgain;
|
|
+ Label exit;
|
|
+
|
|
+ masm.memoryBarrierBefore(sync);
|
|
+ masm.bind(&tryAgain);
|
|
+
|
|
+ if (access) masm.append(*access, wasm::TrapMachineInsn::Atomic,
|
|
+ FaultingCodeOffset(masm.currentOffset()));
|
|
+ // 'r0' for 'ra' indicates hard 0, not GPR r0
|
|
+ masm.as_ldarx(output.reg, r0, SecondScratchReg);
|
|
+ masm.ma_bc(output.reg, expect.reg, &exit, Assembler::NotEqual, ShortJump);
|
|
+ masm.movePtr(replace.reg, ScratchRegister);
|
|
+ /* if (access) masm.append(*access, wasm::TrapMachineInsn::Atomic,
|
|
+ FaultingCodeOffset(masm.currentOffset())); */
|
|
+ masm.as_stdcx(ScratchRegister, r0, SecondScratchReg);
|
|
+ masm.ma_bc(Assembler::NotEqual, &tryAgain, ShortJump);
|
|
+
|
|
+ masm.memoryBarrierAfter(sync);
|
|
+ //masm.as_isync();
|
|
+ masm.bind(&exit);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::compareExchange64(Synchronization sync, const Address& mem,
|
|
+ Register64 expect, Register64 replace, Register64 output)
|
|
+{
|
|
+ CompareExchange64(*this, nullptr, sync, mem, expect, replace, output);
|
|
+}
|
|
+
|
|
+void MacroAssembler::compareExchange64(Synchronization sync,
|
|
+ const BaseIndex& mem, Register64 expect,
|
|
+ Register64 replace, Register64 output) {
|
|
+ CompareExchange64(*this, nullptr, sync, mem, expect, replace, output);
|
|
+}
|
|
+
|
|
+template <typename T>
|
|
+static void
|
|
+AtomicExchange64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, Synchronization sync, const T& mem,
|
|
+ Register64 src, Register64 output)
|
|
+{
|
|
+ masm.computeEffectiveAddress(mem, SecondScratchReg);
|
|
+
|
|
+ Label tryAgain;
|
|
+
|
|
+ masm.memoryBarrierBefore(sync);
|
|
+
|
|
+ masm.bind(&tryAgain);
|
|
+
|
|
+ if (access) masm.append(*access, js::wasm::TrapMachineInsn::Load64,
|
|
+ FaultingCodeOffset(masm.currentOffset()));
|
|
+ // 'r0' for 'ra' indicates hard 0, not GPR r0
|
|
+ masm.as_ldarx(output.reg, r0, SecondScratchReg);
|
|
+ /* if (access) masm.append(*access, js::wasm::TrapMachineInsn::Load64,
|
|
+ FaultingCodeOffset(masm.currentOffset())); */
|
|
+ masm.as_stdcx(src.reg, r0, SecondScratchReg);
|
|
+ masm.ma_bc(cr0, Assembler::NotEqual, &tryAgain, ShortJump);
|
|
+
|
|
+ //masm.as_isync();
|
|
+ masm.memoryBarrierAfter(sync);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::atomicExchange64(Synchronization sync, const Address& mem, Register64 src,
|
|
+ Register64 output)
|
|
+{
|
|
+ AtomicExchange64(*this, nullptr, sync, mem, src, output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::atomicExchange64(Synchronization sync, const BaseIndex& mem, Register64 src,
|
|
+ Register64 output)
|
|
+{
|
|
+ AtomicExchange64(*this, nullptr, sync, mem, src, output);
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+static void
|
|
+AtomicFetchOp64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, Synchronization sync, AtomicOp op, Register64 value,
|
|
+ const T& mem, Register64 temp, Register64 output)
|
|
+{
|
|
+ masm.computeEffectiveAddress(mem, SecondScratchReg);
|
|
+
|
|
+ Label tryAgain;
|
|
+
|
|
+ masm.memoryBarrierBefore(sync);
|
|
+
|
|
+ masm.bind(&tryAgain);
|
|
+
|
|
+ if (access) masm.append(*access, js::wasm::TrapMachineInsn::Load64,
|
|
+ FaultingCodeOffset(masm.currentOffset()));
|
|
+ // 'r0' for 'ra' indicates hard 0, not GPR r0
|
|
+ masm.as_ldarx(output.reg, r0, SecondScratchReg);
|
|
+
|
|
+ switch(op) {
|
|
+ case AtomicOp::Add:
|
|
+ masm.as_add(temp.reg, output.reg, value.reg);
|
|
+ break;
|
|
+ case AtomicOp::Sub:
|
|
+ masm.as_subf(temp.reg, value.reg, output.reg);
|
|
+ break;
|
|
+ case AtomicOp::And:
|
|
+ masm.as_and(temp.reg, output.reg, value.reg);
|
|
+ break;
|
|
+ case AtomicOp::Or:
|
|
+ masm.as_or(temp.reg, output.reg, value.reg);
|
|
+ break;
|
|
+ case AtomicOp::Xor:
|
|
+ masm.as_xor(temp.reg, output.reg, value.reg);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH();
|
|
+ }
|
|
+
|
|
+ /* if (access) masm.append(*access, masm.size()); */
|
|
+ masm.as_stdcx(temp.reg, r0, SecondScratchReg);
|
|
+ masm.ma_bc(Assembler::NotEqual, &tryAgain, ShortJump);
|
|
+
|
|
+ //masm.as_isync();
|
|
+ masm.memoryBarrierAfter(sync);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op, Register64 value,
|
|
+ const Address& mem, Register64 temp, Register64 output)
|
|
+{
|
|
+ AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output);
|
|
+}
|
|
+
|
|
+void MacroAssembler::atomicFetchOp64(Synchronization sync, AtomicOp op,
|
|
+ Register64 value, const BaseIndex& mem,
|
|
+ Register64 temp, Register64 output) {
|
|
+ AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output);
|
|
+}
|
|
+
|
|
+void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op,
|
|
+ Register64 value, const Address& mem,
|
|
+ Register64 temp) {
|
|
+ AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp);
|
|
+}
|
|
+
|
|
+void MacroAssembler::atomicEffectOp64(Synchronization sync, AtomicOp op,
|
|
+ Register64 value, const BaseIndex& mem,
|
|
+ Register64 temp) {
|
|
+ AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp);
|
|
+}
|
|
+
|
|
+void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access,
|
|
+ const Address& mem,
|
|
+ Register64 expect,
|
|
+ Register64 replace,
|
|
+ Register64 output) {
|
|
+ CompareExchange64(*this, &access, access.sync(), mem, expect, replace,
|
|
+ output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access,
|
|
+ const BaseIndex& mem,
|
|
+ Register64 expect,
|
|
+ Register64 replace,
|
|
+ Register64 output) {
|
|
+ CompareExchange64(*this, &access, access.sync(), mem, expect, replace, output);
|
|
+}
|
|
+
|
|
+void MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access,
|
|
+ const Address& mem, Register64 src,
|
|
+ Register64 output) {
|
|
+ //WasmAtomicExchange64(*this, access, mem, src, output);
|
|
+ AtomicExchange64(*this, &access, access.sync(), mem, src, output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access,
|
|
+ const BaseIndex& mem,
|
|
+ Register64 value, Register64 output)
|
|
+{
|
|
+ AtomicExchange64(*this, &access, access.sync(), mem, value, output);
|
|
+}
|
|
+
|
|
+void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access,
|
|
+ AtomicOp op, Register64 value,
|
|
+ const Address& mem, Register64 temp,
|
|
+ Register64 output) {
|
|
+ AtomicFetchOp64(*this, &access, access.sync(), op, value, mem, temp, output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access,
|
|
+ AtomicOp op, Register64 value,
|
|
+ const BaseIndex& mem, Register64 temp,
|
|
+ Register64 output)
|
|
+{
|
|
+ AtomicFetchOp64(*this, &access, access.sync(), op, value, mem, temp, output);
|
|
+}
|
|
+
|
|
+
|
|
+// ========================================================================
|
|
+// Convert floating point.
|
|
+
|
|
+void
|
|
+MacroAssembler::convertInt64ToDouble(Register64 src, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ moveToDouble(src.reg, dest);
|
|
+ as_fcfid(dest, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::convertIntPtrToDouble(Register src, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ moveToDouble(src, dest);
|
|
+ as_fcfid(dest, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::convertInt64ToFloat32(Register64 src, FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ moveToDouble(src.reg, dest);
|
|
+ // Enforce rounding mode 0b00 (round-to-nearest ties-to-even).
|
|
+ as_mtfsfi(7, 0);
|
|
+ as_fcfids(dest, dest);
|
|
+}
|
|
+
|
|
+bool
|
|
+MacroAssembler::convertUInt64ToDoubleNeedsTemp()
|
|
+{
|
|
+ // We're not like those other inferior architectures.
|
|
+ return false;
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(temp == Register::Invalid());
|
|
+
|
|
+ MacroAssemblerSpecific::convertUInt64ToDouble(src.reg, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::convertUInt64ToFloat32(Register64 src, FloatRegister dest, Register temp)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(temp == Register::Invalid());
|
|
+
|
|
+ moveToDouble(src.reg, dest);
|
|
+ // Enforce rounding mode 0b00 (round-to-nearest ties-to-even).
|
|
+ as_mtfsfi(7, 0);
|
|
+ as_fcfidus(dest, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::copySignDouble(FloatRegister lhs, FloatRegister rhs, FloatRegister dest)
|
|
+{
|
|
+ // From inspection, 'rhs' is the sign and 'lhs' is the value. Opposite of
|
|
+ // what the instruction takes.
|
|
+ as_fcpsgn(dest, rhs, lhs);
|
|
+}
|
|
+
|
|
+void MacroAssembler::shiftIndex32AndAdd(Register indexTemp32, int shift,
|
|
+ Register pointer) {
|
|
+ if (IsShiftInScaleRange(shift)) {
|
|
+ computeEffectiveAddress(
|
|
+ BaseIndex(pointer, indexTemp32, ShiftToScale(shift)), pointer);
|
|
+ return;
|
|
+ }
|
|
+ lshift32(Imm32(shift), indexTemp32);
|
|
+ addPtr(indexTemp32, pointer);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::truncFloat32ToInt32(FloatRegister src, Register dest, Label* fail)
|
|
+{
|
|
+ return truncDoubleToInt32(src, dest, fail);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::truncDoubleToInt32(FloatRegister src, Register dest, Label* fail)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(dest != ScratchRegister);
|
|
+ MOZ_ASSERT(src != ScratchDoubleReg);
|
|
+
|
|
+ // We only care if the conversion is invalid, not if it's inexact.
|
|
+ // However, JavaScript defines Math.trunc(-0) == -0, so we need a check.
|
|
+ // Whack VXCVI.
|
|
+ as_mtfsb0(23);
|
|
+ as_fctiwz(ScratchDoubleReg, src);
|
|
+ // VXCVI is a failure (over/underflow, NaN, etc.)
|
|
+ as_mcrfs(cr1, 5); // reserved - VXSOFT - VXSQRT - VXCVI -> CR1[...SO]
|
|
+ moveFromDouble(ScratchDoubleReg, dest);
|
|
+ as_srawi(dest, dest, 0); // clear upper word and sign extend
|
|
+ as_cmpdi(cr7, dest, 0); // check for zero
|
|
+ moveFromDouble(src, ScratchRegister);
|
|
+ as_cmpdi(ScratchRegister, 0); // check sign bit of original float
|
|
+ as_crand(0, 0, 30); // CR0[LT] &= CR7[EQ] (only bail if - and 0)
|
|
+ as_cror(0, 0, 7); // Bond, James Bond: CR0[LT] |= CR1[SO]
|
|
+ ma_bc(Assembler::LessThan, fail);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::nearbyIntDouble(RoundingMode mode, FloatRegister src,
|
|
+ FloatRegister dest)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ switch (mode) {
|
|
+ case RoundingMode::Up:
|
|
+ as_frip(dest, src);
|
|
+ break;
|
|
+ case RoundingMode::Down:
|
|
+ as_frim(dest, src);
|
|
+ break;
|
|
+ case RoundingMode::NearestTiesToEven:
|
|
+ // This is actually IEEE nearest ties-to-even. |frin| doesn't
|
|
+ // do this behaviour as specified by JavaScript, so we don't
|
|
+ // support this, and it should never be called ... right?
|
|
+ MOZ_CRASH("on a scale of one to even I just can't");
|
|
+ break;
|
|
+ case RoundingMode::TowardsZero:
|
|
+ as_friz(dest, src);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("unsupported mode");
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::nearbyIntFloat32(RoundingMode mode, FloatRegister src,
|
|
+ FloatRegister dest)
|
|
+{
|
|
+ return nearbyIntDouble(mode, src, dest);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::ceilFloat32ToInt32(FloatRegister src, Register dest,
|
|
+ Label* fail)
|
|
+{
|
|
+ return ceilDoubleToInt32(src, dest, fail);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::ceilDoubleToInt32(FloatRegister src, Register dest, Label* fail)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(dest != ScratchRegister);
|
|
+ MOZ_ASSERT(src != ScratchDoubleReg);
|
|
+
|
|
+ // We only care if the conversion is invalid, not if it's inexact.
|
|
+ // However, JavaScript defines Math.ceil(-0) == -0, so we need a check.
|
|
+ // Whack VXCVI.
|
|
+ as_mtfsb0(23);
|
|
+ // "Pre-round" to +inf. Any NaN will get passed to fctiw.
|
|
+ // (For pre-v2.02, set rounding to 0b10.)
|
|
+ as_frip(ScratchDoubleReg, src);
|
|
+ as_fctiw(ScratchDoubleReg, ScratchDoubleReg);
|
|
+ // VXCVI is a failure (over/underflow, NaN, etc.)
|
|
+ as_mcrfs(cr1, 5); // reserved - VXSOFT - VXSQRT - VXCVI -> CR1[...SO]
|
|
+ moveFromDouble(ScratchDoubleReg, dest);
|
|
+ as_srawi(dest, dest, 0); // clear upper word and sign extend
|
|
+ as_cmpdi(cr7, dest, 0); // check for zero
|
|
+ moveFromDouble(src, ScratchRegister);
|
|
+ as_cmpdi(ScratchRegister, 0); // check sign bit of original float
|
|
+ as_crand(0, 0, 30); // CR0[LT] &= CR7[EQ] (only bail if - and 0)
|
|
+ as_cror(0, 0, 7); // Licenced to kill: CR0[LT] |= CR1[SO]
|
|
+ ma_bc(Assembler::LessThan, fail);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::floorFloat32ToInt32(FloatRegister src, Register dest,
|
|
+ Label* fail)
|
|
+{
|
|
+ return floorDoubleToInt32(src, dest, fail);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::floorDoubleToInt32(FloatRegister src, Register dest, Label* fail)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(dest != ScratchRegister);
|
|
+ MOZ_ASSERT(src != ScratchDoubleReg);
|
|
+
|
|
+ // We only care if the conversion is invalid, not if it's inexact.
|
|
+ // However, we have to check -0 here too for the same stupid reason.
|
|
+ // Whack VXCVI.
|
|
+ as_mtfsb0(23);
|
|
+ // "Pre-round" to -inf. Any NaN will get passed to fctiw.
|
|
+ // (For pre-v2.02, set rounding to 0b11.)
|
|
+ as_frim(ScratchDoubleReg, src);
|
|
+ as_fctiw(ScratchDoubleReg, ScratchDoubleReg);
|
|
+ // VXCVI is a failure (over/underflow, NaN, etc.)
|
|
+ as_mcrfs(cr1, 5); // reserved - VXSOFT - VXSQRT - VXCVI -> CR1[...SO]
|
|
+ moveFromDouble(ScratchDoubleReg, dest);
|
|
+ as_srawi(dest, dest, 0); // clear upper word and sign extend
|
|
+ as_cmpdi(cr7, dest, 0); // check for zero
|
|
+ moveFromDouble(src, ScratchRegister);
|
|
+ as_cmpdi(ScratchRegister, 0); // check sign bit of original float
|
|
+ as_crand(0, 0, 30); // CR0[LT] &= CR7[EQ] (only bail if - and 0)
|
|
+ as_cror(0, 0, 7); // Nobody does it better: CR0[LT] |= CR1[SO]
|
|
+ ma_bc(Assembler::LessThan, fail);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::roundFloat32ToInt32(FloatRegister src, Register dest,
|
|
+ FloatRegister temp, Label* fail)
|
|
+{
|
|
+ return floorDoubleToInt32(src, dest, fail);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::roundDoubleToInt32(FloatRegister src, Register dest,
|
|
+ FloatRegister temp, Label* fail)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(dest != ScratchRegister);
|
|
+ MOZ_ASSERT(src != ScratchDoubleReg);
|
|
+
|
|
+ // We only care if the conversion is invalid, not if it's inexact.
|
|
+ // And, you know, negative zero. BECAUSE THAT HAPPENS SOOO MUCH.
|
|
+ // Whack VXCVI.
|
|
+ as_mtfsb0(23);
|
|
+ // The default b00 rounding mode is implemented as IEEE round-to-nearest
|
|
+ // and ties-to-even. This means round(0.5) == 0. However, JavaScript
|
|
+ // expects round(0.5) == 1. |frin| is not an exact duplicate for this
|
|
+ // behaviour either. The best option is to fudge it by adding 0.5 and
|
|
+ // round towards -Inf, which works in both the positive and negative cases.
|
|
+ xs_lis(ScratchRegister, 0x3f00); // 0x3f000000 = 0.5
|
|
+ moveToFloat32(ScratchRegister, ScratchDoubleReg);
|
|
+ as_fadd(ScratchDoubleReg, ScratchDoubleReg, src); // preserves NaN
|
|
+ as_mtfsfi(7, 3); // RN to -Inf
|
|
+ as_fctiw(ScratchDoubleReg, ScratchDoubleReg); // truncate according to RN
|
|
+ // VXCVI is a failure (over/underflow, NaN, etc.)
|
|
+ as_mcrfs(cr1, 5); // reserved - VXSOFT - VXSQRT - VXCVI -> CR1[...SO]
|
|
+ as_mtfsfi(7, 0); // RN to default
|
|
+ moveFromDouble(ScratchDoubleReg, dest);
|
|
+ as_srawi(dest, dest, 0); // clear upper word and sign extend
|
|
+ as_cmpdi(cr7, dest, 0); // check for zero
|
|
+ moveFromDouble(src, ScratchRegister);
|
|
+ as_cmpdi(ScratchRegister, 0); // check sign bit of original float
|
|
+ as_crand(0, 0, 30); // CR0[LT] &= CR7[EQ] (only bail if - and 0)
|
|
+ as_cror(0, 0, 7); // Makes me feel sad for the rest: CR0[LT] |= CR1[SO]
|
|
+ ma_bc(Assembler::LessThan, fail);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::flexibleRemainder32(Register rhs, Register srcDest,
|
|
+ bool isUnsigned, const LiveRegisterSet&)
|
|
+{
|
|
+ ADBlock();
|
|
+ remainder32(rhs, srcDest, isUnsigned);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::flexibleQuotient32(Register rhs, Register srcDest,
|
|
+ bool isUnsigned,
|
|
+ const LiveRegisterSet&)
|
|
+{
|
|
+ ADBlock();
|
|
+ quotient32(rhs, srcDest, isUnsigned);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::flexibleDivMod32(Register rhs, Register srcDest,
|
|
+ Register remOutput, bool isUnsigned,
|
|
+ const LiveRegisterSet&)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ if (HasPPCISA3()) {
|
|
+ if (isUnsigned) {
|
|
+ as_moduw(remOutput, srcDest, rhs);
|
|
+ as_divwu(srcDest, srcDest, rhs);
|
|
+ } else {
|
|
+ as_modsw(remOutput, srcDest, rhs);
|
|
+ as_divw(srcDest, srcDest, rhs);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Register scratch = ScratchRegister;
|
|
+ if (isUnsigned) {
|
|
+ as_divwu(scratch, srcDest, rhs);
|
|
+ } else {
|
|
+ as_divw(scratch, srcDest, rhs);
|
|
+ }
|
|
+ // Compute remainder
|
|
+ as_mullw(remOutput, srcDest, rhs);
|
|
+ as_subf(remOutput, scratch, srcDest);
|
|
+ xs_mr(srcDest, scratch);
|
|
+}
|
|
+
|
|
+//}}} check_macroassembler_style
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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/. */
|
|
+
|
|
+#include "mozilla/EndianUtils.h"
|
|
+
|
|
+#include "jit/MacroAssembler.h"
|
|
+
|
|
+using namespace js;
|
|
+using namespace jit;
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_move(Register rd, Register rs)
|
|
+{
|
|
+ if (rd != rs)
|
|
+ as_or(rd, rs, rs);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_li(Register dest, ImmGCPtr ptr)
|
|
+{
|
|
+ writeDataRelocation(ptr);
|
|
+ asMasm().ma_liPatchable(dest, ImmPtr(ptr.value));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_li(Register dest, Imm32 imm)
|
|
+{
|
|
+ ADBlock();
|
|
+ // signed
|
|
+// XXX: why??? why not just call ma_li(Register dest, int64_t value)?
|
|
+ if (Imm16::IsInSignedRange(imm.value)) {
|
|
+ xs_li(dest, imm.value);
|
|
+ } else if (Imm16::IsInUnsignedRange(imm.value)) {
|
|
+ xs_li(dest, 0);
|
|
+ as_ori(dest, dest, Imm16::Lower(imm).encode());
|
|
+ } else if (Imm16::Lower(imm).encode() == 0) {
|
|
+ xs_lis(dest, Imm16::Upper(imm).encode());
|
|
+ } else {
|
|
+ xs_lis(dest, Imm16::Upper(imm).encode());
|
|
+ as_ori(dest, dest, Imm16::Lower(imm).encode());
|
|
+ }
|
|
+}
|
|
+
|
|
+// This method generates lis and ori instruction pair that can be modified by
|
|
+// UpdateLisOriValue, either during compilation (eg. Assembler::bind), or
|
|
+// during execution (eg. jit::PatchJump).
|
|
+void
|
|
+MacroAssemblerPPC64::ma_liPatchable(Register dest, Imm32 imm)
|
|
+{
|
|
+ m_buffer.ensureSpace(2 * sizeof(uint32_t));
|
|
+ xs_lis(dest, Imm16::Upper(imm).encode());
|
|
+ as_ori(dest, dest, Imm16::Lower(imm).encode());
|
|
+}
|
|
+
|
|
+// Shifts
|
|
+
|
|
+// Bit extract/insert
|
|
+void
|
|
+MacroAssemblerPPC64::ma_ext(Register rt, Register rs, uint16_t pos, uint16_t size) {
|
|
+ MOZ_ASSERT(pos < 32);
|
|
+ MOZ_ASSERT(pos + size < 33);
|
|
+
|
|
+ as_rlwinm(rt, rs, 0, pos, size);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_ins(Register rt, Register rs, uint16_t pos, uint16_t size) {
|
|
+ MOZ_ASSERT(pos < 32);
|
|
+ MOZ_ASSERT(pos + size <= 32);
|
|
+ MOZ_ASSERT(size != 0);
|
|
+
|
|
+ as_rlwimi(rt, rs, 0, pos, size);
|
|
+}
|
|
+
|
|
+
|
|
+// And.
|
|
+void
|
|
+MacroAssemblerPPC64::ma_and(Register rd, Register rs)
|
|
+{
|
|
+ as_and(rd, rd, rs);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_and(Register rd, Imm32 imm)
|
|
+{
|
|
+ ma_and(rd, rd, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_and(Register rd, Register rs, Imm32 imm)
|
|
+{
|
|
+ if (Imm16::IsInUnsignedRange(imm.value)) {
|
|
+ as_andi_rc(rd, rs, imm.value);
|
|
+ } else {
|
|
+ MOZ_ASSERT(rs != ScratchRegister);
|
|
+
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ as_and(rd, rs, ScratchRegister);
|
|
+ }
|
|
+}
|
|
+
|
|
+// Or.
|
|
+void
|
|
+MacroAssemblerPPC64::ma_or(Register rd, Register rs)
|
|
+{
|
|
+ as_or(rd, rd, rs);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_or(Register rd, Imm32 imm)
|
|
+{
|
|
+ ma_or(rd, rd, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_or(Register rd, Register rs, Imm32 imm)
|
|
+{
|
|
+ if (Imm16::IsInUnsignedRange(imm.value)) {
|
|
+ as_ori(rd, rs, imm.value);
|
|
+ } else {
|
|
+ MOZ_ASSERT(rs != ScratchRegister);
|
|
+
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ as_or(rd, rs, ScratchRegister);
|
|
+ }
|
|
+}
|
|
+
|
|
+// xor
|
|
+void
|
|
+MacroAssemblerPPC64::ma_xor(Register rd, Register rs)
|
|
+{
|
|
+ as_xor(rd, rd, rs);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_xor(Register rd, Imm32 imm)
|
|
+{
|
|
+ ma_xor(rd, rd, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_xor(Register rd, Register rs, Imm32 imm)
|
|
+{
|
|
+ if (Imm16::IsInUnsignedRange(imm.value)) {
|
|
+ as_xori(rd, rs, imm.value);
|
|
+ } else {
|
|
+ MOZ_ASSERT(rs != ScratchRegister);
|
|
+
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ as_xor(rd, rs, ScratchRegister);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+// Arithmetic-based ops.
|
|
+
|
|
+// Add.
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_addTestCarry(Condition cond, Register rd, Register rs, Register rt,
|
|
+ Label* overflow, bool is32)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(cond == Assembler::CarrySet || cond == Assembler::CarryClear);
|
|
+ MOZ_ASSERT_IF(rd == rs, rt != rd);
|
|
+ as_addc(rd, rs, rt);
|
|
+ as_mcrxrx(cr0);
|
|
+ if (is32) {
|
|
+ // CA32 (Power ISA 3.0B spec page 120)
|
|
+ ma_bc(cond == Assembler::CarrySet ? Assembler::SOBit : Assembler::NSOBit, overflow);
|
|
+ } else {
|
|
+ // regular CA
|
|
+ ma_bc(cond == Assembler::CarrySet ? Assembler::Equal : Assembler::NotEqual, overflow);
|
|
+ }
|
|
+}
|
|
+
|
|
+// XXX: add short option
|
|
+void
|
|
+MacroAssemblerPPC64::ma_addTestCarry(Condition cond, Register rd, Register rs, Imm32 imm,
|
|
+ Label* overflow, bool is32)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(cond == Assembler::CarrySet || cond == Assembler::CarryClear);
|
|
+ if (!Imm16::IsInSignedRange(imm.value)) {
|
|
+ MOZ_ASSERT(rs != ScratchRegister);
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ ma_addTestCarry(cond, rd, rs, ScratchRegister, overflow, is32);
|
|
+ return;
|
|
+ }
|
|
+ as_addic(rd, rs, imm.value);
|
|
+ as_mcrxrx(cr0);
|
|
+ if (is32) {
|
|
+ // CA32
|
|
+ ma_bc(cond == Assembler::CarrySet ? Assembler::SOBit : Assembler::NSOBit, overflow);
|
|
+ } else {
|
|
+ // regular CA
|
|
+ ma_bc(cond == Assembler::CarrySet ? Assembler::Equal : Assembler::NotEqual, overflow);
|
|
+ }
|
|
+}
|
|
+
|
|
+// XXX: add short option
|
|
+void
|
|
+MacroAssemblerPPC64::ma_addTestCarry(Condition cond, Register rd, Register rs, ImmWord imm,
|
|
+ Label* overflow, bool is32)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(cond == Assembler::CarrySet || cond == Assembler::CarryClear);
|
|
+ // This can only enter through a 64-bit path.
|
|
+ MOZ_ASSERT(!is32);
|
|
+;
|
|
+ if (!Imm16::IsInSignedRange(imm.value)) {
|
|
+ MOZ_ASSERT(rs != ScratchRegister);
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ ma_addTestCarry(cond, rd, rs, ScratchRegister, overflow, is32);
|
|
+ return;
|
|
+ }
|
|
+ as_addic(rd, rs, imm.value);
|
|
+ as_mcrxrx(cr0);
|
|
+ ma_bc(cond == Assembler::CarrySet ? Assembler::Equal : Assembler::NotEqual, overflow);
|
|
+}
|
|
+
|
|
+// Subtract.
|
|
+void
|
|
+MacroAssemblerPPC64::ma_subu(Register rd, Register rs, Imm32 imm)
|
|
+{
|
|
+ if (Imm16::IsInSignedRange(-imm.value)) {
|
|
+ as_addi(rd, rs, -imm.value);
|
|
+ } else {
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ as_subf(rd, ScratchRegister, rs);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_subu(Register rd, Imm32 imm)
|
|
+{
|
|
+ ma_subu(rd, rd, imm);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_subu(Register rd, Register rs)
|
|
+{
|
|
+ as_subf(rd, rs, rd);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_mul(Register rd, Register rs, Imm32 imm)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(rs != ScratchRegister);
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ as_mulld(rd, rs, ScratchRegister);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_mul_branch_overflow(Register rd, Register rs, Register rt, Label* overflow)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(rs != ScratchRegister);
|
|
+ MOZ_ASSERT(rt != ScratchRegister);
|
|
+
|
|
+ // This is a 32-bit operation, so we need to whack and test XER[OV32].
|
|
+ xs_li(ScratchRegister, 0);
|
|
+ xs_mtxer(ScratchRegister);
|
|
+ as_mullwo(rd, rs, rt);
|
|
+ ma_bc(Assembler::Overflow, overflow);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_mul_branch_overflow(Register rd, Register rs, Imm32 imm, Label* overflow)
|
|
+{
|
|
+ ADBlock();
|
|
+ ma_li(SecondScratchReg, imm);
|
|
+ ma_mul_branch_overflow(rd, rs, SecondScratchReg, overflow);
|
|
+}
|
|
+
|
|
+// Memory.
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::ma_load(Register dest, const BaseIndex& src,
|
|
+ LoadStoreSize size, LoadStoreExtension extension)
|
|
+{
|
|
+ // TODO: See note in ::computeScaledAddress. Can we turn this into
|
|
+ // a smaller instruction sequence?
|
|
+ asMasm().computeScaledAddress(src, SecondScratchReg);
|
|
+
|
|
+ // If src.offset is out of 16-bit signed range, we will hit an assert
|
|
+ // doing the next ma_load() because the second scratch register is needed
|
|
+ // again. In that case, hoist the add up since we can freely clobber it.
|
|
+ if (!Imm16::IsInSignedRange(src.offset)) {
|
|
+ ma_add(SecondScratchReg, SecondScratchReg, Imm32(src.offset));
|
|
+ return ma_load(dest, Address(SecondScratchReg, 0), size, extension);
|
|
+ } else {
|
|
+ return asMasm().ma_load(dest, Address(SecondScratchReg, src.offset), size, extension);
|
|
+ }
|
|
+}
|
|
+
|
|
+#if(0)
|
|
+// XXX: remove
|
|
+void
|
|
+MacroAssemblerPPC64::ma_load_unaligned(const wasm::MemoryAccessDesc& access, Register dest, const BaseIndex& src, Register temp,
|
|
+ LoadStoreSize size, LoadStoreExtension extension)
|
|
+{
|
|
+ MOZ_ASSERT(MOZ_LITTLE_ENDIAN(), "Wasm-only; wasm is disabled on big-endian.");
|
|
+ MOZ_CRASH("only needed for 64-bit loads");
|
|
+#if 0
|
|
+ int16_t lowOffset, hiOffset;
|
|
+ Register base;
|
|
+
|
|
+ asMasm().computeScaledAddress(src, SecondScratchReg);
|
|
+
|
|
+ if (Imm16::IsInSignedRange(src.offset) && Imm16::IsInSignedRange(src.offset + size / 8 - 1)) {
|
|
+ base = SecondScratchReg;
|
|
+ lowOffset = Imm16(src.offset).encode();
|
|
+ hiOffset = Imm16(src.offset + size / 8 - 1).encode();
|
|
+ } else {
|
|
+ ma_li(ScratchRegister, Imm32(src.offset));
|
|
+ asMasm().addPtr(SecondScratchReg, ScratchRegister);
|
|
+ base = ScratchRegister;
|
|
+ lowOffset = Imm16(0).encode();
|
|
+ hiOffset = Imm16(size / 8 - 1).encode();
|
|
+ }
|
|
+
|
|
+ BufferOffset load;
|
|
+ switch (size) {
|
|
+ case SizeHalfWord:
|
|
+ if (extension != ZeroExtend)
|
|
+ load = as_lbu(temp, base, hiOffset);
|
|
+ else
|
|
+ load = as_lb(temp, base, hiOffset);
|
|
+ as_lbu(dest, base, lowOffset);
|
|
+ ma_ins(dest, temp, 8, 24);
|
|
+ break;
|
|
+ case SizeWord:
|
|
+ load = as_lwl(dest, base, hiOffset);
|
|
+ as_lwr(dest, base, lowOffset);
|
|
+#ifdef JS_CODEGEN_PPC64
|
|
+ if (extension != ZeroExtend)
|
|
+ as_dext(dest, dest, 0, 32);
|
|
+#endif
|
|
+ break;
|
|
+#ifdef JS_CODEGEN_PPC64
|
|
+ case SizeDouble:
|
|
+ load = as_ldl(dest, base, hiOffset);
|
|
+ as_ldr(dest, base, lowOffset);
|
|
+ break;
|
|
+#endif
|
|
+ default:
|
|
+ MOZ_CRASH("Invalid argument for ma_load");
|
|
+ }
|
|
+
|
|
+ append(access, load.getOffset());
|
|
+#endif
|
|
+}
|
|
+#endif
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::ma_store(Register data, const BaseIndex& dest,
|
|
+ LoadStoreSize size)
|
|
+{
|
|
+ // TODO: See note in ::computeScaledAddress. Can we turn this into
|
|
+ // a smaller instruction sequence?
|
|
+ MOZ_ASSERT(data != SecondScratchReg);
|
|
+ asMasm().computeScaledAddress(dest, SecondScratchReg);
|
|
+
|
|
+ // If dest.offset is out of 16-bit signed range, we will hit an assert
|
|
+ // doing the next ma_store() because the second scratch register is needed
|
|
+ // again. In that case, hoist the add up since we can freely clobber it.
|
|
+ if (!Imm16::IsInSignedRange(dest.offset)) {
|
|
+ ma_add(SecondScratchReg, SecondScratchReg, Imm32(dest.offset));
|
|
+ return ma_store(data, Address(SecondScratchReg, 0), size);
|
|
+ } else {
|
|
+ return asMasm().ma_store(data, Address(SecondScratchReg, dest.offset), size);
|
|
+ }
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::ma_store(Imm32 imm, const BaseIndex& dest,
|
|
+ LoadStoreSize size)
|
|
+{
|
|
+ // Make sure that SecondScratchReg contains the absolute address so that
|
|
+ // offset is 0.
|
|
+ asMasm().computeEffectiveAddress(dest, SecondScratchReg);
|
|
+
|
|
+ // With offset of 0 ScratchRegister will not be used in ma_store(),
|
|
+ // so we can use it as a parameter here.
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ return asMasm().ma_store(ScratchRegister, Address(SecondScratchReg, 0), size);
|
|
+}
|
|
+
|
|
+#if(0)
|
|
+// XXX: remove
|
|
+void
|
|
+MacroAssemblerPPC64::ma_store_unaligned(Register src, const BaseIndex& dest,
|
|
+ LoadStoreSize size)
|
|
+{
|
|
+ MOZ_CRASH("NYI");
|
|
+}
|
|
+
|
|
+// XXX: remove
|
|
+void
|
|
+MacroAssemblerPPC64::ma_store_unaligned(const wasm::MemoryAccessDesc& access, Register data,
|
|
+ const BaseIndex& dest, Register temp,
|
|
+ LoadStoreSize size, LoadStoreExtension extension)
|
|
+{
|
|
+ MOZ_ASSERT(MOZ_LITTLE_ENDIAN(), "Wasm-only; wasm is disabled on big-endian.");
|
|
+ MOZ_CRASH("only needed for 64-bit loads");
|
|
+#if 0
|
|
+ int16_t lowOffset, hiOffset;
|
|
+ Register base;
|
|
+
|
|
+ asMasm().computeScaledAddress(dest, SecondScratchReg);
|
|
+
|
|
+ if (Imm16::IsInSignedRange(dest.offset) && Imm16::IsInSignedRange(dest.offset + size / 8 - 1)) {
|
|
+ base = SecondScratchReg;
|
|
+ lowOffset = Imm16(dest.offset).encode();
|
|
+ hiOffset = Imm16(dest.offset + size / 8 - 1).encode();
|
|
+ } else {
|
|
+ ma_li(ScratchRegister, Imm32(dest.offset));
|
|
+ asMasm().addPtr(SecondScratchReg, ScratchRegister);
|
|
+ base = ScratchRegister;
|
|
+ lowOffset = Imm16(0).encode();
|
|
+ hiOffset = Imm16(size / 8 - 1).encode();
|
|
+ }
|
|
+
|
|
+ BufferOffset store;
|
|
+ switch (size) {
|
|
+ case SizeHalfWord:
|
|
+ ma_ext(temp, data, 8, 8);
|
|
+ store = as_sb(temp, base, hiOffset);
|
|
+ as_sb(data, base, lowOffset);
|
|
+ break;
|
|
+ case SizeWord:
|
|
+ store = as_swl(data, base, hiOffset);
|
|
+ as_swr(data, base, lowOffset);
|
|
+ break;
|
|
+#ifdef JS_CODEGEN_PPC64
|
|
+ case SizeDouble:
|
|
+ store = as_sdl(data, base, hiOffset);
|
|
+ as_sdr(data, base, lowOffset);
|
|
+ break;
|
|
+#endif
|
|
+ default:
|
|
+ MOZ_CRASH("Invalid argument for ma_store");
|
|
+ }
|
|
+ append(access, store.getOffset());
|
|
+#endif
|
|
+}
|
|
+#endif
|
|
+
|
|
+// Branches when done from within ppc64-specific code.
|
|
+void
|
|
+MacroAssemblerPPC64::ma_bc(Register lhs, Register rhs, Label* label, Condition c, JumpKind jumpKind)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(!(c & ConditionOnlyXER));
|
|
+ if (c == Always) {
|
|
+ ma_b(label, jumpKind);
|
|
+ } else if (c & ConditionZero) {
|
|
+ MOZ_ASSERT(lhs == rhs);
|
|
+ as_cmpdi(lhs, 0);
|
|
+ ma_bc(c, label, jumpKind);
|
|
+ } else if (c & ConditionUnsigned) {
|
|
+ as_cmpld(lhs, rhs);
|
|
+ ma_bc(c, label, jumpKind);
|
|
+ } else {
|
|
+ MOZ_ASSERT(c < 0x100); // paranoia
|
|
+ as_cmpd(lhs, rhs);
|
|
+ ma_bc(c, label, jumpKind);
|
|
+ }
|
|
+}
|
|
+
|
|
+// For an explicit 32-bit compare (mostly wasm).
|
|
+void
|
|
+MacroAssemblerPPC64::ma_bc32(Register lhs, Register rhs, Label* label, Condition c, JumpKind jumpKind)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(!(c & ConditionOnlyXER));
|
|
+ if (c == Always) {
|
|
+ ma_b(label, jumpKind);
|
|
+ } else if (c & ConditionZero) {
|
|
+ MOZ_ASSERT(lhs == rhs);
|
|
+ as_cmpwi(lhs, 0);
|
|
+ ma_bc(c, label, jumpKind);
|
|
+ } else if (c & ConditionUnsigned) {
|
|
+ as_cmplw(lhs, rhs);
|
|
+ ma_bc(c, label, jumpKind);
|
|
+ } else {
|
|
+ MOZ_ASSERT(c < 0x100); // paranoia
|
|
+ as_cmpw(lhs, rhs);
|
|
+ ma_bc(c, label, jumpKind);
|
|
+ }
|
|
+}
|
|
+
|
|
+// For an explicit 64-bit compare.
|
|
+void
|
|
+MacroAssemblerPPC64::ma_bc64(Register lhs, Imm32 imm, Label* label, Condition c, JumpKind jumpKind)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(!(c & ConditionOnlyXER));
|
|
+ if (c == Always) {
|
|
+ ma_b(label, jumpKind);
|
|
+ return;
|
|
+ }
|
|
+ if (c & ConditionZero) {
|
|
+ MOZ_ASSERT(imm.value == 0);
|
|
+ as_cmpdi(lhs, 0);
|
|
+ ma_bc(c, label, jumpKind);
|
|
+ return;
|
|
+ }
|
|
+ if (c & ConditionUnsigned) {
|
|
+ if (Imm16::IsInUnsignedRange(imm.value)) {
|
|
+ as_cmpldi(lhs, imm.value);
|
|
+ } else {
|
|
+ MOZ_ASSERT(lhs != ScratchRegister);
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ as_cmpld(lhs, ScratchRegister);
|
|
+ }
|
|
+ } else {
|
|
+ MOZ_ASSERT(c < 0x100); // just in case
|
|
+ if (Imm16::IsInSignedRange(imm.value)) {
|
|
+ as_cmpdi(lhs, imm.value);
|
|
+ } else {
|
|
+ MOZ_ASSERT(lhs != ScratchRegister);
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ as_cmpd(lhs, ScratchRegister);
|
|
+ }
|
|
+ }
|
|
+ ma_bc(c, label, jumpKind);
|
|
+}
|
|
+
|
|
+// For everyone else, there's MasterCard.
|
|
+void
|
|
+MacroAssemblerPPC64::ma_bc(Register lhs, Imm32 imm, Label* label, Condition c, JumpKind jumpKind)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(!(c & ConditionOnlyXER));
|
|
+ if (c == Always) {
|
|
+ ma_b(label, jumpKind);
|
|
+ return;
|
|
+ }
|
|
+ if (c & ConditionZero) {
|
|
+ MOZ_ASSERT(imm.value == 0);
|
|
+ as_cmpdi(lhs, 0);
|
|
+ ma_bc(c, label, jumpKind);
|
|
+ return;
|
|
+ }
|
|
+ if (c & ConditionUnsigned) {
|
|
+ if (Imm16::IsInUnsignedRange(imm.value)) {
|
|
+ as_cmplwi(lhs, imm.value);
|
|
+ } else {
|
|
+ MOZ_ASSERT(lhs != ScratchRegister);
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ as_cmplw(lhs, ScratchRegister);
|
|
+ }
|
|
+ } else {
|
|
+ MOZ_ASSERT(c < 0x100); // just in case
|
|
+ if (Imm16::IsInSignedRange(imm.value)) {
|
|
+ as_cmpwi(lhs, imm.value);
|
|
+ } else {
|
|
+ MOZ_ASSERT(lhs != ScratchRegister);
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ as_cmpw(lhs, ScratchRegister);
|
|
+ }
|
|
+ }
|
|
+ ma_bc(c, label, jumpKind);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_bc(Register lhs, ImmPtr imm, Label* l, Condition c, JumpKind jumpKind)
|
|
+{
|
|
+ asMasm().ma_bc(lhs, ImmWord(uintptr_t(imm.value)), l, c, jumpKind);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_b(Label* label, JumpKind jumpKind)
|
|
+{
|
|
+ ADBlock();
|
|
+ if (!label->bound()) {
|
|
+ BufferOffset bo;
|
|
+
|
|
+ // Emit an unbound branch to be bound later by |Assembler::bind|.
|
|
+ // This and ma_bc() are largely the same in this respect.
|
|
+ spew(".Llabel %p", label);
|
|
+ uint32_t nextInChain = label->used() ? label->offset() : LabelBase::INVALID_OFFSET;
|
|
+ if (jumpKind == ShortJump) {
|
|
+ // We know this branch must be short. Unfortunately, because we
|
|
+ // have to also store the next-in-chain, we can't make this less
|
|
+ // than two instructions.
|
|
+ m_buffer.ensureSpace(2 * sizeof(uint32_t));
|
|
+ bo = as_b(4, RelativeBranch, DontLinkB);
|
|
+ spew(".long %08x ; next in chain", nextInChain);
|
|
+ writeInst(nextInChain);
|
|
+ if (!oom())
|
|
+ label->use(bo.getOffset());
|
|
+ } else {
|
|
+ m_buffer.ensureSpace(7 * sizeof(uint32_t));
|
|
+ bo = xs_trap_tagged(BTag);
|
|
+ spew(".long %08x ; next in chain", nextInChain);
|
|
+ writeInst(nextInChain);
|
|
+ if (!oom())
|
|
+ label->use(bo.getOffset());
|
|
+ // Leave space for potential long jump.
|
|
+ as_nop(); // rldicr
|
|
+ as_nop(); // oris
|
|
+ as_nop(); // ori
|
|
+ as_nop(); // mtctr
|
|
+ as_nop(); // bctr
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Label is bound, emit final code.
|
|
+ int64_t offset = label->offset() - currentOffset();
|
|
+ if (jumpKind == ShortJump || JOffImm26::IsInRange(offset)) {
|
|
+ spew("# static short jump %08x to label %p @ %08x (offset %ld)",
|
|
+ currentOffset(), label, label->offset(), offset);
|
|
+ MOZ_ASSERT(JOffImm26::IsInRange(offset));
|
|
+ as_b(offset);
|
|
+ } else {
|
|
+ // Use r12 "as expected" even though this is probably not to ABI-compliant code.
|
|
+ m_buffer.ensureSpace(7 * sizeof(uint32_t));
|
|
+ addLongJump(nextOffset(), BufferOffset(label->offset()));
|
|
+ ma_liPatchable(SecondScratchReg, ImmWord(LabelBase::INVALID_OFFSET));
|
|
+ xs_mtctr(SecondScratchReg);
|
|
+ as_bctr();
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_cmp32(Register lhs, Register rhs, Condition c)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(!(c & ConditionOnlyXER));
|
|
+ MOZ_ASSERT(!(c & ConditionZero));
|
|
+
|
|
+ if (c & ConditionUnsigned) {
|
|
+ as_cmplw(lhs, rhs);
|
|
+ } else {
|
|
+ as_cmpw(lhs, rhs);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_cmp32(Register lhs, Imm32 rhs, Condition c)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(!(c & ConditionOnlyXER));
|
|
+ MOZ_ASSERT_IF((c & ConditionZero), (rhs.value == 0));
|
|
+
|
|
+ if (c & ConditionZero) {
|
|
+ as_cmpwi(lhs, 0);
|
|
+ } else {
|
|
+ if (c & ConditionUnsigned) {
|
|
+ if (Imm16::IsInUnsignedRange(rhs.value)) {
|
|
+ as_cmplwi(lhs, rhs.value);
|
|
+ } else {
|
|
+ MOZ_ASSERT(lhs != ScratchRegister);
|
|
+ ma_li(ScratchRegister, rhs);
|
|
+ as_cmplw(lhs, ScratchRegister);
|
|
+ }
|
|
+ } else {
|
|
+ if (Imm16::IsInSignedRange(rhs.value)) {
|
|
+ as_cmpwi(lhs, rhs.value);
|
|
+ } else {
|
|
+ MOZ_ASSERT(lhs != ScratchRegister);
|
|
+ ma_li(ScratchRegister, rhs);
|
|
+ as_cmpw(lhs, ScratchRegister);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_cmp64(Register lhs, Imm64 rhs, Condition c)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(!(c & ConditionOnlyXER));
|
|
+ MOZ_ASSERT_IF((c & ConditionZero), (rhs.value == 0));
|
|
+
|
|
+ if (c & ConditionZero) {
|
|
+ as_cmpdi(lhs, 0);
|
|
+ } else {
|
|
+ if (c & ConditionUnsigned) {
|
|
+ if (Imm16::IsInUnsignedRange(rhs.value)) {
|
|
+ as_cmpldi(lhs, rhs.value);
|
|
+ } else {
|
|
+ MOZ_ASSERT(lhs != ScratchRegister);
|
|
+ ma_li(ScratchRegister, rhs);
|
|
+ as_cmpld(lhs, ScratchRegister);
|
|
+ }
|
|
+ } else {
|
|
+ if (Imm16::IsInSignedRange(rhs.value)) {
|
|
+ as_cmpdi(lhs, rhs.value);
|
|
+ } else {
|
|
+ MOZ_ASSERT(lhs != ScratchRegister);
|
|
+ ma_li(ScratchRegister, rhs);
|
|
+ as_cmpd(lhs, ScratchRegister);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_cmp32(Register lhs, const Address& rhs, Condition c)
|
|
+{
|
|
+ MOZ_ASSERT(lhs != ScratchRegister);
|
|
+ ma_load(ScratchRegister, rhs, SizeWord);
|
|
+ ma_cmp32(lhs, ScratchRegister, c);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::compareFloatingPoint(FloatRegister lhs, FloatRegister rhs,
|
|
+ DoubleCondition c)
|
|
+{
|
|
+ if ((c & DoubleConditionUnordered) || (c == DoubleUnordered)) {
|
|
+ as_fcmpu(lhs, rhs);
|
|
+ } else {
|
|
+ as_fcmpo(lhs, rhs);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_cmp_set_double(Register dest, FloatRegister lhs, FloatRegister rhs,
|
|
+ DoubleCondition c)
|
|
+{
|
|
+ Label skip;
|
|
+ compareFloatingPoint(lhs, rhs, c);
|
|
+
|
|
+ ma_li(dest, 1L);
|
|
+
|
|
+// XXX: use CR
|
|
+ ma_bc(c, &skip);
|
|
+ ma_li(dest, 0L);
|
|
+ bind(&skip);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_cmp_set(Register rd, Register rs, Imm16 imm, Condition c, bool useCmpw)
|
|
+{
|
|
+ ADBlock();
|
|
+// XXX: remove duplicate code, just call ma_cmp32 or ma_cmp64 as appropriate
|
|
+
|
|
+ // Handle any synthetic codes.
|
|
+ MOZ_ASSERT_IF((c & ConditionZero), (imm.encode() == 0));
|
|
+ MOZ_ASSERT(!(c & ConditionOnlyXER));
|
|
+ if (c & ConditionUnsigned) {
|
|
+ MOZ_ASSERT(Imm16::IsInUnsignedRange(imm.encode())); // paranoia
|
|
+ if (useCmpw) {
|
|
+ as_cmplwi(rs, imm.encode());
|
|
+ } else {
|
|
+ as_cmpldi(rs, imm.encode());
|
|
+ }
|
|
+ } else {
|
|
+ // Just because it's an Imm16 doesn't mean it always fits.
|
|
+ if (!Imm16::IsInSignedRange(imm.decodeSigned())) {
|
|
+ MOZ_ASSERT(rs != ScratchRegister);
|
|
+ ma_li(ScratchRegister, imm.decodeSigned());
|
|
+ if (useCmpw) {
|
|
+ as_cmpw(rs, ScratchRegister);
|
|
+ } else {
|
|
+ as_cmpd(rs, ScratchRegister);
|
|
+ }
|
|
+ } else {
|
|
+ if (useCmpw) {
|
|
+ as_cmpwi(rs, imm.decodeSigned());
|
|
+ } else {
|
|
+ as_cmpdi(rs, imm.decodeSigned());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Common routine to extract or flip the appropriate CR bit.
|
|
+ ma_cmp_set_coda(rd, c);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_cmp_set(Register rd, Register rs, Register rt, Condition c, bool useCmpw)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ // Handle any synthetic codes.
|
|
+ MOZ_ASSERT(!(c & ConditionOnlyXER));
|
|
+ MOZ_ASSERT(!(c & ConditionZero));
|
|
+ if (c & ConditionUnsigned) {
|
|
+ // Some compares should only pay attention to the lower word.
|
|
+ if (useCmpw) {
|
|
+ as_cmplw(rs, rt);
|
|
+ } else {
|
|
+ as_cmpld(rs, rt);
|
|
+ }
|
|
+ } else {
|
|
+ if (useCmpw) {
|
|
+ as_cmpw(rs, rt);
|
|
+ } else {
|
|
+ as_cmpd(rs, rt);
|
|
+ }
|
|
+ }
|
|
+ ma_cmp_set_coda(rd, c);
|
|
+}
|
|
+
|
|
+static_assert(((Assembler::LessThanOrEqual & Assembler::BranchOptionMask) == Assembler::BranchOnClear),
|
|
+ "Assembler conditions don't match CR bits");
|
|
+void
|
|
+MacroAssemblerPPC64::ma_cmp_set_coda(Register rd, Condition c) {
|
|
+ MOZ_ASSERT(!(c & ConditionOnlyXER));
|
|
+
|
|
+ // Extract the underlying CR field bit.
|
|
+ as_mfcr(rd);
|
|
+ switch(c & 0xff) {
|
|
+ case Equal:
|
|
+ case NotEqual:
|
|
+ as_rlwinm(rd, rd, 3, 31, 31); // PowerPC CWG page 38
|
|
+ break;
|
|
+ case GreaterThan:
|
|
+ case LessThanOrEqual:
|
|
+ as_rlwinm(rd, rd, 2, 31, 31);
|
|
+ break;
|
|
+ case LessThan:
|
|
+ case GreaterThanOrEqual:
|
|
+ as_rlwinm(rd, rd, 1, 31, 31); // p40
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("Unhandled condition");
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ // Negate the boolean if necessary.
|
|
+ if ((c & BranchOptionMask) == BranchOnClear) {
|
|
+ as_xori(rd, rd, 1);
|
|
+ }
|
|
+}
|
|
+
|
|
+// fp instructions
|
|
+void
|
|
+MacroAssemblerPPC64::ma_lis(FloatRegister dest, float value)
|
|
+{
|
|
+ Imm32 imm(mozilla::BitwiseCast<uint32_t>(value));
|
|
+
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ asMasm().moveToFloat32(ScratchRegister, dest);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::ma_sd(FloatRegister ft, BaseIndex address)
|
|
+{
|
|
+/*
|
|
+ if (Imm16::IsInSignedRange(address.offset) && address.scale == TimesOne) {
|
|
+ as_stfd(ft, address.base, address.offset);
|
|
+ return;
|
|
+ }
|
|
+*/
|
|
+
|
|
+ asMasm().computeScaledAddress(address, SecondScratchReg);
|
|
+ return asMasm().ma_sd(ft, Address(SecondScratchReg, address.offset));
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::ma_ss(FloatRegister ft, BaseIndex address)
|
|
+{
|
|
+/*
|
|
+ if (Imm16::IsInSignedRange(address.offset) && address.scale == TimesOne) {
|
|
+ as_stfs(ft, address.base, address.offset);
|
|
+ return;
|
|
+ }
|
|
+*/
|
|
+
|
|
+ asMasm().computeScaledAddress(address, SecondScratchReg);
|
|
+ return asMasm().ma_ss(ft, Address(SecondScratchReg, address.offset));
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::ma_ld(FloatRegister ft, const BaseIndex& src)
|
|
+{
|
|
+ asMasm().computeScaledAddress(src, SecondScratchReg);
|
|
+ return asMasm().ma_ld(ft, Address(SecondScratchReg, src.offset));
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::ma_ls(FloatRegister ft, const BaseIndex& src)
|
|
+{
|
|
+ asMasm().computeScaledAddress(src, SecondScratchReg);
|
|
+ return asMasm().ma_ls(ft, Address(SecondScratchReg, src.offset));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::minMaxDouble(FloatRegister srcDest, FloatRegister second,
|
|
+ bool handleNaN, bool isMax)
|
|
+{
|
|
+ ADBlock();
|
|
+ Label zero, done, nan;
|
|
+
|
|
+ if (handleNaN) {
|
|
+ // First or second is NaN, result is NaN.
|
|
+ compareFloatingPoint(srcDest, second, Assembler::DoubleUnordered);
|
|
+ ma_bc(Assembler::DoubleUnordered, &nan, ShortJump);
|
|
+ }
|
|
+ // Check for zero and equal.
|
|
+ asMasm().loadConstantDouble(0.0, ScratchDoubleReg);
|
|
+ as_fcmpo(srcDest, ScratchDoubleReg);
|
|
+ as_fcmpo(cr1, second, ScratchDoubleReg);
|
|
+ // Hoist the sub here because we've already done the compare and the
|
|
+ // crand will serialize.
|
|
+ as_fsub(ScratchDoubleReg, srcDest, second);
|
|
+ as_crand(2, 6, 2); // CR0[EQ] &= CR1[EQ]
|
|
+ // We can use ::Equal, because the unordered check is done, and save
|
|
+ // emitting a crandc we don't actually need.
|
|
+ ma_bc(Assembler::Equal, &zero, ShortJump);
|
|
+
|
|
+ // Neither can be zero. Use fsel.
|
|
+ if (isMax) {
|
|
+ as_fsel(srcDest, ScratchDoubleReg, srcDest, second);
|
|
+ } else {
|
|
+ as_fsel(srcDest, ScratchDoubleReg, second, srcDest);
|
|
+ }
|
|
+ ma_b(&done, ShortJump);
|
|
+
|
|
+ if (handleNaN) {
|
|
+ bind(&nan);
|
|
+ asMasm().loadConstantDouble(JS::GenericNaN(), srcDest);
|
|
+ ma_b(&done, ShortJump);
|
|
+ }
|
|
+
|
|
+ // Make sure we handle -0 and 0 right. Dump A into a GPR and check
|
|
+ // the sign bit.
|
|
+ bind(&zero);
|
|
+ asMasm().moveFromDouble(srcDest, ScratchRegister);
|
|
+ as_cmpdi(ScratchRegister, 0);
|
|
+ if (isMax) {
|
|
+ // If a == -0, then b is either -0 or 0. In this case, return b.
|
|
+ ma_bc(Assembler::GreaterThanOrEqual, &done, ShortJump); // a == 0
|
|
+ } else {
|
|
+ // If a == -0, then b is either -0 or 0. In this case, return a.
|
|
+ ma_bc(Assembler::LessThan, &done, ShortJump); // a == -0
|
|
+ }
|
|
+ as_fmr(srcDest, second);
|
|
+ bind(&done);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::loadDouble(const Address& address, FloatRegister dest)
|
|
+{
|
|
+ return ma_ld(dest, address);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::loadDouble(const BaseIndex& src, FloatRegister dest)
|
|
+{
|
|
+ return ma_ld(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::loadFloatAsDouble(const Address& address, FloatRegister dest)
|
|
+{
|
|
+ ma_ls(dest, address);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::loadFloatAsDouble(const BaseIndex& src, FloatRegister dest)
|
|
+{
|
|
+ ma_ls(dest, src);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::loadFloat32(const Address& address, FloatRegister dest)
|
|
+{
|
|
+ return ma_ls(dest, address);
|
|
+}
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssemblerPPC64::loadFloat32(const BaseIndex& src, FloatRegister dest)
|
|
+{
|
|
+ return ma_ls(dest, src);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_call(ImmPtr dest)
|
|
+{
|
|
+ asMasm().ma_liPatchable(CallReg, dest);
|
|
+ xs_mtctr(CallReg);
|
|
+ as_bctr(LinkB);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::ma_jump(ImmPtr dest)
|
|
+{
|
|
+ asMasm().ma_liPatchable(SecondScratchReg, dest);
|
|
+ xs_mtctr(SecondScratchReg);
|
|
+ as_bctr();
|
|
+}
|
|
+
|
|
+MacroAssembler&
|
|
+MacroAssemblerPPC64::asMasm()
|
|
+{
|
|
+ return *static_cast<MacroAssembler*>(this);
|
|
+}
|
|
+
|
|
+const MacroAssembler&
|
|
+MacroAssemblerPPC64::asMasm() const
|
|
+{
|
|
+ return *static_cast<const MacroAssembler*>(this);
|
|
+}
|
|
+
|
|
+//{{{ check_macroassembler_style
|
|
+// ===============================================================
|
|
+// MacroAssembler high-level usage.
|
|
+
|
|
+void
|
|
+MacroAssembler::flush()
|
|
+{
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Stack manipulation functions.
|
|
+
|
|
+void
|
|
+MacroAssembler::Push(Register reg)
|
|
+{
|
|
+ ma_push(reg);
|
|
+ adjustFrame(int32_t(sizeof(intptr_t)));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::Push(const Imm32 imm)
|
|
+{
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ ma_push(ScratchRegister);
|
|
+ adjustFrame(int32_t(sizeof(intptr_t)));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::Push(const ImmWord imm)
|
|
+{
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ ma_push(ScratchRegister);
|
|
+ adjustFrame(int32_t(sizeof(intptr_t)));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::Push(const ImmPtr imm)
|
|
+{
|
|
+ Push(ImmWord(uintptr_t(imm.value)));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::Push(const ImmGCPtr ptr)
|
|
+{
|
|
+ ma_li(ScratchRegister, ptr);
|
|
+ ma_push(ScratchRegister);
|
|
+ adjustFrame(int32_t(sizeof(intptr_t)));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::Push(FloatRegister f)
|
|
+{
|
|
+ ma_push(f);
|
|
+ adjustFrame(int16_t(sizeof(double))); // Keep stack aligned to double, even if it's a float.
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::Pop(Register reg)
|
|
+{
|
|
+ ma_pop(reg);
|
|
+ adjustFrame(-int16_t(sizeof(intptr_t)));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::Pop(FloatRegister f)
|
|
+{
|
|
+ ma_pop(f);
|
|
+ adjustFrame(-int16_t(sizeof(double)));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::Pop(const ValueOperand& val)
|
|
+{
|
|
+ popValue(val);
|
|
+ adjustFrame(-int16_t(sizeof(Value)));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::PopStackPtr()
|
|
+{
|
|
+ loadPtr(Address(StackPointer, 0), StackPointer);
|
|
+ adjustFrame(-int16_t(sizeof(intptr_t)));
|
|
+}
|
|
+
|
|
+void MacroAssembler::freeStackTo(uint32_t framePushed) {
|
|
+ MOZ_ASSERT(framePushed <= framePushed_);
|
|
+ MOZ_ASSERT(framePushed <= 32767);
|
|
+ as_addi(StackPointer, FramePointer, -(int16_t(framePushed)));
|
|
+ framePushed_ = framePushed;
|
|
+}
|
|
+
|
|
+
|
|
+// ===============================================================
|
|
+// Simple call functions.
|
|
+
|
|
+CodeOffset
|
|
+MacroAssembler::call(Register reg)
|
|
+{
|
|
+ xs_mtctr(reg);
|
|
+ as_bctr(LinkB);
|
|
+ return CodeOffset(currentOffset());
|
|
+}
|
|
+
|
|
+CodeOffset
|
|
+MacroAssembler::call(Label* label)
|
|
+{
|
|
+ ma_bal(label);
|
|
+ return CodeOffset(currentOffset());
|
|
+}
|
|
+
|
|
+CodeOffset
|
|
+MacroAssembler::callWithPatch()
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ // Patched with |patchCall|. Assuming we set JumpImmediateRange correctly
|
|
+ // in Architecture-ppc64.h, we will never get a long branch given to this
|
|
+ // routine, so a naked bl is sufficient.
|
|
+ m_buffer.ensureSpace(sizeof(uint32_t));
|
|
+ as_b(0, RelativeBranch, LinkB); // infinite loop if unpatched
|
|
+ return CodeOffset(currentOffset());
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
|
|
+{
|
|
+ // Account for the bl (the code offset points to the return address).
|
|
+ int32_t offset = calleeOffset - callerOffset + 4;
|
|
+
|
|
+ // Get a handle to the instruction.
|
|
+ Instruction* inst = editSrc(BufferOffset(callerOffset - 4));
|
|
+ MOZ_ASSERT(inst->extractOpcode() == PPC_b);
|
|
+ MOZ_ASSERT(inst[0].encode() & LinkB); // only patch calls!
|
|
+ MOZ_ASSERT(JOffImm26::IsInRange(offset)); // damn well better be
|
|
+
|
|
+ inst->setData(PPC_b | JOffImm26(offset).encode() | LinkB);
|
|
+ FlushICache(inst, sizeof(uint32_t));
|
|
+}
|
|
+
|
|
+CodeOffset
|
|
+MacroAssembler::farJumpWithPatch()
|
|
+{
|
|
+ ADBlock();
|
|
+ CodeOffset farJump;
|
|
+
|
|
+ // Patched with |farJumpWithPatch|. It is guaranteed to be a full stanza.
|
|
+ if (HasPPCISA3()) {
|
|
+ // We can use addpcis to access the PC directly. Use r12 "as expected"
|
|
+ // even though this is not necessarily to ABI-compliant code.
|
|
+ m_buffer.ensureSpace(10 * sizeof(uint32_t));
|
|
+ as_addpcis(SecondScratchReg, 0); // r12 = address of jump
|
|
+
|
|
+ farJump.bind(currentOffset());
|
|
+ // NIP is here (one word after the non-POWER9 version).
|
|
+ ma_liPatchable(ScratchRegister, ImmWord(LabelBase::INVALID_OFFSET));
|
|
+ as_add(SecondScratchReg, SecondScratchReg, ScratchRegister);
|
|
+ xs_mtctr(SecondScratchReg);
|
|
+ as_bctr();
|
|
+ } else {
|
|
+ // We need to do some footwork to get LR since this is PC-relative.
|
|
+ m_buffer.ensureSpace(13 * sizeof(uint32_t));
|
|
+
|
|
+ // Because this whacks LR, we have to save it first. Unfortunately
|
|
+ // we'll need a third scratch register here.
|
|
+ xs_mflr(ThirdScratchReg); // 4 // 48
|
|
+ // Use the special bcl-Always (20,31) form to avoid tainting the BHRB.
|
|
+ xs_bcl_always(4); // 8 // 44
|
|
+ xs_mflr(SecondScratchReg); // 12; LR points here // 40
|
|
+
|
|
+ farJump.bind(currentOffset());
|
|
+ ma_liPatchable(ScratchRegister, ImmWord(LabelBase::INVALID_OFFSET)); // 32
|
|
+ as_add(SecondScratchReg, SecondScratchReg, ScratchRegister); // 36 // 16
|
|
+ xs_mtctr(SecondScratchReg); // 40 // 12
|
|
+ xs_mtlr(ThirdScratchReg); // 44; restore LR // 8
|
|
+ as_bctr(); // 48 // 4
|
|
+ }
|
|
+
|
|
+ return farJump;
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::patchFarJump(CodeOffset farJump, uint32_t targetOffset)
|
|
+{
|
|
+ int32_t offset;
|
|
+
|
|
+ if (HasPPCISA3()) {
|
|
+ // NIP is pointing at the stanza.
|
|
+ offset = targetOffset - farJump.offset();
|
|
+ } else {
|
|
+ // The offset is the patchable stanza, but LR is pointing at the mflr
|
|
+ // right before it.
|
|
+ offset = sizeof(uint32_t) + targetOffset - farJump.offset();
|
|
+ }
|
|
+
|
|
+ // Get a handle to the branch stanza.
|
|
+ Instruction* inst = editSrc(BufferOffset(farJump.offset()));
|
|
+ MOZ_ASSERT(inst->extractOpcode() == PPC_addis);
|
|
+
|
|
+ Assembler::WriteLoad64Instructions(inst, ScratchRegister, (uint64_t)offset);
|
|
+ FlushICache(inst, sizeof(uint32_t) * 5);
|
|
+}
|
|
+
|
|
+CodeOffset
|
|
+MacroAssembler::call(wasm::SymbolicAddress target)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ // This call is very likely to ABI compliant code. Since this is coming
|
|
+ // from Wasm and Wasm Frames sit on the top of the stack where the linkage
|
|
+ // area goes, we need to pull down a dummy ABI stack frame to prevent the
|
|
+ // callee from unwittingly stomping on the Wasm Frame. ShadowStackSpace
|
|
+ // does not fix this; see Architecture-ppc64le.h for a more intemperate
|
|
+ // explanation. We can get away with this in the general case because the
|
|
+ // argument registers have already been calculated relative to the prior
|
|
+ // (unsafe) value of the stack pointer. If it's not to ABI compliant code,
|
|
+ // then we just bloat the stack temporarily and life goes on.
|
|
+ //
|
|
+ // 512 bytes ought to be enough for anybody ...
|
|
+ as_addi(StackPointer, StackPointer, -512);
|
|
+ movePtr(target, CallReg);
|
|
+ // XXX: No current consumer seems to care about the return value.
|
|
+ // Should it be after the call, or after the stack pointer adjustment?
|
|
+ CodeOffset c = call(CallReg);
|
|
+ as_addi(StackPointer, StackPointer, 512);
|
|
+ return c;
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::call(const Address& addr)
|
|
+{
|
|
+ loadPtr(addr, CallReg);
|
|
+ call(CallReg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::call(ImmWord target)
|
|
+{
|
|
+ call(ImmPtr((void*)target.value));
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::call(ImmPtr target)
|
|
+{
|
|
+ BufferOffset bo = m_buffer.nextOffset();
|
|
+ addPendingJump(bo, target, RelocationKind::HARDCODED);
|
|
+ ma_call(target);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::call(JitCode* c)
|
|
+{
|
|
+ BufferOffset bo = m_buffer.nextOffset();
|
|
+ addPendingJump(bo, ImmPtr(c->raw()), RelocationKind::JITCODE);
|
|
+ ma_liPatchable(SecondScratchReg, ImmPtr(c->raw()));
|
|
+ callJitNoProfiler(SecondScratchReg);
|
|
+}
|
|
+
|
|
+CodeOffset
|
|
+MacroAssembler::nopPatchableToCall()
|
|
+{
|
|
+ ADBlock();
|
|
+ as_nop(); // lis
|
|
+ as_nop(); // ori
|
|
+ as_nop(); // rldicr
|
|
+ as_nop(); // oris
|
|
+ as_nop(); // ori
|
|
+ as_nop(); // mtctr
|
|
+ as_nop(); // bctrl
|
|
+ // Even though this isn't a call (yet), treat it like one, and have
|
|
+ // the returned offset match the future return address.
|
|
+ return CodeOffset(currentOffset());
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::patchNopToCall(uint8_t* call, uint8_t* target)
|
|
+{
|
|
+ // This will always be a full call stanza.
|
|
+ Instruction* inst = (Instruction*) call;
|
|
+ inst -= 7; // rewind to the first nop
|
|
+#if DEBUG
|
|
+ if (inst[0].encode() == PPC_nop) {
|
|
+ MOZ_ASSERT(inst[1].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[2].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[3].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[4].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[5].encode() == PPC_nop);
|
|
+ MOZ_ASSERT(inst[6].encode() == PPC_nop);
|
|
+ } else {
|
|
+ // It is permitted to patch a pre-existing call.
|
|
+ MOZ_ASSERT(inst->extractOpcode() == PPC_addis); // lis
|
|
+ MOZ_ASSERT(inst[6].encode() == (PPC_bctr | LinkB)); // bctrl
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ Assembler::WriteLoad64Instructions(inst, SecondScratchReg, (uint64_t) target);
|
|
+ inst[5].makeOp_mtctr(SecondScratchReg);
|
|
+ inst[6].makeOp_bctr(LinkB);
|
|
+
|
|
+ FlushICache(inst, sizeof(uint32_t) * 7);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::patchCallToNop(uint8_t* call)
|
|
+{
|
|
+ // everything be nops now yo
|
|
+ Instruction* inst = (Instruction*) call;
|
|
+ inst -= 7; // rewind to the stanza entry
|
|
+ MOZ_ASSERT(inst->extractOpcode() == PPC_addis); // lis
|
|
+ MOZ_ASSERT(inst[6].encode() == (PPC_bctr | LinkB)); // bctrl
|
|
+
|
|
+ inst[0].makeOp_nop();
|
|
+ inst[1].makeOp_nop();
|
|
+ inst[2].makeOp_nop();
|
|
+ inst[3].makeOp_nop();
|
|
+ inst[4].makeOp_nop();
|
|
+ inst[5].makeOp_nop();
|
|
+ inst[6].makeOp_nop();
|
|
+
|
|
+ FlushICache(inst, sizeof(uint32_t) * 7);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::pushReturnAddress()
|
|
+{
|
|
+ ADBlock();
|
|
+ xs_mflr(ScratchRegister);
|
|
+ as_stdu(ScratchRegister, StackPointer, -8);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::popReturnAddress()
|
|
+{
|
|
+ ADBlock();
|
|
+ as_ld(ScratchRegister, StackPointer, 0);
|
|
+ xs_mtlr(ScratchRegister);
|
|
+ as_addi(StackPointer, StackPointer, 8);
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// Jit Frames.
|
|
+
|
|
+uint32_t
|
|
+MacroAssembler::pushFakeReturnAddress(Register scratch)
|
|
+{
|
|
+ ADBlock();
|
|
+ CodeLabel cl;
|
|
+
|
|
+ ma_li(scratch, &cl);
|
|
+ Push(scratch);
|
|
+ bind(&cl);
|
|
+ uint32_t retAddr = currentOffset();
|
|
+
|
|
+ addCodeLabel(cl);
|
|
+ return retAddr;
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::loadStoreBuffer(Register ptr, Register buffer)
|
|
+{
|
|
+ ma_and(buffer, ptr, Imm32(int32_t(~gc::ChunkMask)));
|
|
+ loadPtr(Address(buffer, gc::ChunkStoreBufferOffset), buffer);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::branchPtrInNurseryChunk(Condition cond, Register ptr, Register temp,
|
|
+ Label* label)
|
|
+{
|
|
+ ADBlock();
|
|
+ MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
|
|
+ MOZ_ASSERT(ptr != temp);
|
|
+ MOZ_ASSERT(ptr != ScratchRegister); // Both may be used internally.
|
|
+ MOZ_ASSERT(ptr != SecondScratchReg);
|
|
+ MOZ_ASSERT(temp != ScratchRegister); // probably unpossible
|
|
+ MOZ_ASSERT(temp != SecondScratchReg);
|
|
+ MOZ_ASSERT(gc::ChunkStoreBufferOffset < 32767);
|
|
+
|
|
+ ma_and(SecondScratchReg, ptr, Imm32(int32_t(~gc::ChunkMask)));
|
|
+ branchPtr(InvertCondition(cond), Address(SecondScratchReg, gc::ChunkStoreBufferOffset),
|
|
+ ImmWord(0), label);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::comment(const char* msg)
|
|
+{
|
|
+ Assembler::comment(msg);
|
|
+}
|
|
+
|
|
+// ===============================================================
|
|
+// WebAssembly
|
|
+
|
|
+FaultingCodeOffset
|
|
+MacroAssembler::wasmTrapInstruction()
|
|
+{
|
|
+ FaultingCodeOffset offset(currentOffset());
|
|
+ // Only Wasm uses this, and it uses it to force a signal that the Wasm
|
|
+ // handler will then intercept. We don't want to use a trap instruction
|
|
+ // because hooking SIGTRAP will interfere with debugging. So we use a
|
|
+ // stop instruction: it was illegal prior to ISA 3.0, and is privileged
|
|
+ // on 3.0+, so it will cause a SIGILL no matter what CPU it's run on.
|
|
+ // Helpfully, SIGILL is exactly what the Wasm signal handler is watching
|
|
+ // for.
|
|
+ as_stop();
|
|
+ return offset;
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input, Register output, bool isSaturating,
|
|
+ Label* oolEntry)
|
|
+{
|
|
+ ADBlock();
|
|
+
|
|
+ // We only care if the conversion is invalid, not if it's inexact.
|
|
+ // Negative zero is treated like positive zero.
|
|
+ // Whack VXCVI.
|
|
+ as_mtfsb0(23);
|
|
+ as_fctiwz(ScratchDoubleReg, input);
|
|
+ // VXCVI is a failure (over/underflow, NaN, etc.)
|
|
+ as_mcrfs(cr0, 5); // reserved - VXSOFT - VXSQRT - VXCVI -> CR0[...SO]
|
|
+ ma_bc(Assembler::SOBit, oolEntry);
|
|
+
|
|
+ // OutOfLineTruncateCheckF32/F64ToI32 -> outOfLineWasmTruncateToInt32Check
|
|
+ moveFromDouble(ScratchDoubleReg, output);
|
|
+ // Clear and sign extend.
|
|
+ as_srawi(output, output, 0);
|
|
+}
|
|
+
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmTruncateFloat32ToInt32(FloatRegister input, Register output, bool isSaturating,
|
|
+ Label* oolEntry)
|
|
+{
|
|
+ wasmTruncateDoubleToInt32(input, output, isSaturating, oolEntry);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::oolWasmTruncateCheckF32ToI32(FloatRegister input, Register output,
|
|
+ TruncFlags flags, wasm::BytecodeOffset off,
|
|
+ Label* rejoin)
|
|
+{
|
|
+ outOfLineWasmTruncateToInt32Check(input, output, MIRType::Float32, flags, rejoin, off);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::oolWasmTruncateCheckF64ToI32(FloatRegister input, Register output,
|
|
+ TruncFlags flags, wasm::BytecodeOffset off,
|
|
+ Label* rejoin)
|
|
+{
|
|
+ outOfLineWasmTruncateToInt32Check(input, output, MIRType::Double, flags, rejoin, off);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::oolWasmTruncateCheckF32ToI64(FloatRegister input, Register64 output,
|
|
+ TruncFlags flags, wasm::BytecodeOffset off,
|
|
+ Label* rejoin)
|
|
+{
|
|
+ outOfLineWasmTruncateToInt64Check(input, output, MIRType::Float32, flags, rejoin, off);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::oolWasmTruncateCheckF64ToI64(FloatRegister input, Register64 output,
|
|
+ TruncFlags flags, wasm::BytecodeOffset off,
|
|
+ Label* rejoin)
|
|
+{
|
|
+ outOfLineWasmTruncateToInt64Check(input, output, MIRType::Double, flags, rejoin, off);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::outOfLineWasmTruncateToInt32Check(FloatRegister input, Register output,
|
|
+ MIRType fromType, TruncFlags flags,
|
|
+ Label* rejoin,
|
|
+ wasm::BytecodeOffset trapOffset)
|
|
+{
|
|
+ ADBlock();
|
|
+ // We must be using a signed truncation or a truncation that is not
|
|
+ // saturating, and the FPSCR VXCVI bit got set indicating an inexact
|
|
+ // conversion or a NaN. Unsigned saturating truncates already "do the
|
|
+ // right thing" with their conversion, so they never end up here.
|
|
+
|
|
+ Label inputIsNaN;
|
|
+ bool isSaturating = flags & TRUNC_SATURATING;
|
|
+
|
|
+ // Test for NaN.
|
|
+ compareFloatingPoint(input, input, Assembler::DoubleUnordered);
|
|
+ ma_bc(Assembler::DoubleUnordered, &inputIsNaN, ShortJump);
|
|
+
|
|
+ if (isSaturating) {
|
|
+ // fctiwz and fctiwuz both saturate to their respective extents, so
|
|
+ // we can rejoin; the output value is correct. Do the work we would
|
|
+ // have done inline if there had been no exception. The scratch
|
|
+ // register still contains the result.
|
|
+ asMasm().moveFromDouble(ScratchDoubleReg, ScratchRegister);
|
|
+ // Don't sign extend.
|
|
+ as_rldicl(output, ScratchRegister, 0, 32); // "clrldi"
|
|
+ // Rejoin. This follows the truncation stanza.
|
|
+ ma_b(rejoin);
|
|
+ } else {
|
|
+ // We must have overflowed.
|
|
+ asMasm().wasmTrap(wasm::Trap::IntegerOverflow, trapOffset);
|
|
+ }
|
|
+
|
|
+ asMasm().bind(&inputIsNaN);
|
|
+ if (isSaturating) {
|
|
+ // A saturated NaN is zero. (fctiwuz does this for us.)
|
|
+ xs_li(output, 0);
|
|
+ ma_b(rejoin);
|
|
+ } else {
|
|
+ asMasm().wasmTrap(wasm::Trap::InvalidConversionToInteger, trapOffset);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::outOfLineWasmTruncateToInt64Check(FloatRegister input, Register64 output,
|
|
+ MIRType fromType, TruncFlags flags,
|
|
+ Label* rejoin,
|
|
+ wasm::BytecodeOffset trapOffset)
|
|
+{
|
|
+ ADBlock();
|
|
+ // We must be using a signed truncation or a truncation that is not
|
|
+ // saturating, and the FPSCR VXCVI bit got set indicating an inexact
|
|
+ // conversion or a NaN. Unsigned saturating truncates already "do the
|
|
+ // right thing" with their conversion, so they never end up here.
|
|
+
|
|
+ Label inputIsNaN;
|
|
+ bool isSaturating = flags & TRUNC_SATURATING;
|
|
+
|
|
+ // Test for NaN.
|
|
+ compareFloatingPoint(input, input, Assembler::DoubleUnordered);
|
|
+ ma_bc(Assembler::DoubleUnordered, &inputIsNaN, ShortJump);
|
|
+
|
|
+ if (isSaturating) {
|
|
+ // fctidz and fctiduz both saturate to their respective extents, so
|
|
+ // we can rejoin; the output value is correct. Do the work we would
|
|
+ // have done inline if there had been no exception. The scratch
|
|
+ // register still contains the result.
|
|
+ asMasm().moveFromDouble(ScratchDoubleReg, output.reg);
|
|
+ ma_b(rejoin);
|
|
+ } else {
|
|
+ // We must have overflowed.
|
|
+ asMasm().wasmTrap(wasm::Trap::IntegerOverflow, trapOffset);
|
|
+ }
|
|
+
|
|
+ asMasm().bind(&inputIsNaN);
|
|
+ if (isSaturating) {
|
|
+ // A saturated NaN is zero. (fctiduz does this for us.)
|
|
+ xs_li(output.reg, 0);
|
|
+ ma_b(rejoin);
|
|
+ } else {
|
|
+ asMasm().wasmTrap(wasm::Trap::InvalidConversionToInteger, trapOffset);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmLoad(const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr,
|
|
+ Register ptrScratch, AnyRegister output)
|
|
+{
|
|
+ ADBlock();
|
|
+ wasmLoadImpl(access, memoryBase, ptr, ptrScratch, output, InvalidReg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmUnalignedLoad(const wasm::MemoryAccessDesc& access, Register memoryBase,
|
|
+ Register ptr, Register ptrScratch, Register output, Register tmp)
|
|
+{
|
|
+ ADBlock();
|
|
+ wasmLoadImpl(access, memoryBase, ptr, ptrScratch, AnyRegister(output), tmp);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmUnalignedLoadFP(const wasm::MemoryAccessDesc& access, Register memoryBase,
|
|
+ Register ptr, Register ptrScratch, FloatRegister output,
|
|
+ Register tmp1)
|
|
+{
|
|
+ ADBlock();
|
|
+ wasmLoadImpl(access, memoryBase, ptr, ptrScratch, AnyRegister(output), tmp1);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmStore(const wasm::MemoryAccessDesc& access, AnyRegister value,
|
|
+ Register memoryBase, Register ptr, Register ptrScratch)
|
|
+{
|
|
+ ADBlock();
|
|
+ wasmStoreImpl(access, value, memoryBase, ptr, ptrScratch, InvalidReg);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmUnalignedStore(const wasm::MemoryAccessDesc& access, Register value,
|
|
+ Register memoryBase, Register ptr, Register ptrScratch,
|
|
+ Register tmp)
|
|
+{
|
|
+ ADBlock();
|
|
+ wasmStoreImpl(access, AnyRegister(value), memoryBase, ptr, ptrScratch, tmp);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmUnalignedStoreFP(const wasm::MemoryAccessDesc& access, FloatRegister floatValue,
|
|
+ Register memoryBase, Register ptr, Register ptrScratch,
|
|
+ Register tmp)
|
|
+{
|
|
+ ADBlock();
|
|
+ wasmStoreImpl(access, AnyRegister(floatValue), memoryBase, ptr, ptrScratch, tmp);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::wasmLoadImpl(const wasm::MemoryAccessDesc& access, Register memoryBase,
|
|
+ Register ptr, Register ptrScratch, AnyRegister output,
|
|
+ Register tmp)
|
|
+{
|
|
+ ADBlock();
|
|
+ access.assertOffsetInGuardPages();
|
|
+ uint32_t offset = access.offset();
|
|
+ FaultingCodeOffset fco;
|
|
+ MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
|
|
+
|
|
+ // Maybe add the offset.
|
|
+ if (offset) {
|
|
+ asMasm().addPtr(Imm32(offset), ptrScratch);
|
|
+ ptr = ptrScratch;
|
|
+ }
|
|
+
|
|
+ unsigned byteSize = access.byteSize();
|
|
+ bool isSigned;
|
|
+ bool isFloat = false;
|
|
+
|
|
+ switch (access.type()) {
|
|
+ case Scalar::Int8: isSigned = true; break;
|
|
+ case Scalar::Uint8: isSigned = false; break;
|
|
+ case Scalar::Int16: isSigned = true; break;
|
|
+ case Scalar::Uint16: isSigned = false; break;
|
|
+ case Scalar::Int32: isSigned = true; break;
|
|
+ case Scalar::Uint32: isSigned = false; break;
|
|
+ case Scalar::Float64: isFloat = true; break;
|
|
+ case Scalar::Float32: isFloat = true; break;
|
|
+ default: MOZ_CRASH("unexpected array type");
|
|
+ }
|
|
+
|
|
+ BaseIndex address(memoryBase, ptr, TimesOne);
|
|
+/*
|
|
+ if (IsUnaligned(access)) {
|
|
+ if (isFloat) {
|
|
+ if (byteSize == 4)
|
|
+ asMasm().loadUnalignedFloat32(access, address, tmp, output.fpu());
|
|
+ else
|
|
+ asMasm().loadUnalignedDouble(access, address, tmp, output.fpu());
|
|
+ } else {
|
|
+ asMasm().ma_load(output.gpr(), address, static_cast<LoadStoreSize>(8 * byteSize), isSigned ? SignExtend : ZeroExtend);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+*/
|
|
+
|
|
+ asMasm().memoryBarrierBefore(access.sync());
|
|
+ if (isFloat) {
|
|
+ if (byteSize == 4)
|
|
+ fco = asMasm().ma_ls(output.fpu(), address);
|
|
+ else
|
|
+ fco = asMasm().ma_ld(output.fpu(), address);
|
|
+ } else {
|
|
+ // This function doesn't handle 64-bit ints, so we should never
|
|
+ // end up in a situation where we have to break a load apart.
|
|
+ MOZ_ASSERT(byteSize < 8);
|
|
+ fco = asMasm().ma_load(output.gpr(), address, static_cast<LoadStoreSize>(8 * byteSize),
|
|
+ isSigned ? SignExtend : ZeroExtend);
|
|
+ }
|
|
+ asMasm().append(access, js::wasm::TrapMachineInsnForLoad(access.byteSize()),
|
|
+ fco);
|
|
+ asMasm().memoryBarrierAfter(access.sync());
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssemblerPPC64::wasmStoreImpl(const wasm::MemoryAccessDesc& access, AnyRegister value,
|
|
+ Register memoryBase, Register ptr, Register ptrScratch,
|
|
+ Register tmp)
|
|
+{
|
|
+ ADBlock();
|
|
+ access.assertOffsetInGuardPages();
|
|
+ uint32_t offset = access.offset();
|
|
+ MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
|
|
+
|
|
+ // Maybe add the offset.
|
|
+ if (offset) {
|
|
+ asMasm().addPtr(Imm32(offset), ptrScratch);
|
|
+ ptr = ptrScratch;
|
|
+ }
|
|
+
|
|
+ unsigned byteSize = access.byteSize();
|
|
+ bool isSigned;
|
|
+ bool isFloat = false;
|
|
+
|
|
+ switch (access.type()) {
|
|
+ case Scalar::Int8: isSigned = true; break;
|
|
+ case Scalar::Uint8: isSigned = false; break;
|
|
+ case Scalar::Int16: isSigned = true; break;
|
|
+ case Scalar::Uint16: isSigned = false; break;
|
|
+ case Scalar::Int32: isSigned = true; break;
|
|
+ case Scalar::Uint32: isSigned = false; break;
|
|
+ case Scalar::Int64: isSigned = true; break;
|
|
+ case Scalar::Float64: isFloat = true; break;
|
|
+ case Scalar::Float32: isFloat = true; break;
|
|
+ default: MOZ_CRASH("unexpected array type");
|
|
+ }
|
|
+
|
|
+ BaseIndex address(memoryBase, ptr, TimesOne);
|
|
+/*
|
|
+ if (IsUnaligned(access)) {
|
|
+ if (isFloat) {
|
|
+ if (byteSize == 4)
|
|
+ asMasm().storeUnalignedFloat32(access, value.fpu(), tmp, address);
|
|
+ else
|
|
+ asMasm().storeUnalignedDouble(access, value.fpu(), tmp, address);
|
|
+ } else {
|
|
+ asMasm().ma_store(value.gpr(), address,
|
|
+ static_cast<LoadStoreSize>(8 * byteSize),
|
|
+ isSigned ? SignExtend : ZeroExtend);
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+*/
|
|
+
|
|
+ asMasm().memoryBarrierBefore(access.sync());
|
|
+ FaultingCodeOffset fco;
|
|
+ if (isFloat) {
|
|
+ // The store instruction will always be last.
|
|
+ if (byteSize == 4)
|
|
+ fco = asMasm().ma_ss(value.fpu(), address);
|
|
+ else
|
|
+ fco = asMasm().ma_sd(value.fpu(), address);
|
|
+ } else {
|
|
+ fco = asMasm().ma_store(value.gpr(), address,
|
|
+ static_cast<LoadStoreSize>(8 * byteSize));
|
|
+/*
|
|
+ if (loadSize & 0x01) {
|
|
+ // Split store emitted.
|
|
+ asMasm().append(access, loadSize - 1);
|
|
+ // The second store is always the last instruction.
|
|
+ asMasm().append(access, asMasm().size() - 4);
|
|
+ } else {
|
|
+ asMasm().append(access, loadSize);
|
|
+ }
|
|
+*/
|
|
+ }
|
|
+ asMasm().append(access, js::wasm::TrapMachineInsnForStore(access.byteSize()), fco);
|
|
+ asMasm().memoryBarrierAfter(access.sync());
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::enterFakeExitFrameForWasm(Register cxreg, Register scratch, ExitFrameType type)
|
|
+{
|
|
+ enterFakeExitFrame(cxreg, scratch, type);
|
|
+}
|
|
+
|
|
+// ========================================================================
|
|
+// Primitive atomic operations.
|
|
+
|
|
+template<typename T>
|
|
+static void
|
|
+CompareExchange(MacroAssembler& masm, const wasm::MemoryAccessDesc* access,
|
|
+Scalar::Type type, Synchronization sync, const T& mem,
|
|
+ Register oldval, Register newval, Register valueTemp, Register offsetTemp,
|
|
+ Register maskTemp, Register output)
|
|
+{
|
|
+ bool signExtend = Scalar::isSignedIntType(type);
|
|
+ unsigned nbytes = Scalar::byteSize(type);
|
|
+
|
|
+ switch (nbytes) {
|
|
+ case 1:
|
|
+ case 2:
|
|
+ break;
|
|
+ case 4:
|
|
+ MOZ_ASSERT(valueTemp == InvalidReg);
|
|
+ MOZ_ASSERT(offsetTemp == InvalidReg);
|
|
+ MOZ_ASSERT(maskTemp == InvalidReg);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH();
|
|
+ }
|
|
+
|
|
+ Label again, end;
|
|
+ masm.computeEffectiveAddress(mem, SecondScratchReg);
|
|
+
|
|
+ if (nbytes == 4) {
|
|
+ masm.memoryBarrierBefore(sync);
|
|
+ masm.bind(&again);
|
|
+
|
|
+ if (access) masm.append(*access, wasm::TrapMachineInsn::Atomic,
|
|
+ FaultingCodeOffset(masm.currentOffset()));
|
|
+ masm.as_lwarx(output, r0, SecondScratchReg);
|
|
+ masm.ma_bc(output, oldval, &end, Assembler::NotEqual, ShortJump);
|
|
+ /* if (access) masm.append(*access, masm.size()); */
|
|
+ masm.as_stwcx(newval, r0, SecondScratchReg);
|
|
+ masm.ma_bc(Assembler::NotEqual, &again, ShortJump);
|
|
+
|
|
+ masm.memoryBarrierAfter(sync);
|
|
+ masm.bind(&end);
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ masm.as_andi_rc(offsetTemp, SecondScratchReg, 3);
|
|
+ masm.subPtr(offsetTemp, SecondScratchReg);
|
|
+#if !MOZ_LITTLE_ENDIAN()
|
|
+ masm.as_xori(offsetTemp, offsetTemp, 3);
|
|
+#endif
|
|
+ masm.x_slwi(offsetTemp, offsetTemp, 3);
|
|
+ masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8)));
|
|
+ masm.as_slw(maskTemp, maskTemp, offsetTemp);
|
|
+ masm.as_nor(maskTemp, maskTemp, maskTemp);
|
|
+
|
|
+ masm.memoryBarrierBefore(sync);
|
|
+
|
|
+ masm.bind(&again);
|
|
+
|
|
+ if (access) masm.append(*access, wasm::TrapMachineInsn::Atomic,
|
|
+ FaultingCodeOffset(masm.currentOffset()));
|
|
+ masm.as_lwarx(ScratchRegister, r0, SecondScratchReg);
|
|
+
|
|
+ masm.as_srw(output, ScratchRegister, offsetTemp);
|
|
+
|
|
+ switch (nbytes) {
|
|
+ case 1:
|
|
+ masm.as_andi_rc(valueTemp, oldval, 0xff);
|
|
+ masm.as_andi_rc(output, output, 0xff);
|
|
+ if (signExtend) {
|
|
+ masm.as_extsb(valueTemp, oldval);
|
|
+ masm.as_extsb(output, output);
|
|
+ }
|
|
+ break;
|
|
+ case 2:
|
|
+ masm.as_andi_rc(valueTemp, oldval, 0xffff);
|
|
+ masm.as_andi_rc(output, output, 0xffff);
|
|
+ if (signExtend) {
|
|
+ masm.as_extsh(valueTemp, oldval);
|
|
+ masm.as_extsh(output, output);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ masm.ma_bc(output, valueTemp, &end, Assembler::NotEqual, ShortJump);
|
|
+
|
|
+ masm.as_slw(valueTemp, newval, offsetTemp);
|
|
+ masm.as_and(ScratchRegister, ScratchRegister, maskTemp);
|
|
+ masm.as_or(ScratchRegister, ScratchRegister, valueTemp);
|
|
+
|
|
+ /* if (access) masm.append(*access, masm.size()); */
|
|
+ masm.as_stwcx(ScratchRegister, r0, SecondScratchReg);
|
|
+ masm.ma_bc(Assembler::NotEqual, &again, ShortJump);
|
|
+
|
|
+ masm.memoryBarrierAfter(sync);
|
|
+ masm.bind(&end);
|
|
+}
|
|
+
|
|
+
|
|
+void
|
|
+MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, const Address& mem,
|
|
+ Register oldval, Register newval, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register output)
|
|
+{
|
|
+ CompareExchange(*this, nullptr, type, sync, mem, oldval, newval, valueTemp, offsetTemp, maskTemp,
|
|
+ output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::compareExchange(Scalar::Type type, Synchronization sync, const BaseIndex& mem,
|
|
+ Register oldval, Register newval, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register output)
|
|
+{
|
|
+ CompareExchange(*this, nullptr, type, sync, mem, oldval, newval, valueTemp, offsetTemp, maskTemp,
|
|
+ output);
|
|
+}
|
|
+void
|
|
+MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access, const Address& mem,
|
|
+ Register oldval, Register newval, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register output)
|
|
+{
|
|
+ CompareExchange(*this, &access, access.type(), access.sync(), mem, oldval, newval, valueTemp, offsetTemp, maskTemp,
|
|
+ output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access, const BaseIndex& mem,
|
|
+ Register oldval, Register newval, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register output)
|
|
+{
|
|
+ CompareExchange(*this, &access, access.type(), access.sync(), mem, oldval, newval, valueTemp, offsetTemp, maskTemp,
|
|
+ output);
|
|
+}
|
|
+
|
|
+
|
|
+template<typename T>
|
|
+static void
|
|
+AtomicExchange(MacroAssembler& masm, const wasm::MemoryAccessDesc* access,
|
|
+Scalar::Type type, Synchronization sync, const T& mem,
|
|
+ Register value, Register valueTemp, Register offsetTemp, Register maskTemp,
|
|
+ Register output)
|
|
+{
|
|
+ bool signExtend = Scalar::isSignedIntType(type);
|
|
+ unsigned nbytes = Scalar::byteSize(type);
|
|
+
|
|
+ switch (nbytes) {
|
|
+ case 1:
|
|
+ case 2:
|
|
+ break;
|
|
+ case 4:
|
|
+ MOZ_ASSERT(valueTemp == InvalidReg);
|
|
+ MOZ_ASSERT(offsetTemp == InvalidReg);
|
|
+ MOZ_ASSERT(maskTemp == InvalidReg);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH();
|
|
+ }
|
|
+
|
|
+ Label again;
|
|
+
|
|
+ masm.computeEffectiveAddress(mem, SecondScratchReg);
|
|
+
|
|
+ if (nbytes == 4) {
|
|
+
|
|
+ masm.memoryBarrierBefore(sync);
|
|
+ masm.bind(&again);
|
|
+
|
|
+ if (access) masm.append(*access, wasm::TrapMachineInsn::Atomic,
|
|
+ FaultingCodeOffset(masm.currentOffset()));
|
|
+ masm.as_lwarx(output, r0, SecondScratchReg);
|
|
+ /* if (access) masm.append(*access, masm.size()); */
|
|
+ masm.as_stwcx(value, r0, SecondScratchReg);
|
|
+ masm.ma_bc(Assembler::NotEqual, &again, ShortJump);
|
|
+
|
|
+ masm.memoryBarrierAfter(sync);
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ masm.as_andi_rc(offsetTemp, SecondScratchReg, 3);
|
|
+ masm.subPtr(offsetTemp, SecondScratchReg);
|
|
+#if !MOZ_LITTLE_ENDIAN()
|
|
+ masm.as_xori(offsetTemp, offsetTemp, 3);
|
|
+#endif
|
|
+ masm.x_sldi(offsetTemp, offsetTemp, 3);
|
|
+ masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8)));
|
|
+ masm.as_sld(maskTemp, maskTemp, offsetTemp);
|
|
+ masm.as_nor(maskTemp, maskTemp, maskTemp);
|
|
+ switch (nbytes) {
|
|
+ case 1:
|
|
+ masm.as_andi_rc(valueTemp, value, 0xff);
|
|
+ break;
|
|
+ case 2:
|
|
+ masm.as_andi_rc(valueTemp, value, 0xffff);
|
|
+ break;
|
|
+ }
|
|
+ masm.as_sld(valueTemp, valueTemp, offsetTemp);
|
|
+ masm.memoryBarrierBefore(sync);
|
|
+ masm.bind(&again);
|
|
+
|
|
+ if (access) masm.append(*access, wasm::TrapMachineInsn::Atomic,
|
|
+ FaultingCodeOffset(masm.currentOffset()));
|
|
+ masm.as_lwarx(output, r0, SecondScratchReg);
|
|
+ masm.as_and(ScratchRegister, output, maskTemp);
|
|
+ masm.as_or(ScratchRegister, ScratchRegister, valueTemp);
|
|
+
|
|
+ /* if (access) masm.append(*access, masm.size()); */
|
|
+ masm.as_stwcx(ScratchRegister, r0, SecondScratchReg);
|
|
+ masm.ma_bc(Assembler::NotEqual, &again, ShortJump);
|
|
+
|
|
+ masm.as_srd(output, output, offsetTemp);
|
|
+
|
|
+ switch (nbytes) {
|
|
+ case 1:
|
|
+ masm.as_andi_rc(output, output, 0xff);
|
|
+ if (signExtend) {
|
|
+ masm.as_extsb(output, output);
|
|
+ }
|
|
+ break;
|
|
+ case 2:
|
|
+ masm.as_andi_rc(output, output, 0xffff);
|
|
+ if (signExtend) {
|
|
+ masm.as_extsh(output, output);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ masm.memoryBarrierAfter(sync);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, const Address& mem,
|
|
+ Register value, Register valueTemp, Register offsetTemp,
|
|
+ Register maskTemp, Register output)
|
|
+{
|
|
+ AtomicExchange(*this, nullptr, type, sync, mem, value, valueTemp, offsetTemp, maskTemp, output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::atomicExchange(Scalar::Type type, Synchronization sync, const BaseIndex& mem,
|
|
+ Register value, Register valueTemp, Register offsetTemp,
|
|
+ Register maskTemp, Register output)
|
|
+{
|
|
+ AtomicExchange(*this, nullptr, type, sync, mem, value, valueTemp, offsetTemp, maskTemp, output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access, const Address& mem,
|
|
+ Register value, Register valueTemp, Register offsetTemp,
|
|
+ Register maskTemp, Register output)
|
|
+{
|
|
+ AtomicExchange(*this, &access, access.type(), access.sync(), mem, value, valueTemp, offsetTemp, maskTemp, output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access, const BaseIndex& mem,
|
|
+ Register value, Register valueTemp, Register offsetTemp,
|
|
+ Register maskTemp, Register output)
|
|
+{
|
|
+ AtomicExchange(*this, &access, access.type(), access.sync(), mem, value, valueTemp, offsetTemp, maskTemp, output);
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+static void
|
|
+AtomicFetchOp(MacroAssembler& masm, const wasm::MemoryAccessDesc* access,
|
|
+Scalar::Type type, Synchronization sync,
|
|
+ AtomicOp op, const T& mem, Register value, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register output)
|
|
+{
|
|
+ bool signExtend = Scalar::isSignedIntType(type);
|
|
+ unsigned nbytes = Scalar::byteSize(type);
|
|
+
|
|
+ switch (nbytes) {
|
|
+ case 1:
|
|
+ case 2:
|
|
+ break;
|
|
+ case 4:
|
|
+ MOZ_ASSERT(valueTemp == InvalidReg);
|
|
+ MOZ_ASSERT(offsetTemp == InvalidReg);
|
|
+ MOZ_ASSERT(maskTemp == InvalidReg);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH();
|
|
+ }
|
|
+
|
|
+ Label again;
|
|
+
|
|
+ masm.computeEffectiveAddress(mem, SecondScratchReg);
|
|
+
|
|
+ if (nbytes == 4) {
|
|
+
|
|
+ masm.memoryBarrierBefore(sync);
|
|
+ masm.bind(&again);
|
|
+
|
|
+ if (access) masm.append(*access, wasm::TrapMachineInsn::Atomic,
|
|
+ FaultingCodeOffset(masm.currentOffset()));
|
|
+ masm.as_lwarx(output, r0, SecondScratchReg);
|
|
+
|
|
+ switch (op) {
|
|
+ case AtomicOp::Add:
|
|
+ masm.as_add(ScratchRegister, output, value);
|
|
+ break;
|
|
+ case AtomicOp::Sub:
|
|
+ masm.as_subf(ScratchRegister, value, output);
|
|
+ break;
|
|
+ case AtomicOp::And:
|
|
+ masm.as_and(ScratchRegister, output, value);
|
|
+ break;
|
|
+ case AtomicOp::Or:
|
|
+ masm.as_or(ScratchRegister, output, value);
|
|
+ break;
|
|
+ case AtomicOp::Xor:
|
|
+ masm.as_xor(ScratchRegister, output, value);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH();
|
|
+ }
|
|
+
|
|
+ /* if (access) masm.append(*access, masm.size()); */
|
|
+ masm.as_stwcx(ScratchRegister, r0, SecondScratchReg);
|
|
+ masm.ma_bc(Assembler::NotEqual, &again, ShortJump);
|
|
+
|
|
+ masm.memoryBarrierAfter(sync);
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+
|
|
+ masm.as_andi_rc(offsetTemp, SecondScratchReg, 3);
|
|
+ masm.subPtr(offsetTemp, SecondScratchReg);
|
|
+#if !MOZ_LITTLE_ENDIAN()
|
|
+ masm.as_xori(offsetTemp, offsetTemp, 3);
|
|
+#endif
|
|
+ masm.x_sldi(offsetTemp, offsetTemp, 3);
|
|
+ masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8)));
|
|
+ masm.as_sld(maskTemp, maskTemp, offsetTemp);
|
|
+ masm.as_nor(maskTemp, maskTemp, maskTemp);
|
|
+
|
|
+ masm.memoryBarrierBefore(sync);
|
|
+
|
|
+ masm.bind(&again);
|
|
+
|
|
+ if (access) masm.append(*access, wasm::TrapMachineInsn::Atomic,
|
|
+ FaultingCodeOffset(masm.currentOffset()));
|
|
+ masm.as_lwarx(ScratchRegister, r0, SecondScratchReg);
|
|
+ masm.as_srd(output, ScratchRegister, offsetTemp);
|
|
+
|
|
+ switch (op) {
|
|
+ case AtomicOp::Add:
|
|
+ masm.as_add(valueTemp, output, value);
|
|
+ break;
|
|
+ case AtomicOp::Sub:
|
|
+ masm.as_subf(valueTemp, value, output);
|
|
+ break;
|
|
+ case AtomicOp::And:
|
|
+ masm.as_and(valueTemp, output, value);
|
|
+ break;
|
|
+ case AtomicOp::Or:
|
|
+ masm.as_or(valueTemp, output, value);
|
|
+ break;
|
|
+ case AtomicOp::Xor:
|
|
+ masm.as_xor(valueTemp, output, value);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH();
|
|
+ }
|
|
+
|
|
+ switch (nbytes) {
|
|
+ case 1:
|
|
+ masm.as_andi_rc(valueTemp, valueTemp, 0xff);
|
|
+ break;
|
|
+ case 2:
|
|
+ masm.as_andi_rc(valueTemp, valueTemp, 0xffff);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ masm.as_sld(valueTemp, valueTemp, offsetTemp);
|
|
+
|
|
+ masm.as_and(ScratchRegister, ScratchRegister, maskTemp);
|
|
+ masm.as_or(ScratchRegister, ScratchRegister, valueTemp);
|
|
+
|
|
+ /* if (access) masm.append(*access, masm.size()); */
|
|
+ masm.as_stwcx(ScratchRegister, r0, SecondScratchReg);
|
|
+ masm.ma_bc(Assembler::NotEqual, &again, ShortJump);
|
|
+
|
|
+ switch (nbytes) {
|
|
+ case 1:
|
|
+ masm.as_andi_rc(output, output, 0xff);
|
|
+ if (signExtend) {
|
|
+ masm.as_extsb(output, output);
|
|
+ }
|
|
+ break;
|
|
+ case 2:
|
|
+ masm.as_andi_rc(output, output, 0xffff);
|
|
+ if (signExtend) {
|
|
+ masm.as_extsh(output, output);
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH();
|
|
+ }
|
|
+
|
|
+ masm.memoryBarrierAfter(sync);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, AtomicOp op,
|
|
+ Register value, const Address& mem, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register output)
|
|
+{
|
|
+ AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp, offsetTemp, maskTemp, output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::atomicFetchOp(Scalar::Type type, Synchronization sync, AtomicOp op,
|
|
+ Register value, const BaseIndex& mem, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register output)
|
|
+{
|
|
+ AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp, offsetTemp, maskTemp, output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, AtomicOp op,
|
|
+ Register value, const Address& mem, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register output)
|
|
+{
|
|
+ AtomicFetchOp(*this, &access, access.type(), access.sync(), op, mem, value, valueTemp, offsetTemp, maskTemp, output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, AtomicOp op,
|
|
+ Register value, const BaseIndex& mem, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register output)
|
|
+{
|
|
+ AtomicFetchOp(*this, &access, access.type(), access.sync(), op, mem, value, valueTemp, offsetTemp, maskTemp, output);
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+static void
|
|
+AtomicEffectOp(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, Scalar::Type type, Synchronization sync, AtomicOp op,
|
|
+ const T& mem, Register value, Register valueTemp, Register offsetTemp, Register maskTemp)
|
|
+{
|
|
+ unsigned nbytes = Scalar::byteSize(type);
|
|
+
|
|
+ switch (nbytes) {
|
|
+ case 1:
|
|
+ case 2:
|
|
+ break;
|
|
+ case 4:
|
|
+ MOZ_ASSERT(valueTemp == InvalidReg);
|
|
+ MOZ_ASSERT(offsetTemp == InvalidReg);
|
|
+ MOZ_ASSERT(maskTemp == InvalidReg);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH();
|
|
+ }
|
|
+
|
|
+ Label again;
|
|
+
|
|
+ masm.computeEffectiveAddress(mem, SecondScratchReg);
|
|
+
|
|
+ if (nbytes == 4) {
|
|
+
|
|
+ masm.memoryBarrierBefore(sync);
|
|
+ masm.bind(&again);
|
|
+
|
|
+ if (access) masm.append(*access, wasm::TrapMachineInsn::Atomic,
|
|
+ FaultingCodeOffset(masm.currentOffset()));
|
|
+ masm.as_lwarx(ScratchRegister, r0, SecondScratchReg);
|
|
+
|
|
+ switch (op) {
|
|
+ case AtomicOp::Add:
|
|
+ masm.as_add(ScratchRegister, ScratchRegister, value);
|
|
+ break;
|
|
+ case AtomicOp::Sub:
|
|
+ masm.as_subf(ScratchRegister, value, ScratchRegister);
|
|
+ break;
|
|
+ case AtomicOp::And:
|
|
+ masm.as_and(ScratchRegister, ScratchRegister, value);
|
|
+ break;
|
|
+ case AtomicOp::Or:
|
|
+ masm.as_or(ScratchRegister, ScratchRegister, value);
|
|
+ break;
|
|
+ case AtomicOp::Xor:
|
|
+ masm.as_xor(ScratchRegister, ScratchRegister, value);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH();
|
|
+ }
|
|
+
|
|
+ /* if (access) masm.append(*access, masm.size()); */
|
|
+ masm.as_stwcx(ScratchRegister, r0, SecondScratchReg);
|
|
+ masm.ma_bc(Assembler::NotEqual, &again, ShortJump);
|
|
+
|
|
+ masm.memoryBarrierAfter(sync);
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ masm.as_andi_rc(offsetTemp, SecondScratchReg, 3);
|
|
+ masm.subPtr(offsetTemp, SecondScratchReg);
|
|
+#if !MOZ_LITTLE_ENDIAN()
|
|
+ masm.as_xori(offsetTemp, offsetTemp, 3);
|
|
+#endif
|
|
+ masm.x_sldi(offsetTemp, offsetTemp, 3);
|
|
+ masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8)));
|
|
+ masm.as_sld(maskTemp, maskTemp, offsetTemp);
|
|
+ masm.as_nor(maskTemp, maskTemp, maskTemp);
|
|
+
|
|
+ masm.memoryBarrierBefore(sync);
|
|
+
|
|
+ masm.bind(&again);
|
|
+
|
|
+ if (access) masm.append(*access, wasm::TrapMachineInsn::Atomic,
|
|
+ FaultingCodeOffset(masm.currentOffset()));
|
|
+ masm.as_lwarx(ScratchRegister, r0, SecondScratchReg);
|
|
+ masm.as_srd(valueTemp, ScratchRegister, offsetTemp);
|
|
+
|
|
+ switch (op) {
|
|
+ case AtomicOp::Add:
|
|
+ masm.as_add(valueTemp, valueTemp, value);
|
|
+ break;
|
|
+ case AtomicOp::Sub:
|
|
+ masm.as_subf(valueTemp, value, valueTemp);
|
|
+ break;
|
|
+ case AtomicOp::And:
|
|
+ masm.as_and(valueTemp, valueTemp, value);
|
|
+ break;
|
|
+ case AtomicOp::Or:
|
|
+ masm.as_or(valueTemp, valueTemp, value);
|
|
+ break;
|
|
+ case AtomicOp::Xor:
|
|
+ masm.as_xor(valueTemp, valueTemp, value);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH();
|
|
+ }
|
|
+
|
|
+ switch (nbytes) {
|
|
+ case 1:
|
|
+ masm.as_andi_rc(valueTemp, valueTemp, 0xff);
|
|
+ break;
|
|
+ case 2:
|
|
+ masm.as_andi_rc(valueTemp, valueTemp, 0xffff);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH();
|
|
+ }
|
|
+
|
|
+ masm.as_sld(valueTemp, valueTemp, offsetTemp);
|
|
+
|
|
+ masm.as_and(ScratchRegister, ScratchRegister, maskTemp);
|
|
+ masm.as_or(ScratchRegister, ScratchRegister, valueTemp);
|
|
+
|
|
+ /* if (access) masm.append(*access, masm.size()); */
|
|
+ masm.as_stwcx(ScratchRegister, r0, SecondScratchReg);
|
|
+ masm.ma_bc(Assembler::NotEqual, &again, ShortJump);
|
|
+
|
|
+ masm.memoryBarrierAfter(sync);
|
|
+}
|
|
+
|
|
+
|
|
+void
|
|
+MacroAssembler::atomicEffectOpJS(Scalar::Type type, Synchronization sync, AtomicOp op,
|
|
+ Register value, const Address& mem, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp)
|
|
+{
|
|
+ AtomicEffectOp(*this, nullptr, type, sync, op, mem, value, valueTemp, offsetTemp, maskTemp);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::atomicEffectOpJS(Scalar::Type type, Synchronization sync, AtomicOp op,
|
|
+ Register value, const BaseIndex& mem, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp)
|
|
+{
|
|
+ AtomicEffectOp(*this, nullptr, type, sync, op, mem, value, valueTemp, offsetTemp, maskTemp);
|
|
+}
|
|
+
|
|
+// ========================================================================
|
|
+// JS atomic operations.
|
|
+
|
|
+template<typename T>
|
|
+static void
|
|
+CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, Synchronization sync,
|
|
+ const T& mem, Register oldval, Register newval, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register temp, AnyRegister output)
|
|
+{
|
|
+ if (arrayType == Scalar::Uint32) {
|
|
+ masm.compareExchange(arrayType, sync, mem, oldval, newval, valueTemp, offsetTemp, maskTemp,
|
|
+ temp);
|
|
+ masm.convertUInt32ToDouble(temp, output.fpu());
|
|
+ } else {
|
|
+ masm.compareExchange(arrayType, sync, mem, oldval, newval, valueTemp, offsetTemp, maskTemp,
|
|
+ output.gpr());
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::compareExchangeJS(Scalar::Type arrayType, Synchronization sync,
|
|
+ const Address& mem, Register oldval, Register newval,
|
|
+ Register valueTemp, Register offsetTemp, Register maskTemp,
|
|
+ Register temp, AnyRegister output)
|
|
+{
|
|
+ CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, valueTemp, offsetTemp, maskTemp,
|
|
+ temp, output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::compareExchangeJS(Scalar::Type arrayType, Synchronization sync,
|
|
+ const BaseIndex& mem, Register oldval, Register newval,
|
|
+ Register valueTemp, Register offsetTemp, Register maskTemp,
|
|
+ Register temp, AnyRegister output)
|
|
+{
|
|
+ CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval,valueTemp, offsetTemp, maskTemp,
|
|
+ temp, output);
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+static void
|
|
+AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, Synchronization sync,
|
|
+ const T& mem, Register value, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register temp, AnyRegister output)
|
|
+{
|
|
+ if (arrayType == Scalar::Uint32) {
|
|
+ masm.atomicExchange(arrayType, sync, mem, value, valueTemp, offsetTemp, maskTemp, temp);
|
|
+ masm.convertUInt32ToDouble(temp, output.fpu());
|
|
+ } else {
|
|
+ masm.atomicExchange(arrayType, sync, mem, value, valueTemp, offsetTemp, maskTemp,
|
|
+ output.gpr());
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, Synchronization sync,
|
|
+ const Address& mem, Register value, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register temp,
|
|
+ AnyRegister output)
|
|
+{
|
|
+ AtomicExchangeJS(*this, arrayType, sync, mem, value, valueTemp, offsetTemp, maskTemp, temp,
|
|
+ output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, Synchronization sync,
|
|
+ const BaseIndex& mem, Register value, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register temp,
|
|
+ AnyRegister output)
|
|
+{
|
|
+ AtomicExchangeJS(*this, arrayType, sync, mem, value, valueTemp, offsetTemp, maskTemp, temp, output);
|
|
+}
|
|
+
|
|
+template<typename T>
|
|
+static void
|
|
+AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, Synchronization sync,
|
|
+ AtomicOp op, Register value, const T& mem, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register temp,
|
|
+ AnyRegister output)
|
|
+{
|
|
+ if (arrayType == Scalar::Uint32) {
|
|
+ masm.atomicFetchOp(arrayType, sync, op, value, mem, valueTemp, offsetTemp, maskTemp, temp);
|
|
+ masm.convertUInt32ToDouble(temp, output.fpu());
|
|
+ } else {
|
|
+ masm.atomicFetchOp(arrayType, sync, op, value, mem, valueTemp, offsetTemp, maskTemp,
|
|
+ output.gpr());
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, Synchronization sync, AtomicOp op,
|
|
+ Register value, const Address& mem, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register temp,
|
|
+ AnyRegister output)
|
|
+{
|
|
+ AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, valueTemp, offsetTemp, maskTemp, temp,
|
|
+ output);
|
|
+}
|
|
+
|
|
+void
|
|
+MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, Synchronization sync, AtomicOp op,
|
|
+ Register value, const BaseIndex& mem, Register valueTemp,
|
|
+ Register offsetTemp, Register maskTemp, Register temp,
|
|
+ AnyRegister output)
|
|
+{
|
|
+ AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, valueTemp, offsetTemp, maskTemp, temp,
|
|
+ output);
|
|
+}
|
|
+
|
|
+// ========================================================================
|
|
+// Spectre Mitigations.
|
|
+
|
|
+void
|
|
+MacroAssembler::speculationBarrier()
|
|
+{
|
|
+ // eieio appears to be the fastest way of defeating Spectre, since its
|
|
+ // memory ordering is sufficient to defeat gadgets and it's less heavy
|
|
+ // than even so-called lwsync. Doo doo doo doo doo, a Spectre gadget ...
|
|
+ // See a real world demonstration in Shen et al, Restricting Control
|
|
+ // Flow During Speculative Execution with Venkman, pp6-7.
|
|
+ as_eieio();
|
|
+ // Go, Gadget, Go!
|
|
+}
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/MacroAssembler-ppc64.h
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/MacroAssembler-ppc64.h Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,1178 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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_ppc64_shared_MacroAssembler_ppc64_shared_h
|
|
+#define jit_ppc64_shared_MacroAssembler_ppc64_shared_h
|
|
+
|
|
+#include "jit/AtomicOp.h"
|
|
+#include "jit/JitFrames.h"
|
|
+#include "jit/MoveResolver.h"
|
|
+#include "jit/ppc64/Assembler-ppc64.h"
|
|
+#include "vm/BytecodeUtil.h"
|
|
+
|
|
+
|
|
+namespace js {
|
|
+namespace jit {
|
|
+
|
|
+enum LoadStoreSize
|
|
+{
|
|
+ SizeByte = 8,
|
|
+ SizeHalfWord = 16,
|
|
+ SizeWord = 32,
|
|
+ SizeDouble = 64
|
|
+};
|
|
+
|
|
+enum LoadStoreExtension
|
|
+{
|
|
+ ZeroExtend = 0,
|
|
+ SignExtend = 1
|
|
+};
|
|
+
|
|
+enum JumpKind
|
|
+{
|
|
+ LongJump = 0,
|
|
+ ShortJump = 1
|
|
+};
|
|
+
|
|
+enum LiFlags
|
|
+{
|
|
+ Li64 = 0,
|
|
+ Li48 = 1,
|
|
+};
|
|
+
|
|
+struct ImmShiftedTag : public ImmWord
|
|
+{
|
|
+ explicit ImmShiftedTag(JSValueShiftedTag shtag)
|
|
+ : ImmWord((uintptr_t)shtag)
|
|
+ { }
|
|
+
|
|
+ explicit ImmShiftedTag(JSValueType type)
|
|
+ : ImmWord(uintptr_t(JSValueShiftedTag(JSVAL_TYPE_TO_SHIFTED_TAG(type))))
|
|
+ { }
|
|
+};
|
|
+
|
|
+struct ImmTag : public Imm32
|
|
+{
|
|
+ ImmTag(JSValueTag mask)
|
|
+ : Imm32(int32_t(mask))
|
|
+ { }
|
|
+};
|
|
+
|
|
+static const int defaultShift = 3;
|
|
+static_assert(1 << defaultShift == sizeof(JS::Value), "The defaultShift is wrong");
|
|
+
|
|
+// See documentation for ScratchTagScope and ScratchTagScopeRelease in
|
|
+// MacroAssembler-x64.h.
|
|
+class ScratchTagScope : public SecondScratchRegisterScope
|
|
+{
|
|
+ public:
|
|
+ ScratchTagScope(MacroAssembler& masm, const ValueOperand&)
|
|
+ : SecondScratchRegisterScope(masm)
|
|
+ {}
|
|
+};
|
|
+
|
|
+class ScratchTagScopeRelease
|
|
+{
|
|
+ ScratchTagScope* ts_;
|
|
+ public:
|
|
+ explicit ScratchTagScopeRelease(ScratchTagScope* ts) : ts_(ts) {
|
|
+ ts_->release();
|
|
+ }
|
|
+ ~ScratchTagScopeRelease() {
|
|
+ ts_->reacquire();
|
|
+ }
|
|
+};
|
|
+
|
|
+static Register CallReg = r12; // XXX
|
|
+
|
|
+class MacroAssemblerPPC64 : public Assembler
|
|
+{
|
|
+ protected:
|
|
+ // Perform a downcast.
|
|
+ MacroAssembler& asMasm();
|
|
+ const MacroAssembler& asMasm() const;
|
|
+
|
|
+/*
|
|
+ Condition ma_cmp(Register rd, Register lhs, Register rhs, Condition c);
|
|
+ Condition ma_cmp(Register rd, Register lhs, Imm32 imm, Condition c);
|
|
+*/
|
|
+ void compareFloatingPoint(FloatRegister lhs, FloatRegister rhs,
|
|
+ DoubleCondition c);
|
|
+
|
|
+/* XXX: TO DO: ma_d* variants are probably superfluous */
|
|
+ public:
|
|
+ void ma_move(Register rd, Register rs);
|
|
+
|
|
+ void ma_li(Register dest, ImmGCPtr ptr);
|
|
+ void ma_li(Register dest, Imm32 imm);
|
|
+ void ma_li(Register dest, Imm64 imm);
|
|
+ void ma_liPatchable(Register dest, Imm32 imm);
|
|
+
|
|
+ void ma_li(Register dest, CodeLabel* label);
|
|
+ void ma_li(Register dest, ImmWord imm);
|
|
+ void ma_li(Register dest, int64_t imm);
|
|
+ void ma_liPatchable(Register dest, ImmPtr imm);
|
|
+ void ma_liPatchable(Register dest, ImmWord imm);
|
|
+
|
|
+ // Shift operations
|
|
+ void ma_sll(Register rd, Register rt, Imm32 shift);
|
|
+ void ma_srl(Register rd, Register rt, Imm32 shift);
|
|
+ void ma_sra(Register rd, Register rt, Imm32 shift);
|
|
+
|
|
+ void ma_sll(Register rd, Register rt, Register shift);
|
|
+
|
|
+ void ma_dsll(Register rd, Register rt, Imm32 shift);
|
|
+ void ma_dsrl(Register rd, Register rt, Imm32 shift);
|
|
+ void ma_dsra(Register rd, Register rt, Imm32 shift);
|
|
+ void ma_dror(Register rd, Register rt, Imm32 shift);
|
|
+ void ma_drol(Register rd, Register rt, Imm32 shift);
|
|
+
|
|
+ void ma_dsll(Register rd, Register rt, Register shift);
|
|
+ void ma_dsrl(Register rd, Register rt, Register shift);
|
|
+ void ma_dsra(Register rd, Register rt, Register shift);
|
|
+
|
|
+ // Negate
|
|
+ void ma_negu(Register rd, Register rs);
|
|
+ void ma_dnegu(Register rd, Register rs);
|
|
+
|
|
+ void ma_not(Register rd, Register rs);
|
|
+
|
|
+ // Bit extract/insert
|
|
+ void ma_ext(Register rt, Register rs, uint16_t pos, uint16_t size);
|
|
+ void ma_ins(Register rt, Register rs, uint16_t pos, uint16_t size);
|
|
+
|
|
+ // and
|
|
+ void ma_and(Register rd, Register rs);
|
|
+ void ma_and(Register rd, Imm32 imm);
|
|
+ void ma_and(Register rd, Register rs, Imm32 imm);
|
|
+
|
|
+ // or
|
|
+ void ma_or(Register rd, Register rs);
|
|
+ void ma_or(Register rd, Imm32 imm);
|
|
+ void ma_or(Register rd, Register rs, Imm32 imm);
|
|
+
|
|
+ // xor
|
|
+ void ma_xor(Register rd, Register rs);
|
|
+ void ma_xor(Register rd, Imm32 imm);
|
|
+ void ma_xor(Register rd, Register rs, Imm32 imm);
|
|
+
|
|
+ void ma_ctz(Register rd, Register rs);
|
|
+
|
|
+ // load
|
|
+ // Power ISA handles unaligned scalar LSU operations in hardware.
|
|
+ FaultingCodeOffset ma_load(Register dest, const BaseIndex& src, LoadStoreSize size = SizeWord,
|
|
+ LoadStoreExtension extension = SignExtend);
|
|
+
|
|
+ // store
|
|
+ FaultingCodeOffset ma_store(Register data, const BaseIndex& dest, LoadStoreSize size = SizeWord);
|
|
+ FaultingCodeOffset ma_store(Imm32 imm, const BaseIndex& dest, LoadStoreSize size = SizeWord);
|
|
+
|
|
+ // arithmetic based ops
|
|
+ // add
|
|
+ void ma_add(Register rd, Register rs, Imm32 imm);
|
|
+ void ma_add(Register rd, Register rs);
|
|
+ void ma_add(Register rd, Imm32 imm);
|
|
+ void ma_addTestCarry(Condition cond, Register rd, Register rs, Register rt, Label* overflow, bool is32 = true);
|
|
+ void ma_addTestCarry(Condition cond, Register rd, Register rs, ImmWord imm, Label* overflow, bool is32 = false);
|
|
+ void ma_addTestCarry(Condition cond, Register rd, Register rs, Imm32 imm, Label* overflow, bool is32 = true);
|
|
+
|
|
+ // subtract
|
|
+ void ma_subu(Register rd, Register rs, Imm32 imm);
|
|
+ void ma_subu(Register rd, Register rs);
|
|
+ void ma_subu(Register rd, Imm32 imm);
|
|
+ void ma_subTestOverflow(Register rd, Register rs, Imm32 imm, Label* overflow, bool is32 = true);
|
|
+
|
|
+ // multiplies. For now, there are only few that we care about.
|
|
+ void ma_mul(Register rd, Register rs, Imm32 imm);
|
|
+ void ma_mul_branch_overflow(Register rd, Register rs, Register rt, Label* overflow);
|
|
+ void ma_mul_branch_overflow(Register rd, Register rs, Imm32 imm, Label* overflow);
|
|
+
|
|
+ /*
|
|
+ // divisions
|
|
+ void ma_div_branch_overflow(Register rd, Register rs, Register rt, Label* overflow);
|
|
+ void ma_div_branch_overflow(Register rd, Register rs, Imm32 imm, Label* overflow);
|
|
+
|
|
+ // fast mod, uses scratch registers, and thus needs to be in the assembler
|
|
+ // implicitly assumes that we can overwrite dest at the beginning of the sequence
|
|
+ void ma_mod_mask(Register src, Register dest, Register hold, Register remain,
|
|
+ int32_t shift, Label* negZero = nullptr);
|
|
+ */
|
|
+
|
|
+ // branches when done from within platform-specific code
|
|
+ void ma_bc(Condition c, Label* l, JumpKind jumpKind = LongJump);
|
|
+ template <typename T>
|
|
+ void ma_bc(CRegisterID cr, T c, Label* l, JumpKind jumpKind = LongJump);
|
|
+
|
|
+ void ma_bc(Register lhs, Register rhs, Label* l, Condition c, JumpKind jumpKind = LongJump);
|
|
+ void ma_bc(Register lhs, Imm32 imm, Label* l, Condition c, JumpKind jumpKind = LongJump);
|
|
+ void ma_bc(Register lhs, ImmPtr imm, Label* l, Condition c, JumpKind jumpKind = LongJump);
|
|
+ void ma_bc(Register lhs, ImmGCPtr imm, Label* l, Condition c, JumpKind jumpKind = LongJump) {
|
|
+ MOZ_ASSERT(lhs != ScratchRegister);
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ ma_bc(lhs, ScratchRegister, l, c, jumpKind);
|
|
+ }
|
|
+ // Explicit compare width
|
|
+ void ma_bc32(Register lhs, Register rhs, Label* l, Condition c, JumpKind jumpKind = LongJump);
|
|
+ void ma_bc64(Register lhs, Imm32 imm, Label* l, Condition c, JumpKind jumpKind = LongJump);
|
|
+
|
|
+ void ma_b(Label* l, JumpKind jumpKind = LongJump);
|
|
+
|
|
+ // FP instructions
|
|
+ void ma_lis(FloatRegister dest, float value);
|
|
+
|
|
+ FaultingCodeOffset ma_sd(FloatRegister ft, Address address);
|
|
+ FaultingCodeOffset ma_ss(FloatRegister ft, Address address);
|
|
+ FaultingCodeOffset ma_sd(FloatRegister src, BaseIndex address);
|
|
+ FaultingCodeOffset ma_ss(FloatRegister src, BaseIndex address);
|
|
+
|
|
+ FaultingCodeOffset ma_ld(FloatRegister ft, Address address);
|
|
+ FaultingCodeOffset ma_ls(FloatRegister ft, Address address);
|
|
+ FaultingCodeOffset ma_ld(FloatRegister dest, const BaseIndex& src);
|
|
+ FaultingCodeOffset ma_ls(FloatRegister dest, const BaseIndex& src);
|
|
+
|
|
+ // FP branches
|
|
+ void ma_bc(DoubleCondition c, FloatRegister lhs, FloatRegister rhs, Label* label,
|
|
+ JumpKind jumpKind = LongJump);
|
|
+ void ma_bc(DoubleCondition c, Label* label, JumpKind jumpKind = LongJump);
|
|
+ //void ma_bc(FloatRegister lhs, FloatRegister rhs, Label* label,
|
|
+ // ConditionRegister cr,
|
|
+ // DoubleCondition c, JumpKind jumpKind = LongJump);
|
|
+
|
|
+ void ma_call(ImmPtr dest);
|
|
+
|
|
+ void ma_spectre_isel(Condition cond, Register lhs, Register rhs);
|
|
+
|
|
+ void ma_jump(ImmPtr dest);
|
|
+
|
|
+ void ma_cmp64(Register lhs, Imm64 rhs, Condition c);
|
|
+ void ma_cmp32(Register lhs, Imm32 rhs, Condition c);
|
|
+ void ma_cmp32(Register lhs, Register rhs, Condition c);
|
|
+ void ma_cmp32(Register lhs, const Address& rhs, Condition c);
|
|
+ void ma_cmp_set(Register dest, Address lhs, Register rhs, Condition c, bool useCmpw = false);
|
|
+ void ma_cmp_set(Register dest, Address lhs, Imm32 rhs, Condition c, bool useCmpw = false);
|
|
+ void ma_cmp_set(Register dest, Address lhs, Imm64 rhs, Condition c, bool useCmpw = true);
|
|
+ void ma_cmp_set(Register dst, Register lhs, Register rhs, Condition c, bool useCmpw = false);
|
|
+ void ma_cmp_set(Register dst, Register lhs, Imm16 imm, Condition c, bool useCmpw = false);
|
|
+ void ma_cmp_set(Register dst, Register lhs, Imm32 imm, Condition c, bool useCmpw = true) {
|
|
+ MOZ_ASSERT(useCmpw);
|
|
+ ma_cmp32(lhs, imm, c);
|
|
+ ma_cmp_set_coda(dst, c);
|
|
+ }
|
|
+ void ma_cmp_set(Register dst, Register lhs, Imm64 imm, Condition c, bool useCmpw = false) {
|
|
+ MOZ_ASSERT(!useCmpw);
|
|
+ ma_cmp64(lhs, imm, c);
|
|
+ ma_cmp_set_coda(dst, c);
|
|
+ }
|
|
+ void ma_cmp_set_coda(Register rd, Condition c);
|
|
+ void ma_cmp_set_double(Register dst, FloatRegister lhs, FloatRegister rhs, DoubleCondition c);
|
|
+
|
|
+ // Evaluate srcDest = minmax<isMax>{Float32,Double}(srcDest, other).
|
|
+ // Handle NaN specially if handleNaN is true.
|
|
+ void minMaxDouble(FloatRegister srcDest, FloatRegister other, bool handleNaN, bool isMax);
|
|
+ void minMaxFloat32(FloatRegister srcDest, FloatRegister other, bool handleNaN, bool isMax);
|
|
+
|
|
+ FaultingCodeOffset loadDouble(const Address& addr, FloatRegister dest);
|
|
+ FaultingCodeOffset loadDouble(const BaseIndex& src, FloatRegister dest);
|
|
+
|
|
+ // Load a float value into a register, then expand it to a double.
|
|
+ void loadFloatAsDouble(const Address& addr, FloatRegister dest);
|
|
+ void loadFloatAsDouble(const BaseIndex& src, FloatRegister dest);
|
|
+
|
|
+ FaultingCodeOffset loadFloat32(const Address& addr, FloatRegister dest);
|
|
+ FaultingCodeOffset loadFloat32(const BaseIndex& src, FloatRegister dest);
|
|
+
|
|
+ void outOfLineWasmTruncateToInt32Check(FloatRegister input, Register output, MIRType fromType,
|
|
+ TruncFlags flags, Label* rejoin,
|
|
+ wasm::BytecodeOffset trapOffset);
|
|
+ void outOfLineWasmTruncateToInt64Check(FloatRegister input, Register64 output, MIRType fromType,
|
|
+ TruncFlags flags, Label* rejoin,
|
|
+ wasm::BytecodeOffset trapOffset);
|
|
+
|
|
+ protected:
|
|
+ void wasmLoadImpl(const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr,
|
|
+ Register ptrScratch, AnyRegister output, Register tmp);
|
|
+ void wasmStoreImpl(const wasm::MemoryAccessDesc& access, AnyRegister value, Register memoryBase,
|
|
+ Register ptr, Register ptrScratch, Register tmp);
|
|
+
|
|
+ public:
|
|
+
|
|
+ // Negate
|
|
+
|
|
+ void ma_dins(Register rt, Register rs, Imm32 pos, Imm32 size);
|
|
+ void ma_dext(Register rt, Register rs, Imm32 pos, Imm32 size);
|
|
+
|
|
+ void ma_dctz(Register rd, Register rs);
|
|
+
|
|
+ // load
|
|
+ FaultingCodeOffset ma_load(Register dest, Address address, LoadStoreSize size = SizeWord,
|
|
+ LoadStoreExtension extension = SignExtend);
|
|
+
|
|
+ // store
|
|
+ FaultingCodeOffset ma_store(Register data, Address address, LoadStoreSize size = SizeWord);
|
|
+
|
|
+ // arithmetic based ops
|
|
+ // add
|
|
+ void ma_addTestOverflow(Register rd, Register rs, Register rt, Label* overflow, bool is32 = true);
|
|
+ void ma_addTestOverflow(Register rd, Register rs, ImmWord imm, Label* overflow, bool is32 = false);
|
|
+ void ma_addTestOverflow(Register rd, Register rs, Imm32 imm, Label* overflow, bool is32 = true);
|
|
+
|
|
+ // neg
|
|
+ void ma_negTestOverflow(Register rd, Label* overflow);
|
|
+
|
|
+ // subtract
|
|
+ void ma_dsubu(Register rd, Register rs, Imm32 imm);
|
|
+ void ma_dsubu(Register rd, Register rs);
|
|
+ void ma_dsubu(Register rd, Imm32 imm);
|
|
+ void ma_subTestOverflow(Register rd, Register rs, Register rt, Label* overflow, bool is32 = true);
|
|
+
|
|
+ // multiplies. For now, there are only few that we care about.
|
|
+ void ma_dmult(Register rs, Imm32 imm);
|
|
+
|
|
+ // stack
|
|
+ void ma_pop(Register r);
|
|
+ void ma_push(Register r);
|
|
+
|
|
+ // branches when done from within PPC64-specific code
|
|
+ void ma_bc(Register lhs, ImmWord imm, Label* l, Condition c, JumpKind jumpKind = LongJump);
|
|
+ void ma_bc(Register lhs, Address addr, Label* l, Condition c, JumpKind jumpKind = LongJump);
|
|
+ void ma_bc(Address addr, Imm32 imm, Label* l, Condition c, JumpKind jumpKind = LongJump);
|
|
+ void ma_bc(Address addr, ImmGCPtr imm, Label* l, Condition c, JumpKind jumpKind = LongJump);
|
|
+ void ma_bc(Address addr, Register rhs, Label* l, Condition c, JumpKind jumpKind = LongJump) {
|
|
+ MOZ_ASSERT(rhs != ScratchRegister);
|
|
+ ma_load(ScratchRegister, addr, SizeDouble);
|
|
+ ma_bc(ScratchRegister, rhs, l, c, jumpKind);
|
|
+ }
|
|
+
|
|
+ void ma_bal(Label* l);
|
|
+
|
|
+ // fp instructions
|
|
+ void ma_lid(FloatRegister dest, double value);
|
|
+
|
|
+ void ma_mv(FloatRegister src, ValueOperand dest);
|
|
+ void ma_mv(ValueOperand src, FloatRegister dest);
|
|
+
|
|
+ void ma_pop(FloatRegister f);
|
|
+ void ma_push(FloatRegister f);
|
|
+
|
|
+ void ma_cmp_set(Register dst, Register lhs, ImmWord imm, Condition c, bool useCmpw = false);
|
|
+ void ma_cmp_set(Register dst, Register lhs, ImmPtr imm, Condition c, bool useCmpw = false);
|
|
+};
|
|
+
|
|
+class MacroAssembler;
|
|
+
|
|
+class MacroAssemblerPPC64Compat : public MacroAssemblerPPC64
|
|
+{
|
|
+ public:
|
|
+ using MacroAssemblerPPC64::call;
|
|
+
|
|
+ MacroAssemblerPPC64Compat()
|
|
+ { }
|
|
+
|
|
+ void convertBoolToInt32(Register source, Register dest);
|
|
+ void convertInt32ToDouble(Register src, FloatRegister dest);
|
|
+ void convertInt32ToDouble(const Address& src, FloatRegister dest);
|
|
+ void convertInt32ToDouble(const BaseIndex& src, FloatRegister dest);
|
|
+ void convertUInt32ToDouble(Register src, FloatRegister dest);
|
|
+ void convertUInt32ToFloat32(Register src, FloatRegister dest);
|
|
+ void convertDoubleToFloat32(FloatRegister src, FloatRegister dest);
|
|
+ void convertDoubleToInt32(FloatRegister src, Register dest, Label* fail,
|
|
+ bool negativeZeroCheck = true);
|
|
+ void convertDoubleToPtr(FloatRegister src, Register dest, Label* fail,
|
|
+ bool negativeZeroCheck = true);
|
|
+ void convertFloat32ToInt32(FloatRegister src, Register dest, Label* fail,
|
|
+ bool negativeZeroCheck = true);
|
|
+
|
|
+ void convertFloat32ToDouble(FloatRegister src, FloatRegister dest);
|
|
+ void convertInt32ToFloat32(Register src, FloatRegister dest);
|
|
+ void convertInt32ToFloat32(const Address& src, FloatRegister dest);
|
|
+
|
|
+ void movq(Register rs, Register rd);
|
|
+
|
|
+ void computeScaledAddress(const BaseIndex& address, Register dest);
|
|
+
|
|
+ inline void computeEffectiveAddress(const Address& address, Register dest) {
|
|
+ ma_add(dest, address.base, Imm32(address.offset));
|
|
+ }
|
|
+
|
|
+ inline void computeEffectiveAddress(const BaseIndex &address, Register dest) {
|
|
+ computeScaledAddress(address, dest);
|
|
+ if (address.offset) {
|
|
+ ma_add(dest, dest, Imm32(address.offset));
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ void j(Condition cond, Label* dest) {
|
|
+ ma_b(dest);
|
|
+ }
|
|
+
|
|
+ void mov(Register src, Register dest) {
|
|
+ if (dest != src)
|
|
+ as_or(dest, src, src);
|
|
+ }
|
|
+ void mov(ImmWord imm, Register dest) {
|
|
+ ma_li(dest, imm);
|
|
+ }
|
|
+ void mov(ImmPtr imm, Register dest) {
|
|
+ mov(ImmWord(uintptr_t(imm.value)), dest);
|
|
+ }
|
|
+ void mov(CodeLabel* label, Register dest) {
|
|
+ ma_li(dest, label);
|
|
+ }
|
|
+ void mov(Register src, Address dest) {
|
|
+ MOZ_CRASH("NYI-IC");
|
|
+ }
|
|
+ void mov(Address src, Register dest) {
|
|
+ MOZ_CRASH("NYI-IC");
|
|
+ }
|
|
+
|
|
+#if(0)
|
|
+ // load: offset to the load instruction obtained by movePatchablePtr().
|
|
+ void writeDataRelocation(ImmGCPtr ptr, BufferOffset load) {
|
|
+ // Raw GC pointer relocations and Value relocations both end up in
|
|
+ // Assembler::TraceDataRelocations.
|
|
+ if (ptr.value) {
|
|
+ if (gc::IsInsideNursery(ptr.value)) {
|
|
+ embedsNurseryPointers_ = true;
|
|
+ }
|
|
+ dataRelocations_.writeUnsigned(load.getOffset());
|
|
+ }
|
|
+ }
|
|
+ void writeDataRelocation(const Value& val, BufferOffset load) {
|
|
+ // Raw GC pointer relocations and Value relocations both end up in
|
|
+ // Assembler::TraceDataRelocations.
|
|
+ if (val.isGCThing()) {
|
|
+ gc::Cell* cell = val.toGCThing();
|
|
+ if (cell && gc::IsInsideNursery(cell)) {
|
|
+ embedsNurseryPointers_ = true;
|
|
+ }
|
|
+ dataRelocations_.writeUnsigned(load.getOffset());
|
|
+ }
|
|
+ }
|
|
+#else
|
|
+ void writeDataRelocation(const Value& val) {
|
|
+ if (val.isGCThing()) {
|
|
+ gc::Cell* cell = val.toGCThing();
|
|
+ if (cell && gc::IsInsideNursery(cell))
|
|
+ embedsNurseryPointers_ = true;
|
|
+ dataRelocations_.writeUnsigned(currentOffset());
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ void hop_skip_nop_jump() {
|
|
+ // Common stanza at the end of these CTR branches.
|
|
+ as_nop();
|
|
+ as_nop();
|
|
+ as_nop(); // branch slot
|
|
+ as_bctr();
|
|
+ }
|
|
+
|
|
+ void jump(Label* label) {
|
|
+ ma_b(label);
|
|
+ }
|
|
+ void jump(ImmPtr ptr) {
|
|
+ m_buffer.ensureSpace(7 * sizeof(uint32_t));
|
|
+ BufferOffset bo = m_buffer.nextOffset();
|
|
+ addPendingJump(bo, ptr, RelocationKind::HARDCODED);
|
|
+ ma_jump(ptr);
|
|
+ }
|
|
+ void jump(Register reg) {
|
|
+ // This could be to any code, so definitely use r12.
|
|
+ if (reg != SecondScratchReg)
|
|
+ as_or(SecondScratchReg, reg, reg); // make r12 == CTR
|
|
+ xs_mtctr(SecondScratchReg); // new dispatch group
|
|
+ hop_skip_nop_jump();
|
|
+ }
|
|
+ void jump(const Address& address) {
|
|
+ loadPtr(address, SecondScratchReg);
|
|
+ xs_mtctr(SecondScratchReg); // new dispatch group
|
|
+ as_nop();
|
|
+ hop_skip_nop_jump();
|
|
+ }
|
|
+ void jump(TrampolinePtr code) {
|
|
+ jump(ImmPtr(code.value));
|
|
+ }
|
|
+ void branch(JitCode* c) {
|
|
+ // This is to Ion code, but we still use r12 anyway.
|
|
+ BufferOffset bo = m_buffer.nextOffset();
|
|
+ addPendingJump(bo, ImmPtr(c->raw()), RelocationKind::JITCODE);
|
|
+ ma_liPatchable(SecondScratchReg, ImmPtr(c->raw()));
|
|
+ xs_mtctr(SecondScratchReg); // new dispatch group
|
|
+ // Keep the branch out of the same dispatch group.
|
|
+ as_nop();
|
|
+ hop_skip_nop_jump();
|
|
+ }
|
|
+ void jump(JitCode* code) {
|
|
+ branch(code);
|
|
+ }
|
|
+ void branch(const Register reg) {
|
|
+ jump(reg);
|
|
+ }
|
|
+
|
|
+ void nop() {
|
|
+ as_nop();
|
|
+ }
|
|
+ void ret() {
|
|
+ // Pop LR and return.
|
|
+ ma_pop(ScratchRegister);
|
|
+ xs_mtlr(ScratchRegister);
|
|
+ as_blr();
|
|
+ }
|
|
+ inline void retn(Imm32 n);
|
|
+ void push(Imm32 imm) {
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ ma_push(ScratchRegister);
|
|
+ }
|
|
+ void push(ImmWord imm) {
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ ma_push(ScratchRegister);
|
|
+ }
|
|
+ void push(ImmGCPtr imm) {
|
|
+ ma_li(ScratchRegister, imm);
|
|
+ ma_push(ScratchRegister);
|
|
+ }
|
|
+ void push(const Address& address) {
|
|
+ loadPtr(address, ScratchRegister);
|
|
+ ma_push(ScratchRegister);
|
|
+ }
|
|
+ void push(Register reg) {
|
|
+ ma_push(reg);
|
|
+ }
|
|
+ void push(FloatRegister reg) {
|
|
+ ma_push(reg);
|
|
+ }
|
|
+ void pop(Register reg) {
|
|
+ ma_pop(reg);
|
|
+ }
|
|
+ void pop(FloatRegister reg) {
|
|
+ ma_pop(reg);
|
|
+ }
|
|
+
|
|
+ CodeOffset toggledJump(Label* label);
|
|
+ CodeOffset toggledCall(JitCode* target, bool enabled);
|
|
+ static size_t ToggledCallSize(uint8_t* code) {
|
|
+ // A full branch stanza plus the oris/b gate.
|
|
+ return 8 * sizeof(uint32_t);
|
|
+ }
|
|
+
|
|
+ CodeOffset pushWithPatch(ImmWord imm) {
|
|
+ CodeOffset offset = movWithPatch(imm, ScratchRegister);
|
|
+ ma_push(ScratchRegister);
|
|
+ return offset;
|
|
+ }
|
|
+
|
|
+ CodeOffset movWithPatch(ImmWord imm, Register dest) {
|
|
+ CodeOffset offset = CodeOffset(currentOffset());
|
|
+ ma_liPatchable(dest, imm);
|
|
+ return offset;
|
|
+ }
|
|
+ CodeOffset movWithPatch(ImmPtr imm, Register dest) {
|
|
+ CodeOffset offset = CodeOffset(currentOffset());
|
|
+ ma_liPatchable(dest, imm);
|
|
+ return offset;
|
|
+ }
|
|
+
|
|
+ void writeCodePointer(CodeLabel* label) {
|
|
+ label->patchAt()->bind(currentOffset());
|
|
+ label->setLinkMode(CodeLabel::RawPointer);
|
|
+ m_buffer.ensureSpace(sizeof(void*));
|
|
+ writeInst(-1);
|
|
+ writeInst(-1);
|
|
+ }
|
|
+
|
|
+ void splitTag(Register src, Register dest) {
|
|
+ x_srdi(dest, src, JSVAL_TAG_SHIFT);
|
|
+ }
|
|
+
|
|
+ void splitTag(const ValueOperand& operand, Register dest) {
|
|
+ splitTag(operand.valueReg(), dest);
|
|
+ }
|
|
+
|
|
+ void splitTagForTest(const ValueOperand& value, ScratchTagScope& tag) {
|
|
+ splitTag(value, tag);
|
|
+ }
|
|
+
|
|
+ // unboxing code
|
|
+ void unboxNonDouble(const ValueOperand& operand, Register dest, JSValueType type) {
|
|
+ unboxNonDouble(operand.valueReg(), dest, type);
|
|
+ }
|
|
+
|
|
+ template <typename T>
|
|
+ void unboxNonDouble(T src, Register dest, JSValueType type) {
|
|
+ MOZ_ASSERT(type != JSVAL_TYPE_DOUBLE);
|
|
+ if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
|
|
+ load32(src, dest);
|
|
+ return;
|
|
+ }
|
|
+ loadPtr(src, dest);
|
|
+ unboxNonDouble(dest, dest, type);
|
|
+ }
|
|
+
|
|
+ void unboxNonDouble(Register src, Register dest, JSValueType type) {
|
|
+ MOZ_ASSERT(type != JSVAL_TYPE_DOUBLE);
|
|
+ if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) {
|
|
+ // This has the effect of clearing bits 32-63 and the tag
|
|
+ // with it, plus extending the sign, all in one operation.
|
|
+ as_srawi(dest, src, 0);
|
|
+ return;
|
|
+ }
|
|
+ // Blank out the tag.
|
|
+ as_rldicl(dest, src, 0, 64-JSVAL_TAG_SHIFT); // "clrldi"
|
|
+ }
|
|
+
|
|
+ template <typename T>
|
|
+ void unboxObjectOrNull(const T& src, Register dest) {
|
|
+ unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
|
|
+ // Assuming this assert is true, we've already cleared that bit
|
|
+ // by clearing the tag. We could also do what ARM does.
|
|
+ static_assert(JS::detail::ValueObjectOrNullBit ==
|
|
+ (uint64_t(0x8) << JSVAL_TAG_SHIFT));
|
|
+ }
|
|
+
|
|
+ void unboxGCThingForGCBarrier(const Address& src, Register dest) {
|
|
+ loadPtr(src, dest);
|
|
+ as_rldicl(dest, dest, 0, 64-JSVAL_TAG_SHIFT); // "clrldi"
|
|
+ }
|
|
+ void unboxGCThingForGCBarrier(const ValueOperand& src, Register dest) {
|
|
+ as_rldicl(dest, src.valueReg(), 0, 64-JSVAL_TAG_SHIFT); // "clrldi"
|
|
+ }
|
|
+
|
|
+ void unboxWasmAnyRefGCThingForGCBarrier(const Address& src, Register dest) {
|
|
+ MOZ_ASSERT(ScratchRegister != dest);
|
|
+ movePtr(ImmWord(wasm::AnyRef::GCThingMask), ScratchRegister);
|
|
+ loadPtr(src, dest);
|
|
+ as_and(dest, dest, ScratchRegister);
|
|
+ }
|
|
+
|
|
+ void getWasmAnyRefGCThingChunk(Register src, Register dest) {
|
|
+ MOZ_ASSERT(src != dest);
|
|
+ movePtr(ImmWord(wasm::AnyRef::GCThingChunkMask), dest);
|
|
+ as_and(dest, dest, src);
|
|
+ }
|
|
+
|
|
+ // Like unboxGCThingForGCBarrier, but loads the GC thing's chunk base.
|
|
+ void getGCThingValueChunk(const Address& src, Register dest) {
|
|
+// ScratchRegisterScope scratch(asMasm());
|
|
+// MOZ_ASSERT(scratch != dest);
|
|
+ MOZ_ASSERT(dest != ScratchRegister);
|
|
+ loadPtr(src, dest);
|
|
+ movePtr(ImmWord(JS::detail::ValueGCThingPayloadChunkMask), ScratchRegister);
|
|
+ as_and(dest, dest, ScratchRegister);
|
|
+ }
|
|
+ void getGCThingValueChunk(const ValueOperand& src, Register dest) {
|
|
+ MOZ_ASSERT(src.valueReg() != dest);
|
|
+ movePtr(ImmWord(JS::detail::ValueGCThingPayloadChunkMask), dest);
|
|
+ as_and(dest, dest, src.valueReg());
|
|
+ }
|
|
+
|
|
+ void unboxInt32(const ValueOperand& operand, Register dest);
|
|
+ void unboxInt32(Register src, Register dest);
|
|
+ void unboxInt32(const Address& src, Register dest);
|
|
+ void unboxInt32(const BaseIndex& src, Register dest);
|
|
+ void unboxBoolean(const ValueOperand& operand, Register dest);
|
|
+ void unboxBoolean(Register src, Register dest);
|
|
+ void unboxBoolean(const Address& src, Register dest);
|
|
+ void unboxBoolean(const BaseIndex& src, Register dest);
|
|
+ void unboxDouble(const ValueOperand& operand, FloatRegister dest);
|
|
+ void unboxDouble(Register src, Register dest);
|
|
+ void unboxDouble(const Address& src, FloatRegister dest);
|
|
+ void unboxDouble(const BaseIndex& src, FloatRegister dest);
|
|
+ void unboxString(const ValueOperand& operand, Register dest);
|
|
+ void unboxString(Register src, Register dest);
|
|
+ void unboxString(const Address& src, Register dest);
|
|
+ void unboxSymbol(const ValueOperand& src, Register dest);
|
|
+ void unboxSymbol(Register src, Register dest);
|
|
+ void unboxSymbol(const Address& src, Register dest);
|
|
+ void unboxBigInt(const ValueOperand& operand, Register dest);
|
|
+ void unboxBigInt(Register src, Register dest);
|
|
+ void unboxBigInt(const Address& src, Register dest);
|
|
+ void unboxObject(const ValueOperand& src, Register dest);
|
|
+ void unboxObject(Register src, Register dest);
|
|
+ void unboxObject(const Address& src, Register dest);
|
|
+ void unboxObject(const BaseIndex& src, Register dest) { unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT); }
|
|
+ void unboxValue(const ValueOperand& src, AnyRegister dest, JSValueType type);
|
|
+ void unboxPrivate(const ValueOperand& src, Register dest);
|
|
+
|
|
+ void notBoolean(const ValueOperand& val) {
|
|
+ as_xori(val.valueReg(), val.valueReg(), 1);
|
|
+ }
|
|
+
|
|
+ // boxing code
|
|
+ void boxDouble(FloatRegister src, const ValueOperand& dest, FloatRegister);
|
|
+ void boxNonDouble(JSValueType type, Register src, const ValueOperand& dest);
|
|
+
|
|
+ // Extended unboxing API. If the payload is already in a register, returns
|
|
+ // that register. Otherwise, provides a move to the given scratch register,
|
|
+ // and returns that.
|
|
+ Register extractObject(const Address& address, Register scratch);
|
|
+ [[nodiscard]] Register extractObject(const ValueOperand& value, Register scratch) {
|
|
+ unboxObject(value, scratch);
|
|
+ return scratch;
|
|
+ }
|
|
+ [[nodiscard]] Register extractString(const ValueOperand& value, Register scratch) {
|
|
+ unboxString(value, scratch);
|
|
+ return scratch;
|
|
+ }
|
|
+ [[nodiscard]] Register extractSymbol(const ValueOperand& value, Register scratch) {
|
|
+ unboxSymbol(value, scratch);
|
|
+ return scratch;
|
|
+ }
|
|
+ [[nodiscard]] Register extractInt32(const ValueOperand& value, Register scratch) {
|
|
+ unboxInt32(value, scratch);
|
|
+ return scratch;
|
|
+ }
|
|
+ [[nodiscard]] Register extractBoolean(const ValueOperand& value, Register scratch) {
|
|
+ unboxBoolean(value, scratch);
|
|
+ return scratch;
|
|
+ }
|
|
+ Register extractTag(const Address& address, Register scratch);
|
|
+ Register extractTag(const BaseIndex& address, Register scratch);
|
|
+ Register extractTag(const ValueOperand& value, Register scratch) {
|
|
+ MOZ_ASSERT(scratch != ScratchRegister);
|
|
+ splitTag(value, scratch);
|
|
+ return scratch;
|
|
+ }
|
|
+
|
|
+ void boolValueToDouble(const ValueOperand& operand, FloatRegister dest);
|
|
+ void int32ValueToDouble(const ValueOperand& operand, FloatRegister dest);
|
|
+ void loadInt32OrDouble(const Address& src, FloatRegister dest);
|
|
+ void loadInt32OrDouble(const BaseIndex& addr, FloatRegister dest);
|
|
+ void loadConstantDouble(double dp, FloatRegister dest);
|
|
+
|
|
+ void boolValueToFloat32(const ValueOperand& operand, FloatRegister dest);
|
|
+ void int32ValueToFloat32(const ValueOperand& operand, FloatRegister dest);
|
|
+ void loadConstantFloat32(float f, FloatRegister dest);
|
|
+
|
|
+ void testNullSet(Condition cond, const ValueOperand& value, Register dest);
|
|
+ void testObjectSet(Condition cond, const ValueOperand& value, Register dest);
|
|
+ void testBigIntSet(Condition cond, const ValueOperand& value, Register dest);
|
|
+ void testNumberSet(Condition cond, const ValueOperand& value, Register dest);
|
|
+ void testStringSet(Condition cond, const ValueOperand& value, Register dest);
|
|
+ void testSymbolSet(Condition cond, const ValueOperand& value, Register dest);
|
|
+ void testBooleanSet(Condition cond, const ValueOperand& value, Register dest);
|
|
+ void testUndefinedSet(Condition cond, const ValueOperand& value, Register dest);
|
|
+
|
|
+ // higher level tag testing code
|
|
+ Address ToPayload(Address value) {
|
|
+ return value;
|
|
+ }
|
|
+
|
|
+ template <typename T>
|
|
+ void loadUnboxedValue(const T& address, MIRType type, AnyRegister dest) {
|
|
+ if (dest.isFloat())
|
|
+ loadInt32OrDouble(address, dest.fpu());
|
|
+ else
|
|
+ unboxNonDouble(address, dest.gpr(), ValueTypeFromMIRType(type));
|
|
+ }
|
|
+
|
|
+ void storeUnboxedPayload(ValueOperand value, BaseIndex address, size_t nbytes, JSValueType type) {
|
|
+ switch (nbytes) {
|
|
+ case 8:
|
|
+ if (type == JSVAL_TYPE_OBJECT)
|
|
+ unboxObjectOrNull(value, SecondScratchReg);
|
|
+ else
|
|
+ unboxNonDouble(value, SecondScratchReg, type);
|
|
+ computeEffectiveAddress(address, ScratchRegister);
|
|
+ as_std(SecondScratchReg, ScratchRegister, 0);
|
|
+ return;
|
|
+ case 4:
|
|
+ store32(value.valueReg(), address);
|
|
+ return;
|
|
+ case 1:
|
|
+ store8(value.valueReg(), address);
|
|
+ return;
|
|
+ default: MOZ_CRASH("Bad payload width");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ void storeUnboxedPayload(ValueOperand value, Address address, size_t nbytes, JSValueType type) {
|
|
+ switch (nbytes) {
|
|
+ case 8:
|
|
+ if (type == JSVAL_TYPE_OBJECT)
|
|
+ unboxObjectOrNull(value, SecondScratchReg);
|
|
+ else
|
|
+ unboxNonDouble(value, SecondScratchReg, type);
|
|
+ storePtr(SecondScratchReg, address);
|
|
+ return;
|
|
+ case 4:
|
|
+ store32(value.valueReg(), address);
|
|
+ return;
|
|
+ case 1:
|
|
+ store8(value.valueReg(), address);
|
|
+ return;
|
|
+ default: MOZ_CRASH("Bad payload width");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ void boxValue(JSValueType type, Register src, Register dest);
|
|
+ void storeValue(ValueOperand val, Operand dst);
|
|
+ void storeValue(ValueOperand val, const BaseIndex& dest);
|
|
+ void storeValue(JSValueType type, Register reg, BaseIndex dest);
|
|
+ void storeValue(ValueOperand val, const Address& dest);
|
|
+ void storeValue(JSValueType type, Register reg, Address dest);
|
|
+ void storeValue(const Value& val, Address dest);
|
|
+ void storeValue(const Value& val, BaseIndex dest);
|
|
+ void storeValue(const Address& src, const Address& dest, Register temp) {
|
|
+ loadPtr(src, temp);
|
|
+ storePtr(temp, dest);
|
|
+ }
|
|
+ void storePrivateValue(Register src, const Address& dest) {
|
|
+ storePtr(src, dest);
|
|
+ }
|
|
+ void storePrivateValue(ImmGCPtr imm, const Address& dest) {
|
|
+ storePtr(imm, dest);
|
|
+ }
|
|
+
|
|
+ void loadValue(Address src, ValueOperand val);
|
|
+ void loadValue(Operand dest, ValueOperand val) {
|
|
+ loadValue(dest.toAddress(), val);
|
|
+ }
|
|
+ void loadValue(const BaseIndex& addr, ValueOperand val);
|
|
+
|
|
+ void loadUnalignedValue(const Address& src, ValueOperand dest) {
|
|
+ loadValue(src, dest);
|
|
+ }
|
|
+
|
|
+ void tagValue(JSValueType type, Register payload, ValueOperand dest);
|
|
+
|
|
+ void pushValue(ValueOperand val);
|
|
+ void popValue(ValueOperand val);
|
|
+ void pushValue(const Value& val) {
|
|
+ if (val.isGCThing()) {
|
|
+ writeDataRelocation(val);
|
|
+ movWithPatch(ImmWord(val.asRawBits()), ScratchRegister);
|
|
+ push(ScratchRegister);
|
|
+ } else {
|
|
+ push(ImmWord(val.asRawBits()));
|
|
+ }
|
|
+ }
|
|
+ void pushValue(JSValueType type, Register reg) {
|
|
+ // Use SecondScratchReg as the temp since boxValue uses ScratchRegister
|
|
+ // for the tag.
|
|
+ boxValue(type, reg, SecondScratchReg);
|
|
+ push(SecondScratchReg);
|
|
+ }
|
|
+ void pushValue(const Address& addr);
|
|
+ void pushValue(const BaseIndex& addr, Register scratch) {
|
|
+ loadValue(addr, ValueOperand(scratch));
|
|
+ pushValue(ValueOperand(scratch));
|
|
+ }
|
|
+
|
|
+ void handleFailureWithHandlerTail(Label* profilerExitTail, Label* bailoutTail);
|
|
+
|
|
+ /////////////////////////////////////////////////////////////////
|
|
+ // Common interface.
|
|
+ /////////////////////////////////////////////////////////////////
|
|
+ public:
|
|
+ // The following functions are exposed for use in platform-shared code.
|
|
+
|
|
+ inline void incrementInt32Value(const Address& addr);
|
|
+
|
|
+ void move32(Imm32 imm, Register dest);
|
|
+ void move32(Register src, Register dest);
|
|
+
|
|
+ void movePtr(Register src, Register dest);
|
|
+ void movePtr(ImmWord imm, Register dest);
|
|
+ void movePtr(ImmPtr imm, Register dest);
|
|
+ void movePtr(wasm::SymbolicAddress imm, Register dest);
|
|
+ void movePtr(ImmGCPtr imm, Register dest);
|
|
+
|
|
+ FaultingCodeOffset load8SignExtend(const Address& address, Register dest);
|
|
+ FaultingCodeOffset load8SignExtend(const BaseIndex& src, Register dest);
|
|
+
|
|
+ FaultingCodeOffset load8ZeroExtend(const Address& address, Register dest);
|
|
+ FaultingCodeOffset load8ZeroExtend(const BaseIndex& src, Register dest);
|
|
+
|
|
+ FaultingCodeOffset load16SignExtend(const Address& address, Register dest);
|
|
+ FaultingCodeOffset load16SignExtend(const BaseIndex& src, Register dest);
|
|
+
|
|
+ template <typename S>
|
|
+ FaultingCodeOffset load16UnalignedSignExtend(const S& src, Register dest) {
|
|
+ return ma_load(dest, src, SizeHalfWord, SignExtend);
|
|
+ }
|
|
+
|
|
+ FaultingCodeOffset load16ZeroExtend(const Address& address, Register dest);
|
|
+ FaultingCodeOffset load16ZeroExtend(const BaseIndex& src, Register dest);
|
|
+
|
|
+ template <typename S>
|
|
+ FaultingCodeOffset load16UnalignedZeroExtend(const S& src, Register dest) {
|
|
+ return ma_load(dest, src, SizeHalfWord, ZeroExtend);
|
|
+ }
|
|
+
|
|
+ FaultingCodeOffset load32(const Address& address, Register dest);
|
|
+ FaultingCodeOffset load32(const BaseIndex& address, Register dest);
|
|
+ FaultingCodeOffset load32ZeroExtend(const Address& address, Register dest);
|
|
+ FaultingCodeOffset load32ZeroExtend(const BaseIndex& address, Register dest);
|
|
+ FaultingCodeOffset load32(AbsoluteAddress address, Register dest);
|
|
+ FaultingCodeOffset load32(wasm::SymbolicAddress address, Register dest);
|
|
+
|
|
+ FaultingCodeOffset load64(const Address& address, Register64 dest) {
|
|
+ return loadPtr(address, dest.reg);
|
|
+ }
|
|
+
|
|
+ template <typename S>
|
|
+ FaultingCodeOffset load32Unaligned(const S& src, Register dest) {
|
|
+ return ma_load(dest, src, SizeWord, SignExtend);
|
|
+ }
|
|
+
|
|
+ FaultingCodeOffset load64(const BaseIndex& address, Register64 dest) {
|
|
+ return loadPtr(address, dest.reg);
|
|
+ }
|
|
+
|
|
+ template <typename S>
|
|
+ FaultingCodeOffset load64Unaligned(const S& src, Register64 dest) {
|
|
+ return ma_load(dest.reg, src, SizeDouble, ZeroExtend);
|
|
+ }
|
|
+
|
|
+ void loadPtr(Register src, Register dest);
|
|
+ FaultingCodeOffset loadPtr(const Address& address, Register dest);
|
|
+ FaultingCodeOffset loadPtr(const BaseIndex& src, Register dest);
|
|
+ FaultingCodeOffset loadPtr(AbsoluteAddress address, Register dest);
|
|
+ FaultingCodeOffset loadPtr(wasm::SymbolicAddress address, Register dest);
|
|
+
|
|
+ void loadPrivate(const Address& address, Register dest);
|
|
+
|
|
+ void loadInt32x1(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void loadInt32x1(const BaseIndex& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void loadInt32x2(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void loadInt32x2(const BaseIndex& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void loadInt32x3(const Address& src, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void loadInt32x3(const BaseIndex& src, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void loadInt32x4(const Address& src, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void storeInt32x1(FloatRegister src, const Address& dest) { MOZ_CRASH("NYI"); }
|
|
+ void storeInt32x1(FloatRegister src, const BaseIndex& dest) { MOZ_CRASH("NYI"); }
|
|
+ void storeInt32x2(FloatRegister src, const Address& dest) { MOZ_CRASH("NYI"); }
|
|
+ void storeInt32x2(FloatRegister src, const BaseIndex& dest) { MOZ_CRASH("NYI"); }
|
|
+ void storeInt32x3(FloatRegister src, const Address& dest) { MOZ_CRASH("NYI"); }
|
|
+ void storeInt32x3(FloatRegister src, const BaseIndex& dest) { MOZ_CRASH("NYI"); }
|
|
+ void storeInt32x4(FloatRegister src, const Address& dest) { MOZ_CRASH("NYI"); }
|
|
+ void loadAlignedSimd128Int(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void storeAlignedSimd128Int(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); }
|
|
+ void loadUnalignedSimd128Int(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void loadUnalignedSimd128Int(const BaseIndex& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void storeUnalignedSimd128Int(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); }
|
|
+ void storeUnalignedSimd128Int(FloatRegister src, BaseIndex addr) { MOZ_CRASH("NYI"); }
|
|
+
|
|
+ void loadFloat32x3(const Address& src, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void loadFloat32x3(const BaseIndex& src, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void loadFloat32x4(const Address& src, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void storeFloat32x4(FloatRegister src, const Address& addr) { MOZ_CRASH("NYI"); }
|
|
+
|
|
+ void loadAlignedSimd128Float(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void storeAlignedSimd128Float(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); }
|
|
+ void loadUnalignedSimd128Float(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void loadUnalignedSimd128Float(const BaseIndex& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
|
+ void storeUnalignedSimd128Float(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); }
|
|
+ void storeUnalignedSimd128Float(FloatRegister src, BaseIndex addr) { MOZ_CRASH("NYI"); }
|
|
+
|
|
+/*
|
|
+ void loadUnalignedDouble(const wasm::MemoryAccessDesc& access, const BaseIndex& src,
|
|
+ Register temp, FloatRegister dest);
|
|
+ void loadUnalignedFloat32(const wasm::MemoryAccessDesc& access, const BaseIndex& src,
|
|
+ Register temp, FloatRegister dest);
|
|
+*/
|
|
+
|
|
+ FaultingCodeOffset store8(Register src, const Address& address);
|
|
+ FaultingCodeOffset store8(Imm32 imm, const Address& address);
|
|
+ FaultingCodeOffset store8(Register src, const BaseIndex& address);
|
|
+ FaultingCodeOffset store8(Imm32 imm, const BaseIndex& address);
|
|
+
|
|
+ FaultingCodeOffset store16(Register src, const Address& address);
|
|
+ FaultingCodeOffset store16(Imm32 imm, const Address& address);
|
|
+ FaultingCodeOffset store16(Register src, const BaseIndex& address);
|
|
+ FaultingCodeOffset store16(Imm32 imm, const BaseIndex& address);
|
|
+
|
|
+ template <typename T>
|
|
+ FaultingCodeOffset store16Unaligned(Register src, const T& dest) {
|
|
+ //ma_store_unaligned(src, dest, SizeHalfWord);
|
|
+ return store16(src, dest);
|
|
+ }
|
|
+
|
|
+ FaultingCodeOffset store32(Register src, AbsoluteAddress address);
|
|
+ FaultingCodeOffset store32(Register src, const Address& address);
|
|
+ FaultingCodeOffset store32(Register src, const BaseIndex& address);
|
|
+ FaultingCodeOffset store32(Imm32 src, const Address& address);
|
|
+ FaultingCodeOffset store32(Imm32 src, const BaseIndex& address);
|
|
+
|
|
+ // NOTE: This will use second scratch on PPC64. Only ARM needs the
|
|
+ // implementation without second scratch.
|
|
+ FaultingCodeOffset store32_NoSecondScratch(Imm32 src, const Address& address) {
|
|
+ return store32(src, address);
|
|
+ }
|
|
+
|
|
+ template <typename T>
|
|
+ FaultingCodeOffset store32Unaligned(Register src, const T& dest) {
|
|
+ //ma_store_unaligned(src, dest, SizeWord);
|
|
+ return store32(src, dest);
|
|
+ }
|
|
+
|
|
+ FaultingCodeOffset store64(Imm64 imm, Address address) {
|
|
+ return storePtr(ImmWord(imm.value), address);
|
|
+ }
|
|
+
|
|
+ FaultingCodeOffset store64(Register64 src, Address address) {
|
|
+ return storePtr(src.reg, address);
|
|
+ }
|
|
+
|
|
+ FaultingCodeOffset store64(Register64 src, const BaseIndex& address) {
|
|
+ return storePtr(src.reg, address);
|
|
+ }
|
|
+
|
|
+ template <typename T>
|
|
+ FaultingCodeOffset store64Unaligned(Register64 src, const T& dest) {
|
|
+ //ma_store_unaligned(src.reg, dest, SizeDouble);
|
|
+ return store64(src, dest);
|
|
+ }
|
|
+
|
|
+ template <typename T> FaultingCodeOffset storePtr(ImmWord imm, T address);
|
|
+ template <typename T> FaultingCodeOffset storePtr(ImmPtr imm, T address);
|
|
+ template <typename T> FaultingCodeOffset storePtr(ImmGCPtr imm, T address);
|
|
+ FaultingCodeOffset storePtr(Register src, const Address& address);
|
|
+ FaultingCodeOffset storePtr(Register src, const BaseIndex& address);
|
|
+ FaultingCodeOffset storePtr(Register src, AbsoluteAddress dest);
|
|
+
|
|
+/*
|
|
+ void storeUnalignedFloat32(const wasm::MemoryAccessDesc& access, FloatRegister src,
|
|
+ Register temp, const BaseIndex& dest);
|
|
+ void storeUnalignedDouble(const wasm::MemoryAccessDesc& access, FloatRegister src,
|
|
+ Register temp, const BaseIndex& dest);
|
|
+*/
|
|
+
|
|
+ void moveDouble(FloatRegister src, FloatRegister dest) {
|
|
+ if (src != dest) as_fmr(dest, src);
|
|
+ }
|
|
+
|
|
+ void zeroDouble(FloatRegister reg) {
|
|
+ if (HasPPCISA3()) {
|
|
+ // This should also work on POWER8.
|
|
+ // It's my favourite mnemonic in the whole instruction set.
|
|
+ // Just say it six times fast. It's positively Asgardian.
|
|
+ as_xxlxor(reg, reg, reg);
|
|
+ } else {
|
|
+ xs_li(ScratchRegister, 0);
|
|
+ moveToDouble(ScratchRegister, reg);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ void moveFromDouble(FloatRegister src, Register dest) {
|
|
+ if (HasPPCISA3()) {
|
|
+ // This should also work on POWER8.
|
|
+ as_mfvsrd(dest, src);
|
|
+ } else {
|
|
+ // Sigh.
|
|
+ as_stfdu(src, StackPointer, -8);
|
|
+ as_ld(dest, StackPointer, 0);
|
|
+ as_addi(StackPointer, StackPointer, 8);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ void moveToDouble(Register src, FloatRegister dest) {
|
|
+ if (HasPPCISA3()) {
|
|
+ // This should also work on POWER8.
|
|
+ as_mtvsrd(dest, src);
|
|
+ } else {
|
|
+ // Sigh.
|
|
+ as_stdu(src, StackPointer, -8);
|
|
+ as_lfd(dest, StackPointer, 0);
|
|
+ as_addi(StackPointer, StackPointer, 8);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ void moveFromFloat32(FloatRegister src, Register dest) {
|
|
+ if (HasPPCISA3()) {
|
|
+ MOZ_ASSERT(src != ScratchDoubleReg);
|
|
+ // Enforce rounding mode 0b00 (round-to-nearest tie-to-even).
|
|
+ as_mtfsfi(7, 0);
|
|
+ // Downconvert prior to processing and splat it into 32-bit singles.
|
|
+ as_xscvdpspn(ScratchDoubleReg, src); // preserve sNaN bit, all equal
|
|
+ as_mfvsrd(dest, ScratchDoubleReg);
|
|
+ // Take off the top word, leaving the float.
|
|
+ as_rldicl(dest, dest, 0, 32); // "clrldi"
|
|
+ } else {
|
|
+ // Sigh.
|
|
+ as_stfsu(src, StackPointer, -4);
|
|
+ as_lwz(dest, StackPointer, 0);
|
|
+ as_addi(StackPointer, StackPointer, 4);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ void moveToFloat32(Register src, FloatRegister dest) {
|
|
+ if (HasPPCISA3()) {
|
|
+ // Splat the 32-bit word as singles throughout the VSR, the
|
|
+ // upconvert to double.
|
|
+ as_mtvsrws(dest, src); // loads into both words of DW 0
|
|
+ as_xscvspdpn(dest, dest); // preserve sNaN bit, both doubles equal
|
|
+// XXX: what if we put an frsp here to get rid of it in the 32->64 upconvert?
|
|
+ } else {
|
|
+ // Sigh.
|
|
+ as_stwu(src, StackPointer, -4);
|
|
+ as_lfs(dest, StackPointer, 0);
|
|
+ as_addi(StackPointer, StackPointer, 4);
|
|
+ }
|
|
+ }
|
|
+ void convertUInt64ToDouble(Register src, FloatRegister dest);
|
|
+
|
|
+ void breakpoint();
|
|
+
|
|
+ void checkStackAlignment();
|
|
+
|
|
+ static void calculateAlignedStackPointer(void** stackPointer);
|
|
+
|
|
+ // If source is a double, load it into dest. If source is int32,
|
|
+ // convert it to double. Else, branch to failure.
|
|
+ void ensureDouble(const ValueOperand& source, FloatRegister dest, Label* failure);
|
|
+
|
|
+ void cmpPtrSet(Assembler::Condition cond, Address lhs, ImmPtr rhs, Register dest);
|
|
+ void cmpPtrSet(Assembler::Condition cond, Register lhs, Address rhs, Register dest);
|
|
+
|
|
+ void cmp32Set(Assembler::Condition cond, Register lhs, Address rhs, Register dest);
|
|
+
|
|
+ protected:
|
|
+ bool buildOOLFakeExitFrame(void* fakeReturnAddr);
|
|
+
|
|
+ void wasmLoadI64Impl(const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr,
|
|
+ Register ptrScratch, Register64 output, Register tmp);
|
|
+ void wasmStoreI64Impl(const wasm::MemoryAccessDesc& access, Register64 value, Register memoryBase,
|
|
+ Register ptr, Register ptrScratch, Register tmp);
|
|
+
|
|
+ public:
|
|
+ CodeOffset labelForPatch() {
|
|
+ return CodeOffset(nextOffset().getOffset());
|
|
+ }
|
|
+
|
|
+ void lea(Operand addr, Register dest) {
|
|
+ ma_add(dest, addr.baseReg(), Imm32(addr.disp()));
|
|
+ }
|
|
+
|
|
+ void abiret() {
|
|
+ as_blr();
|
|
+ }
|
|
+
|
|
+ void moveFloat32(FloatRegister src, FloatRegister dest) {
|
|
+ as_fmr(dest, src);
|
|
+ }
|
|
+
|
|
+/*
|
|
+ void loadWasmGlobalPtr(uint32_t globalDataOffset, Register dest) {
|
|
+ loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, globalArea) + globalDataOffset), dest);
|
|
+ }
|
|
+ void loadWasmPinnedRegsFromTls() {
|
|
+ loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, memoryBase)), HeapReg);
|
|
+ }
|
|
+*/
|
|
+
|
|
+ // Instrumentation for entering and leaving the profiler.
|
|
+ void profilerEnterFrame(Register framePtr, Register scratch);
|
|
+ void profilerExitFrame();
|
|
+};
|
|
+
|
|
+typedef MacroAssemblerPPC64Compat MacroAssemblerSpecific;
|
|
+
|
|
+} // namespace jit
|
|
+} // namespace js
|
|
+
|
|
+#endif /* jit_ppc64_MacroAssembler_ppc64_h */
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/MoveEmitter-ppc64.cpp
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/MoveEmitter-ppc64.cpp Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,366 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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/. */
|
|
+
|
|
+#include "jit/ppc64/MoveEmitter-ppc64.h"
|
|
+
|
|
+#include "jit/MacroAssembler-inl.h"
|
|
+
|
|
+using namespace js;
|
|
+using namespace js::jit;
|
|
+
|
|
+void
|
|
+MoveEmitterPPC64::emit(const MoveResolver& moves)
|
|
+{
|
|
+ if (moves.numCycles()) {
|
|
+ // Reserve stack for cycle resolution
|
|
+ masm.reserveStack(moves.numCycles() * sizeof(double));
|
|
+ pushedAtCycle_ = masm.framePushed();
|
|
+ }
|
|
+
|
|
+ for (size_t i = 0; i < moves.numMoves(); i++)
|
|
+ emit(moves.getMove(i));
|
|
+}
|
|
+
|
|
+Address
|
|
+MoveEmitterPPC64::cycleSlot(uint32_t slot, uint32_t subslot) const
|
|
+{
|
|
+ int32_t offset = masm.framePushed() - pushedAtCycle_;
|
|
+ MOZ_ASSERT(Imm16::IsInSignedRange(offset));
|
|
+ return Address(StackPointer, offset + slot * sizeof(double) + subslot);
|
|
+}
|
|
+
|
|
+int32_t
|
|
+MoveEmitterPPC64::getAdjustedOffset(const MoveOperand& operand)
|
|
+{
|
|
+ MOZ_ASSERT(operand.isMemoryOrEffectiveAddress());
|
|
+ if (operand.base() != StackPointer)
|
|
+ return operand.disp();
|
|
+
|
|
+ // Adjust offset if stack pointer has been moved.
|
|
+ return operand.disp() + masm.framePushed() - pushedAtStart_;
|
|
+}
|
|
+
|
|
+Address
|
|
+MoveEmitterPPC64::getAdjustedAddress(const MoveOperand& operand)
|
|
+{
|
|
+ return Address(operand.base(), getAdjustedOffset(operand));
|
|
+}
|
|
+
|
|
+
|
|
+Register
|
|
+MoveEmitterPPC64::tempReg()
|
|
+{
|
|
+ spilledReg_ = SecondScratchReg;
|
|
+ return SecondScratchReg;
|
|
+}
|
|
+
|
|
+void
|
|
+MoveEmitterPPC64::emitMove(const MoveOperand& from, const MoveOperand& to)
|
|
+{
|
|
+ if (from.isGeneralReg()) {
|
|
+ // Second scratch register should not be moved by MoveEmitter.
|
|
+ MOZ_ASSERT(from.reg() != spilledReg_);
|
|
+
|
|
+ if (to.isGeneralReg())
|
|
+ masm.movePtr(from.reg(), to.reg());
|
|
+ else if (to.isMemory())
|
|
+ masm.storePtr(from.reg(), getAdjustedAddress(to));
|
|
+ else
|
|
+ MOZ_CRASH("Invalid emitMove arguments.");
|
|
+ } else if (from.isMemory()) {
|
|
+ if (to.isGeneralReg()) {
|
|
+ masm.loadPtr(getAdjustedAddress(from), to.reg());
|
|
+ } else if (to.isMemory()) {
|
|
+ masm.loadPtr(getAdjustedAddress(from), tempReg());
|
|
+ masm.storePtr(tempReg(), getAdjustedAddress(to));
|
|
+ } else {
|
|
+ MOZ_CRASH("Invalid emitMove arguments.");
|
|
+ }
|
|
+ } else if (from.isEffectiveAddress()) {
|
|
+ if (to.isGeneralReg()) {
|
|
+ masm.computeEffectiveAddress(getAdjustedAddress(from), to.reg());
|
|
+ } else if (to.isMemory()) {
|
|
+ masm.computeEffectiveAddress(getAdjustedAddress(from), tempReg());
|
|
+ masm.storePtr(tempReg(), getAdjustedAddress(to));
|
|
+ } else {
|
|
+ MOZ_CRASH("Invalid emitMove arguments.");
|
|
+ }
|
|
+ } else {
|
|
+ MOZ_CRASH("Invalid emitMove arguments.");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MoveEmitterPPC64::emitInt32Move(const MoveOperand &from, const MoveOperand &to)
|
|
+{
|
|
+ if (from.isGeneralReg()) {
|
|
+ // Second scratch register should not be moved by MoveEmitter.
|
|
+ MOZ_ASSERT(from.reg() != spilledReg_);
|
|
+
|
|
+ if (to.isGeneralReg())
|
|
+ masm.move32(from.reg(), to.reg());
|
|
+ else if (to.isMemory())
|
|
+ masm.store32(from.reg(), getAdjustedAddress(to));
|
|
+ else
|
|
+ MOZ_CRASH("Invalid emitInt32Move arguments.");
|
|
+ } else if (from.isMemory()) {
|
|
+ if (to.isGeneralReg()) {
|
|
+ masm.load32(getAdjustedAddress(from), to.reg());
|
|
+ } else if (to.isMemory()) {
|
|
+ masm.load32(getAdjustedAddress(from), tempReg());
|
|
+ masm.store32(tempReg(), getAdjustedAddress(to));
|
|
+ } else {
|
|
+ MOZ_CRASH("Invalid emitInt32Move arguments.");
|
|
+ }
|
|
+ } else if (from.isEffectiveAddress()) {
|
|
+ if (to.isGeneralReg()) {
|
|
+ masm.computeEffectiveAddress(getAdjustedAddress(from), to.reg());
|
|
+ } else if (to.isMemory()) {
|
|
+ masm.computeEffectiveAddress(getAdjustedAddress(from), tempReg());
|
|
+ masm.store32(tempReg(), getAdjustedAddress(to));
|
|
+ } else {
|
|
+ MOZ_CRASH("Invalid emitInt32Move arguments.");
|
|
+ }
|
|
+ } else {
|
|
+ MOZ_CRASH("Invalid emitInt32Move arguments.");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MoveEmitterPPC64::emitFloat32Move(const MoveOperand& from, const MoveOperand& to)
|
|
+{
|
|
+ // Don't clobber the temp register if it's the source.
|
|
+ MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchFloat32Reg);
|
|
+ MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchFloat32Reg);
|
|
+
|
|
+ if (from.isFloatReg()) {
|
|
+ if (to.isFloatReg()) {
|
|
+ masm.moveFloat32(from.floatReg(), to.floatReg());
|
|
+ } else if (to.isGeneralReg()) {
|
|
+ // Don't bother handling this case.
|
|
+ MOZ_CRASH("emitFloat32Move -> GPR NYI");
|
|
+ } else {
|
|
+ MOZ_ASSERT(to.isMemory());
|
|
+ masm.storeFloat32(from.floatReg(), getAdjustedAddress(to));
|
|
+ }
|
|
+ } else if (to.isFloatReg()) {
|
|
+ MOZ_ASSERT(from.isMemory());
|
|
+ masm.loadFloat32(getAdjustedAddress(from), to.floatReg());
|
|
+ } else if (to.isGeneralReg()) {
|
|
+ MOZ_ASSERT(from.isMemory());
|
|
+ masm.loadPtr(getAdjustedAddress(from), to.reg());
|
|
+ } else {
|
|
+ MOZ_ASSERT(from.isMemory());
|
|
+ MOZ_ASSERT(to.isMemory());
|
|
+ masm.loadFloat32(getAdjustedAddress(from), ScratchFloat32Reg);
|
|
+ masm.storeFloat32(ScratchFloat32Reg, getAdjustedAddress(to));
|
|
+ }
|
|
+}
|
|
+
|
|
+// This is almost the same.
|
|
+void
|
|
+MoveEmitterPPC64::emitDoubleMove(const MoveOperand& from, const MoveOperand& to)
|
|
+{
|
|
+ // Don't clobber the temp register if it's the source.
|
|
+ MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchFloat32Reg);
|
|
+ MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchFloat32Reg);
|
|
+
|
|
+ if (from.isFloatReg()) {
|
|
+ if (to.isFloatReg()) {
|
|
+ masm.moveDouble(from.floatReg(), to.floatReg());
|
|
+ } else if (to.isGeneralReg()) {
|
|
+ // Maximum bogosity again, dude.
|
|
+ MOZ_CRASH("emitDoubleMove FPR -> GPR NYI");
|
|
+ } else {
|
|
+ MOZ_ASSERT(to.isMemory());
|
|
+ masm.storeDouble(from.floatReg(), getAdjustedAddress(to));
|
|
+ }
|
|
+ } else if (to.isFloatReg()) {
|
|
+ MOZ_ASSERT(from.isMemory());
|
|
+ masm.loadDouble(getAdjustedAddress(from), to.floatReg());
|
|
+ } else if (to.isGeneralReg()) {
|
|
+ // Not handled (yet?)
|
|
+ MOZ_CRASH("emitDoubleMove mem -> GPR NYI");
|
|
+ } else {
|
|
+ MOZ_ASSERT(from.isMemory());
|
|
+ MOZ_ASSERT(to.isMemory());
|
|
+ masm.loadDouble(getAdjustedAddress(from), ScratchDoubleReg);
|
|
+ masm.storeDouble(ScratchDoubleReg, getAdjustedAddress(to));
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MoveEmitterPPC64::breakCycle(const MoveOperand& from, const MoveOperand& to,
|
|
+ MoveOp::Type type, uint32_t slotId)
|
|
+{
|
|
+ // There is some pattern:
|
|
+ // (A -> B)
|
|
+ // (B -> A)
|
|
+ //
|
|
+ // This case handles (A -> B), which we reach first. We save B, then allow
|
|
+ // the original move to continue.
|
|
+ switch (type) {
|
|
+ case MoveOp::FLOAT32:
|
|
+ if (to.isMemory()) {
|
|
+ FloatRegister temp = ScratchFloat32Reg;
|
|
+ masm.loadFloat32(getAdjustedAddress(to), temp);
|
|
+ masm.storeFloat32(temp, cycleSlot(slotId));
|
|
+ } else {
|
|
+ masm.storeFloat32(to.floatReg(), cycleSlot(slotId));
|
|
+ }
|
|
+ break;
|
|
+ case MoveOp::DOUBLE:
|
|
+ if (to.isMemory()) {
|
|
+ FloatRegister temp = ScratchDoubleReg;
|
|
+ masm.loadDouble(getAdjustedAddress(to), temp);
|
|
+ masm.storeDouble(temp, cycleSlot(slotId));
|
|
+ } else {
|
|
+ masm.storeDouble(to.floatReg(), cycleSlot(slotId));
|
|
+ }
|
|
+ break;
|
|
+ case MoveOp::INT32:
|
|
+ if (to.isMemory()) {
|
|
+ Register temp = tempReg();
|
|
+ masm.load32(getAdjustedAddress(to), temp);
|
|
+ masm.store32(temp, cycleSlot(0));
|
|
+ } else {
|
|
+ // Second scratch register should not be moved by MoveEmitter.
|
|
+ MOZ_ASSERT(to.reg() != spilledReg_);
|
|
+ masm.store32(to.reg(), cycleSlot(0));
|
|
+ }
|
|
+ break;
|
|
+ case MoveOp::GENERAL:
|
|
+ if (to.isMemory()) {
|
|
+ Register temp = tempReg();
|
|
+ masm.loadPtr(getAdjustedAddress(to), temp);
|
|
+ masm.storePtr(temp, cycleSlot(0));
|
|
+ } else {
|
|
+ // Second scratch register should not be moved by MoveEmitter.
|
|
+ MOZ_ASSERT(to.reg() != spilledReg_);
|
|
+ masm.storePtr(to.reg(), cycleSlot(0));
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("Unexpected move type");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MoveEmitterPPC64::completeCycle(const MoveOperand& from, const MoveOperand& to,
|
|
+ MoveOp::Type type, uint32_t slotId)
|
|
+{
|
|
+ // There is some pattern:
|
|
+ // (A -> B)
|
|
+ // (B -> A)
|
|
+ //
|
|
+ // This case handles (B -> A), which we reach last. We emit a move from the
|
|
+ // saved value of B, to A.
|
|
+ switch (type) {
|
|
+ case MoveOp::FLOAT32:
|
|
+ if (to.isMemory()) {
|
|
+ FloatRegister temp = ScratchFloat32Reg;
|
|
+ masm.loadFloat32(cycleSlot(slotId), temp);
|
|
+ masm.storeFloat32(temp, getAdjustedAddress(to));
|
|
+ } else {
|
|
+ masm.loadFloat32(cycleSlot(slotId), to.floatReg());
|
|
+ }
|
|
+ break;
|
|
+ case MoveOp::DOUBLE:
|
|
+ if (to.isMemory()) {
|
|
+ FloatRegister temp = ScratchDoubleReg;
|
|
+ masm.loadDouble(cycleSlot(slotId), temp);
|
|
+ masm.storeDouble(temp, getAdjustedAddress(to));
|
|
+ } else {
|
|
+ masm.loadDouble(cycleSlot(slotId), to.floatReg());
|
|
+ }
|
|
+ break;
|
|
+ case MoveOp::INT32:
|
|
+ MOZ_ASSERT(slotId == 0);
|
|
+ if (to.isMemory()) {
|
|
+ Register temp = tempReg();
|
|
+ masm.load32(cycleSlot(0), temp);
|
|
+ masm.store32(temp, getAdjustedAddress(to));
|
|
+ } else {
|
|
+ // Second scratch register should not be moved by MoveEmitter.
|
|
+ MOZ_ASSERT(to.reg() != spilledReg_);
|
|
+ masm.load32(cycleSlot(0), to.reg());
|
|
+ }
|
|
+ break;
|
|
+ case MoveOp::GENERAL:
|
|
+ MOZ_ASSERT(slotId == 0);
|
|
+ if (to.isMemory()) {
|
|
+ Register temp = tempReg();
|
|
+ masm.loadPtr(cycleSlot(0), temp);
|
|
+ masm.storePtr(temp, getAdjustedAddress(to));
|
|
+ } else {
|
|
+ // Second scratch register should not be moved by MoveEmitter.
|
|
+ MOZ_ASSERT(to.reg() != spilledReg_);
|
|
+ masm.loadPtr(cycleSlot(0), to.reg());
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("Unexpected move type");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MoveEmitterPPC64::emit(const MoveOp& move)
|
|
+{
|
|
+ const MoveOperand& from = move.from();
|
|
+ const MoveOperand& to = move.to();
|
|
+
|
|
+ if (move.isCycleEnd() && move.isCycleBegin()) {
|
|
+ // A fun consequence of aliased registers is you can have multiple
|
|
+ // cycles at once, and one can end exactly where another begins.
|
|
+ breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot());
|
|
+ completeCycle(from, to, move.type(), move.cycleEndSlot());
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (move.isCycleEnd()) {
|
|
+ MOZ_ASSERT(inCycle_);
|
|
+ completeCycle(from, to, move.type(), move.cycleEndSlot());
|
|
+ MOZ_ASSERT(inCycle_ > 0);
|
|
+ inCycle_--;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (move.isCycleBegin()) {
|
|
+ breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot());
|
|
+ inCycle_++;
|
|
+ }
|
|
+
|
|
+ switch (move.type()) {
|
|
+ case MoveOp::FLOAT32:
|
|
+ emitFloat32Move(from, to);
|
|
+ break;
|
|
+ case MoveOp::DOUBLE:
|
|
+ emitDoubleMove(from, to);
|
|
+ break;
|
|
+ case MoveOp::INT32:
|
|
+ emitInt32Move(from, to);
|
|
+ break;
|
|
+ case MoveOp::GENERAL:
|
|
+ emitMove(from, to);
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("Unexpected move type");
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+MoveEmitterPPC64::assertDone()
|
|
+{
|
|
+ MOZ_ASSERT(inCycle_ == 0);
|
|
+}
|
|
+
|
|
+void
|
|
+MoveEmitterPPC64::finish()
|
|
+{
|
|
+ assertDone();
|
|
+
|
|
+ masm.freeStack(masm.framePushed() - pushedAtStart_);
|
|
+}
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/MoveEmitter-ppc64.h
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/MoveEmitter-ppc64.h Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,78 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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_ppc64le_MoveEmitter_ppc64le_h
|
|
+#define jit_ppc64le_MoveEmitter_ppc64le_h
|
|
+
|
|
+#include "jit/MacroAssembler.h"
|
|
+#include "jit/MoveResolver.h"
|
|
+
|
|
+namespace js {
|
|
+namespace jit {
|
|
+
|
|
+class MoveEmitterPPC64
|
|
+{
|
|
+ protected:
|
|
+ uint32_t inCycle_;
|
|
+ MacroAssembler& masm;
|
|
+
|
|
+ // Original stack push value.
|
|
+ uint32_t pushedAtStart_;
|
|
+
|
|
+ // These store stack offsets to spill locations, snapshotting
|
|
+ // codegen->framePushed_ at the time they were allocated. They are -1 if no
|
|
+ // stack space has been allocated for that particular spill.
|
|
+ int32_t pushedAtCycle_;
|
|
+ int32_t pushedAtSpill_;
|
|
+
|
|
+ // These are registers that are available for temporary use. They may be
|
|
+ // assigned InvalidReg. If no corresponding spill space has been assigned,
|
|
+ // then these registers do not need to be spilled.
|
|
+ Register spilledReg_;
|
|
+ FloatRegister spilledFloatReg_;
|
|
+
|
|
+ void assertDone();
|
|
+ Register tempReg();
|
|
+ FloatRegister tempFloatReg();
|
|
+ Address cycleSlot(uint32_t slot, uint32_t subslot = 0) const;
|
|
+ int32_t getAdjustedOffset(const MoveOperand& operand);
|
|
+ Address getAdjustedAddress(const MoveOperand& operand);
|
|
+
|
|
+ void emitMove(const MoveOperand& from, const MoveOperand& to);
|
|
+ void emitInt32Move(const MoveOperand& from, const MoveOperand& to);
|
|
+ void emitFloat32Move(const MoveOperand& from, const MoveOperand& to);
|
|
+ void emitDoubleMove(const MoveOperand& from, const MoveOperand& to);
|
|
+ void breakCycle(const MoveOperand& from, const MoveOperand& to,
|
|
+ MoveOp::Type type, uint32_t slot);
|
|
+ void completeCycle(const MoveOperand& from, const MoveOperand& to,
|
|
+ MoveOp::Type type, uint32_t slot);
|
|
+ void emit(const MoveOp& move);
|
|
+
|
|
+ public:
|
|
+ MoveEmitterPPC64(MacroAssembler& masm)
|
|
+ : inCycle_(0),
|
|
+ masm(masm),
|
|
+ pushedAtStart_(masm.framePushed()),
|
|
+ pushedAtCycle_(-1),
|
|
+ pushedAtSpill_(-1),
|
|
+ spilledReg_(InvalidReg),
|
|
+ spilledFloatReg_(InvalidFloatReg)
|
|
+ { }
|
|
+ ~MoveEmitterPPC64() {
|
|
+ assertDone();
|
|
+ }
|
|
+ void emit(const MoveResolver& moves);
|
|
+ void finish();
|
|
+
|
|
+ void setScratchRegister(Register reg) {}
|
|
+};
|
|
+
|
|
+typedef MoveEmitterPPC64 MoveEmitter;
|
|
+
|
|
+} // namespace jit
|
|
+} // namespace js
|
|
+
|
|
+#endif /* jit_ppc64le_MoveEmitter_ppc64le_h */
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/SharedICHelpers-ppc64-inl.h
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/SharedICHelpers-ppc64-inl.h Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,80 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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_ppc64le_SharedICHelpers_ppc64le_inl_h
|
|
+#define jit_ppc64le_SharedICHelpers_ppc64le_inl_h
|
|
+
|
|
+#include "jit/SharedICHelpers.h"
|
|
+
|
|
+#include "jit/MacroAssembler-inl.h"
|
|
+
|
|
+namespace js {
|
|
+namespace jit {
|
|
+
|
|
+inline void EmitBaselineTailCallVM(TrampolinePtr target, MacroAssembler& masm,
|
|
+ uint32_t argSize) {
|
|
+#ifdef DEBUG
|
|
+ Register scratch = R2.scratchReg();
|
|
+
|
|
+ // Compute frame size.
|
|
+ masm.movePtr(FramePointer, scratch);
|
|
+ masm.subPtr(StackPointer, scratch);
|
|
+
|
|
+ // Store frame size without VMFunction arguments for debug assertions.
|
|
+ masm.subPtr(Imm32(argSize), scratch);
|
|
+ Address frameSizeAddr(FramePointer,
|
|
+ BaselineFrame::reverseOffsetOfDebugFrameSize());
|
|
+ masm.store32(scratch, frameSizeAddr);
|
|
+ masm.addPtr(Imm32(argSize), scratch);
|
|
+#endif
|
|
+
|
|
+ // Push frame descriptor and perform the tail call.
|
|
+ // The return address will be pushed by the VM wrapper for compatibility
|
|
+ // with direct calls, but since LR is not a GPR, keep ICTailCallReg current.
|
|
+ masm.xs_mflr(ICTailCallReg);
|
|
+ masm.pushFrameDescriptor(FrameType::BaselineJS);
|
|
+ masm.jump(target);
|
|
+}
|
|
+
|
|
+inline void EmitBaselineCallVM(TrampolinePtr target, MacroAssembler& masm) {
|
|
+ masm.pushFrameDescriptor(FrameType::BaselineStub);
|
|
+ masm.call(target);
|
|
+}
|
|
+
|
|
+inline void EmitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch) {
|
|
+ MOZ_ASSERT(scratch != ICTailCallReg);
|
|
+
|
|
+#ifdef DEBUG
|
|
+ // Compute frame size.
|
|
+ masm.movePtr(FramePointer, scratch);
|
|
+ masm.subPtr(StackPointer, scratch);
|
|
+
|
|
+ Address frameSizeAddr(FramePointer,
|
|
+ BaselineFrame::reverseOffsetOfDebugFrameSize());
|
|
+ masm.store32(scratch, frameSizeAddr);
|
|
+#endif
|
|
+
|
|
+ // Note: when making changes here, don't forget to update
|
|
+ // BaselineStubFrame if needed.
|
|
+
|
|
+ // Push frame descriptor and return address.
|
|
+ masm.xs_mflr(ICTailCallReg); // keep current
|
|
+ masm.PushFrameDescriptor(FrameType::BaselineJS);
|
|
+ masm.Push(ICTailCallReg);
|
|
+
|
|
+ // Save old frame pointer, stack pointer and stub reg.
|
|
+ masm.Push(FramePointer);
|
|
+ masm.movePtr(StackPointer, FramePointer);
|
|
+ masm.Push(ICStubReg);
|
|
+
|
|
+ // Stack should remain aligned.
|
|
+ masm.assertStackAlignment(sizeof(Value), 0);
|
|
+}
|
|
+
|
|
+} // namespace jit
|
|
+} // namespace js
|
|
+
|
|
+#endif /* jit_ppc64le_SharedICHelpers_ppc64le_inl_h */
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/SharedICHelpers-ppc64.h
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/SharedICHelpers-ppc64.h Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,106 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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_ppc64le_SharedICHelpers_ppc64le_h
|
|
+#define jit_ppc64le_SharedICHelpers_ppc64le_h
|
|
+
|
|
+#include "jit/BaselineFrame.h"
|
|
+#include "jit/BaselineIC.h"
|
|
+#include "jit/MacroAssembler.h"
|
|
+#include "jit/SharedICRegisters.h"
|
|
+
|
|
+namespace js {
|
|
+namespace jit {
|
|
+
|
|
+// Distance from sp to the top Value inside an IC stub (no return address on
|
|
+// the stack on Power ISA).
|
|
+static const size_t ICStackValueOffset = 0;
|
|
+
|
|
+struct BaselineStubFrame {
|
|
+ uintptr_t savedFrame;
|
|
+ uintptr_t savedStub;
|
|
+ uintptr_t returnAddress;
|
|
+ uintptr_t descriptor;
|
|
+};
|
|
+
|
|
+inline void
|
|
+EmitRestoreTailCallReg(MacroAssembler& masm)
|
|
+{
|
|
+ // No-op; LR is always the return address.
|
|
+}
|
|
+
|
|
+inline void
|
|
+EmitRepushTailCallReg(MacroAssembler& masm)
|
|
+{
|
|
+ // No-op the second; LR is always the return address.
|
|
+}
|
|
+
|
|
+inline void
|
|
+EmitCallIC(MacroAssembler& masm, CodeOffset* callOffset)
|
|
+{
|
|
+ // The stub pointer must already be in ICStubReg.
|
|
+ // Load stubcode pointer from the ICStub.
|
|
+ // R2 won't be active when we call ICs, so we can use it as scratch.
|
|
+ masm.loadPtr(Address(ICStubReg, ICStub::offsetOfStubCode()), R2.scratchReg());
|
|
+
|
|
+ // Call the stubcode via a direct jump-and-link
|
|
+ masm.call(R2.scratchReg());
|
|
+ *callOffset = CodeOffset(masm.currentOffset());
|
|
+}
|
|
+
|
|
+inline void
|
|
+EmitReturnFromIC(MacroAssembler& masm)
|
|
+{
|
|
+ masm.as_blr();
|
|
+}
|
|
+
|
|
+inline void
|
|
+EmitBaselineLeaveStubFrame(MacroAssembler& masm)
|
|
+{
|
|
+ masm.loadPtr(
|
|
+ Address(FramePointer, BaselineStubFrameLayout::ICStubOffsetFromFP),
|
|
+ ICStubReg);
|
|
+ masm.movePtr(FramePointer, StackPointer);
|
|
+ masm.Pop(FramePointer);
|
|
+
|
|
+ // Load the return address into our GPR "mirror."
|
|
+ masm.Pop(ICTailCallReg);
|
|
+ // Move to LR for branching.
|
|
+ masm.xs_mtlr(ICTailCallReg);
|
|
+
|
|
+ // Discard the frame descriptor.
|
|
+ {
|
|
+ SecondScratchRegisterScope scratch2(masm);
|
|
+ masm.Pop(scratch2);
|
|
+ }
|
|
+}
|
|
+
|
|
+template <typename AddrType>
|
|
+inline void
|
|
+EmitPreBarrier(MacroAssembler& masm, const AddrType& addr, MIRType type)
|
|
+{
|
|
+ // Calls made in the prebarrier may clobber LR, so save it first.
|
|
+ masm.xs_mflr(ScratchRegister);
|
|
+ masm.push(ScratchRegister);
|
|
+ masm.guardedCallPreBarrier(addr, type);
|
|
+ masm.pop(ScratchRegister);
|
|
+ masm.xs_mtlr(ScratchRegister);
|
|
+}
|
|
+
|
|
+inline void
|
|
+EmitStubGuardFailure(MacroAssembler& masm)
|
|
+{
|
|
+ // Load next stub into ICStubReg
|
|
+ masm.loadPtr(Address(ICStubReg, ICCacheIRStub::offsetOfNext()), ICStubReg);
|
|
+
|
|
+ // Return address is already loaded, just jump to the next stubcode.
|
|
+ masm.jump(Address(ICStubReg, ICStub::offsetOfStubCode()));
|
|
+}
|
|
+
|
|
+} // namespace jit
|
|
+} // namespace js
|
|
+
|
|
+#endif /* jit_ppc64le_SharedICHelpers_ppc64le_h */
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/SharedICRegisters-ppc64.h
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/SharedICRegisters-ppc64.h Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,44 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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_ppc64le_SharedICRegisters_ppc64le_h
|
|
+#define jit_ppc64le_SharedICRegisters_ppc64le_h
|
|
+
|
|
+#include "jit/MacroAssembler.h"
|
|
+
|
|
+namespace js {
|
|
+namespace jit {
|
|
+
|
|
+// This is just an alias for the stack pointer currently.
|
|
+static constexpr Register BaselineStackReg = r1;
|
|
+
|
|
+// ValueOperands R0, R1, and R2.
|
|
+// R0 == JSReturnReg, and R2 uses registers not preserved across calls. R1 value
|
|
+// should be preserved across calls.
|
|
+static constexpr ValueOperand R0(r4);
|
|
+static constexpr ValueOperand R1(r15); // non-volatile
|
|
+static constexpr ValueOperand R2(r5);
|
|
+
|
|
+// ICTailCallReg and ICStubReg
|
|
+static constexpr Register ICTailCallReg = r25;
|
|
+static constexpr Register ICStubReg = r7;
|
|
+
|
|
+static constexpr Register ExtractTemp0 = InvalidReg;
|
|
+static constexpr Register ExtractTemp1 = InvalidReg;
|
|
+
|
|
+// Register used internally by the Power Macro Assembler.
|
|
+static constexpr Register BaselineSecondScratchReg = SecondScratchReg;
|
|
+
|
|
+// FloatReg0 must be equal to ReturnFloatReg.
|
|
+static constexpr FloatRegister FloatReg0 = f1;
|
|
+static constexpr FloatRegister FloatReg1 = f2;
|
|
+static constexpr FloatRegister FloatReg2 = f3;
|
|
+static constexpr FloatRegister FloatReg3 = f4;
|
|
+
|
|
+} // namespace jit
|
|
+} // namespace js
|
|
+
|
|
+#endif /* jit_ppc64le_SharedICRegisters_ppc64le_h */
|
|
diff -r 485b15bb4a20 -r 9c245f4665be js/src/jit/ppc64/Trampoline-ppc64.cpp
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/js/src/jit/ppc64/Trampoline-ppc64.cpp Thu Aug 08 21:23:52 2024 -0700
|
|
@@ -0,0 +1,988 @@
|
|
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
+ * 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/. */
|
|
+
|
|
+#include "jit/Bailouts.h"
|
|
+#include "jit/BaselineFrame.h"
|
|
+#include "jit/CalleeToken.h"
|
|
+#include "jit/JitFrames.h"
|
|
+#include "jit/JitRuntime.h"
|
|
+#ifdef JS_ION_PERF
|
|
+# include "jit/PerfSpewer.h"
|
|
+#endif
|
|
+#include "jit/ppc64/SharedICRegisters-ppc64.h"
|
|
+#include "jit/VMFunctions.h"
|
|
+#include "vm/JitActivation.h" // js::jit::JitActivation
|
|
+#include "vm/JSContext.h"
|
|
+
|
|
+#include "jit/MacroAssembler-inl.h"
|
|
+
|
|
+#if DEBUG
|
|
+
|
|
+/* Useful class to print visual guard blocks. */
|
|
+class TrampolineAutoDeBlock
|
|
+{
|
|
+ private:
|
|
+ const char *blockname;
|
|
+
|
|
+ public:
|
|
+ TrampolineAutoDeBlock(const char *name) {
|
|
+ blockname = name;
|
|
+ JitSpew(JitSpew_Codegen, "[[[[[[[[ Trampoline: %s", blockname);
|
|
+ }
|
|
+
|
|
+ ~TrampolineAutoDeBlock() {
|
|
+ JitSpew(JitSpew_Codegen, " Trampoline: %s ]]]]]]]]", blockname);
|
|
+ }
|
|
+};
|
|
+
|
|
+#undef ADBlock
|
|
+#define ADBlock(x) TrampolineAutoDeBlock _adbx(x)
|
|
+
|
|
+#else
|
|
+
|
|
+/* Useful preprocessor macros to completely elide visual guard blocks. */
|
|
+
|
|
+#undef ADBlock
|
|
+#define ADBlock(x) ;
|
|
+
|
|
+#endif
|
|
+
|
|
+using namespace js;
|
|
+using namespace js::jit;
|
|
+
|
|
+// All registers to save and restore. This includes the stack pointer, since we
|
|
+// use the ability to reference register values on the stack by index.
|
|
+static const LiveRegisterSet AllRegs =
|
|
+ LiveRegisterSet(GeneralRegisterSet(Registers::AllMask),
|
|
+ FloatRegisterSet(FloatRegisters::AllMask));
|
|
+
|
|
+// This trampoline adheres to the PowerPC ELF64 ABI.
|
|
+static_assert(sizeof(uintptr_t) == sizeof(uint64_t), "Not 32-bit clean.");
|
|
+
|
|
+/*
|
|
+
|
|
+From http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#FUNC-CALL :
|
|
+
|
|
+"Registers r1 [SP], r14 through r31, and f14 through f31 are nonvolatile."
|
|
+"The condition code register fields CR0, CR1, CR5, CR6, and CR7 are volatile."
|
|
+"Registers r0, r3 through r12, f0 through f13, and the special purpose registers LR, CTR,
|
|
+XER, and FPSCR are volatile."
|
|
+"Furthermore, registers r0, r2, r11, and r12 may be modified by cross-module calls."
|
|
+
|
|
+Figure 3-17. Stack Frame Organization
|
|
+(marked up with our values)
|
|
+
|
|
+High Address
|
|
+
|
|
+ +-> Back chain (SP + 416)
|
|
+ | Floating point register save area (SP + 272) (18dw here for f14-f31)
|
|
+ | General register save area (SP + 128) (18dw here for r14-r31)
|
|
+ | VRSAVE save word (32 bits) (SP + 124)
|
|
+ | Alignment padding (4 or 12 bytes) (SP + 112) (12b here)
|
|
+ | Vector register save area (quadword aligned) (SP + 112) (not used)
|
|
+ | Local variable space (SP + 112) (not used)
|
|
+ | Parameter save area (SP + 48) (fixed size minimum 8dw here)
|
|
+ | TOC save area (SP + 40)
|
|
+ | link editor doubleword (SP + 32)
|
|
+ | compiler doubleword (SP + 24)
|
|
+ | LR save area (SP + 16)
|
|
+ | CR save area (SP + 8)
|
|
+SP ---> +-- Back chain (SP + 0)
|
|
+
|
|
+Low Address
|
|
+
|
|
+"The stack pointer shall maintain quadword alignment."
|
|
+
|
|
+*/
|
|
+
|
|
+struct EnterJITRegs
|
|
+{
|
|
+// SP + 0
|
|
+ uint64_t sp;
|
|
+ uint64_t cr;
|
|
+ uint64_t lr;
|
|
+
|
|
+ uint64_t comp;
|
|
+ uint64_t le;
|
|
+ uint64_t toc;
|
|
+
|
|
+ uint64_t parameters[7];
|
|
+
|
|
+// SP + 112
|
|
+// We cheat here: this is a safe place to store our old VP.
|
|
+// In the ABI definition this is just padding.
|
|
+ uint64_t savedvp;
|
|
+ uint32_t padding;
|
|
+ uint32_t vrsave;
|
|
+
|
|
+// SP + 128
|
|
+ uint64_t r14;
|
|
+ uint64_t r15;
|
|
+ uint64_t r16;
|
|
+ uint64_t r17;
|
|
+ uint64_t r18;
|
|
+ uint64_t r19;
|
|
+ uint64_t r20;
|
|
+ uint64_t r21;
|
|
+ uint64_t r22;
|
|
+ uint64_t r23;
|
|
+ uint64_t r24;
|
|
+ uint64_t r25;
|
|
+ uint64_t r26;
|
|
+ uint64_t r27;
|
|
+ uint64_t r28;
|
|
+ uint64_t r29;
|
|
+ uint64_t r30;
|
|
+ uint64_t r31;
|
|
+
|
|
+ double f14;
|
|
+ double f15;
|
|
+ double f16;
|
|
+ double f17;
|
|
+ double f18;
|
|
+ double f19;
|
|
+ double f20;
|
|
+ double f21;
|
|
+ double f22;
|
|
+ double f23;
|
|
+ double f24;
|
|
+ double f25;
|
|
+ double f26;
|
|
+ double f27;
|
|
+ double f28;
|
|
+ double f29;
|
|
+ double f30;
|
|
+ double f31;
|
|
+
|
|
+ uint64_t mo_paddin; // make size 16-byte
|
|
+};
|
|
+
|
|
+static_assert(sizeof(EnterJITRegs) == 416, "Unexpected size of register save frame");
|
|
+static_assert(offsetof(EnterJITRegs, sp) == 0, "Register save frame is incorrectly oriented");
|
|
+
|
|
+// Generates a trampoline for calling JIT code from a C++ function.
|
|
+// The signature is
|
|
+// EnterJitCode(void* code, unsigned argc, Value* argv, InterpreterFrame* fp,
|
|
+// CalleeToken calleeToken, JSObject* envChain,
|
|
+// size_t numStackValues, Value* vp);
|
|
+// Happily, this all fits into registers.
|
|
+void
|
|
+JitRuntime::generateEnterJIT(JSContext* cx, MacroAssembler& masm)
|
|
+{
|
|
+ AutoCreatedBy acb(masm, "JitRuntime::generateEnterJIT");
|
|
+ ADBlock("generateEnterJIT");
|
|
+
|
|
+ enterJITOffset_ = startTrampolineCode(masm);
|
|
+
|
|
+ const Register reg_code = IntArgReg0; // r3
|
|
+ const Register reg_argc = IntArgReg1; // r4
|
|
+ const Register reg_argv = IntArgReg2; // r5
|
|
+ const mozilla::DebugOnly<Register> reg_frame = IntArgReg3; // r6
|
|
+ const Register reg_token = IntArgReg4; // r7
|
|
+ const Register reg_chain = IntArgReg5; // r8
|
|
+ const Register reg_values = IntArgReg6; // r9
|
|
+ const Register reg_vp = IntArgReg7; // r10
|
|
+
|
|
+ MOZ_ASSERT(OsrFrameReg == reg_frame);
|
|
+
|
|
+ // Standard Power prologue, more or less.
|
|
+ // First save LR and CR to the caller linkage area.
|
|
+ masm.xs_mflr(ScratchRegister);
|
|
+ masm.as_std(ScratchRegister, StackPointer, offsetof(EnterJITRegs, lr)); // caller
|
|
+ masm.as_mfcr(ScratchRegister);
|
|
+ masm.as_std(ScratchRegister, StackPointer, offsetof(EnterJITRegs, cr)); // caller
|
|
+ // Save SP to the caller linkage area and pull down the new frame.
|
|
+ masm.as_stdu(StackPointer, StackPointer, -((uint16_t)sizeof(EnterJITRegs)));
|
|
+
|
|
+ // Save non-volatile registers.
|
|
+#define SAVE(x) masm.as_std(x, StackPointer, offsetof(EnterJITRegs, x));
|
|
+ SAVE(r14)
|
|
+ SAVE(r15)
|
|
+ SAVE(r16)
|
|
+ SAVE(r17)
|
|
+ SAVE(r18)
|
|
+ SAVE(r19)
|
|
+ SAVE(r20)
|
|
+ SAVE(r21)
|
|
+ SAVE(r22)
|
|
+ SAVE(r23)
|
|
+ SAVE(r24)
|
|
+ SAVE(r25)
|
|
+ SAVE(r26)
|
|
+ SAVE(r27)
|
|
+ SAVE(r28)
|
|
+ SAVE(r29)
|
|
+ SAVE(r30)
|
|
+ SAVE(r31)
|
|
+#undef SAVE
|
|
+#define SAVE(x) masm.as_stfd(x, StackPointer, offsetof(EnterJITRegs, x));
|
|
+ SAVE(f14)
|
|
+ SAVE(f15)
|
|
+ SAVE(f16)
|
|
+ SAVE(f17)
|
|
+ SAVE(f18)
|
|
+ SAVE(f19)
|
|
+ SAVE(f20)
|
|
+ SAVE(f21)
|
|
+ SAVE(f22)
|
|
+ SAVE(f23)
|
|
+ SAVE(f24)
|
|
+ SAVE(f25)
|
|
+ SAVE(f26)
|
|
+ SAVE(f27)
|
|
+ SAVE(f28)
|
|
+ SAVE(f29)
|
|
+ SAVE(f30)
|
|
+ SAVE(f31)
|
|
+#undef SAVE
|
|
+
|
|
+ // Save VP for the end.
|
|
+ // We would also save VRSAVE here, if we were likely to use VMX/VSX.
|
|
+ // We load nargs a little later.
|
|
+ masm.as_std(reg_vp, StackPointer, offsetof(EnterJITRegs, savedvp));
|
|
+
|
|
+ // Save stack pointer as baseline frame.
|
|
+ masm.movePtr(StackPointer, FramePointer);
|
|
+
|
|
+ /***************************************************************
|
|
+ Loop over argv vector, push arguments onto stack in reverse order
|
|
+ ***************************************************************/
|
|
+
|
|
+ // If we are constructing, the count also needs to include newTarget.
|
|
+ MOZ_ASSERT(CalleeToken_FunctionConstructing == 0x01);
|
|
+ masm.as_andi_rc(ScratchRegister, reg_token, CalleeToken_FunctionConstructing);
|
|
+ masm.as_add(reg_argc, reg_argc, ScratchRegister);
|
|
+
|
|
+ // |Value| is 8-byte aligned, but we want to maintain 16-byte alignment,
|
|
+ // so tack on an extra Value if the number of arguments is odd.
|
|
+ // Set the address to copy from to *vp + (argc * 8).
|
|
+ // WARNING: ABI compliant stack frames are now no longer guaranteed.
|
|
+ MOZ_ASSERT(sizeof(Value) == 8);
|
|
+ masm.as_andi_rc(ScratchRegister, reg_argc, 1);
|
|
+ masm.x_slwi(SecondScratchReg, reg_argc, 3); // times 8
|
|
+ masm.x_slwi(ScratchRegister, ScratchRegister, 3); // times 8
|
|
+ masm.addPtr(reg_argv, SecondScratchReg);
|
|
+ masm.as_add(StackPointer, StackPointer, ScratchRegister);
|
|
+
|
|
+ // Loop over arguments, copying them from the JS buffer onto the Ion
|
|
+ // stack so they can be accessed from JIT'ed code.
|
|
+ Label header, footer;
|
|
+ // If there aren't any arguments, don't do anything.
|
|
+ masm.ma_bc(SecondScratchReg, reg_argv, &footer, Assembler::BelowOrEqual, ShortJump);
|
|
+ {
|
|
+ masm.bind(&header);
|
|
+
|
|
+ masm.subPtr(Imm32(sizeof(Value)), SecondScratchReg);
|
|
+ masm.subPtr(Imm32(sizeof(Value)), StackPointer);
|
|
+
|
|
+ masm.as_ld(ScratchRegister, SecondScratchReg, 0);
|
|
+ // XXX: Is this usually on stack? Would inserting nops here help?
|
|
+ masm.as_std(ScratchRegister, StackPointer, 0);
|
|
+
|
|
+// XXX: bdnz
|
|
+ masm.ma_bc(SecondScratchReg, reg_argv, &header, Assembler::Above, ShortJump);
|
|
+ }
|
|
+ masm.bind(&footer);
|
|
+
|
|
+ // Load the number of actual arguments (a 32-bit word), then push the
|
|
+ // callee token and actual arguments as part of the new frame.
|
|
+ masm.push(reg_token);
|
|
+ masm.as_lwz(ScratchRegister, reg_vp, 0);
|
|
+ masm.pushFrameDescriptorForJitCall(FrameType::CppToJSJit,
|
|
+ ScratchRegister, ScratchRegister);
|
|
+
|
|
+ CodeLabel returnLabel;
|
|
+ Label oomReturnLabel;
|
|
+ {
|
|
+ // Handle Interpreter -> Baseline OSR.
|
|
+ AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
|
+ MOZ_ASSERT(!regs.has(FramePointer));
|
|
+ regs.take(OsrFrameReg);
|
|
+ regs.take(reg_code);
|
|
+ MOZ_ASSERT(reg_code == ReturnReg); // regs.take(ReturnReg);
|
|
+ MOZ_ASSERT(!regs.has(ReturnReg), "ReturnReg matches reg_code");
|
|
+#if(0)
|
|
+ // On Power reg_code and the ReturnReg are always aliased because of
|
|
+ // ABI requirements. The first argument passed, the code pointer,
|
|
+ // comes in r3, and the ABI requires that r3 be the return register.
|
|
+ // Therefore, we don't implement the changes in bug 1770922.
|
|
+ regs.take(JSReturnOperand); // ???
|
|
+#endif
|
|
+
|
|
+ Label notOsr;
|
|
+ masm.ma_bc(OsrFrameReg, OsrFrameReg, ¬Osr, Assembler::Zero, ShortJump);
|
|
+
|
|
+ Register numStackValues = reg_values;
|
|
+ regs.take(numStackValues);
|
|
+ Register scratch = regs.takeAny();
|
|
+
|
|
+ // Frame prologue.
|
|
+ // Push return address.
|
|
+ masm.subPtr(Imm32(sizeof(uintptr_t) * 2), StackPointer);
|
|
+ masm.ma_li(scratch, &returnLabel);
|
|
+ masm.storePtr(scratch, Address(StackPointer, sizeof(uintptr_t)));
|
|
+ // Push previous frame pointer. Recovered at frame epilogue.
|
|
+ masm.storePtr(FramePointer, Address(StackPointer, 0));
|
|
+
|
|
+ // Reserve frame.
|
|
+ masm.movePtr(StackPointer, FramePointer);
|
|
+ masm.subPtr(Imm32(BaselineFrame::Size()), StackPointer);
|
|
+
|
|
+ Register framePtrScratch = regs.takeAny();
|
|
+ masm.movePtr(StackPointer, framePtrScratch);
|
|
+
|
|
+ // Reserve space for locals and stack values.
|
|
+ masm.x_sldi(scratch, numStackValues, 3);
|
|
+ masm.subPtr(scratch, StackPointer);
|
|
+
|
|
+ // Enter exit frame.
|
|
+ masm.reserveStack(3 * sizeof(uintptr_t));
|
|
+ masm.storePtr(
|
|
+ ImmWord(MakeFrameDescriptor(FrameType::BaselineJS)),
|
|
+ Address(StackPointer, 2 * sizeof(uintptr_t))); // Frame descriptor
|
|
+ masm.storePtr(scratch,
|
|
+ Address(StackPointer, sizeof(uintptr_t))); // fake return address
|
|
+ masm.storePtr(FramePointer, Address(StackPointer, 0));
|
|
+
|
|
+ // No GC things to mark, so push a bare token.
|
|
+ masm.loadJSContext(scratch);
|
|
+ masm.enterFakeExitFrame(scratch, scratch, ExitFrameType::Bare);
|
|
+
|
|
+ masm.reserveStack(2 * sizeof(uintptr_t));
|
|
+ masm.storePtr(FramePointer,
|
|
+ Address(StackPointer, sizeof(uintptr_t))); // BaselineFrame
|
|
+ masm.storePtr(reg_code, Address(StackPointer, 0)); // jitcode
|
|
+
|
|
+ using Fn = bool (*)(BaselineFrame * frame, InterpreterFrame * interpFrame,
|
|
+ uint32_t numStackValues);
|
|
+ masm.setupUnalignedABICall(scratch);
|
|
+ masm.passABIArg(framePtrScratch); // BaselineFrame
|
|
+ masm.passABIArg(OsrFrameReg); // InterpreterFrame
|
|
+ masm.passABIArg(numStackValues);
|
|
+ masm.callWithABI<Fn, jit::InitBaselineFrameForOsr>(
|
|
+ ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
|
|
+
|
|
+ regs.add(OsrFrameReg);
|
|
+ Register jitcode = regs.takeAny();
|
|
+ masm.loadPtr(Address(StackPointer, 0), jitcode);
|
|
+ masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), FramePointer);
|
|
+ masm.freeStack(2 * sizeof(uintptr_t));
|
|
+
|
|
+ Label error;
|
|
+ masm.freeStack(ExitFrameLayout::SizeWithFooter());
|
|
+ masm.branchIfFalseBool(ReturnReg, &error);
|
|
+
|
|
+ // If OSR-ing, then emit instrumentation for setting lastProfilerFrame
|
|
+ // if profiler instrumentation is enabled.
|
|
+ {
|
|
+ Label skipProfilingInstrumentation;
|
|
+ AbsoluteAddress addressOfEnabled(cx->runtime()->geckoProfiler().addressOfEnabled());
|
|
+ masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0),
|
|
+ &skipProfilingInstrumentation);
|
|
+ masm.profilerEnterFrame(FramePointer, scratch);
|
|
+ masm.bind(&skipProfilingInstrumentation);
|
|
+ }
|
|
+
|
|
+//masm.xs_trap_tagged(Assembler::DebugTag0);
|
|
+ masm.jump(jitcode);
|
|
+
|
|
+ // OOM: frame epilogue, load error value, discard return address
|
|
+ // and return.
|
|
+ masm.bind(&error);
|
|
+ masm.movePtr(FramePointer, StackPointer); // don't need to reload FP
|
|
+ masm.addPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer);
|
|
+ masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
|
|
+ masm.jump(&oomReturnLabel);
|
|
+
|
|
+ masm.bind(¬Osr);
|
|
+ // Load the scope chain in R1.
|
|
+ MOZ_ASSERT(R1.scratchReg() != reg_code);
|
|
+ masm.ma_move(R1.scratchReg(), reg_chain);
|
|
+ }
|
|
+
|
|
+ // The call will push the return address on the stack, thus we check that
|
|
+ // the stack would be aligned once the call is complete.
|
|
+// XXX: I don't think this is appropriate for us. We don't claim to be ABI
|
|
+// compliant at this point, and we pass all the stack invariants.
|
|
+// masm.assertStackAlignment(JitStackAlignment, 16);
|
|
+
|
|
+ // Call the function with pushing return address to stack.
|
|
+//masm.xs_trap_tagged(Assembler::DebugTag0);
|
|
+ masm.callJitNoProfiler(reg_code);
|
|
+
|
|
+ {
|
|
+ // Interpreter -> Baseline OSR will return here.
|
|
+ masm.bind(&returnLabel);
|
|
+ masm.addCodeLabel(returnLabel);
|
|
+ masm.bind(&oomReturnLabel);
|
|
+ }
|
|
+
|
|
+ // Discard arguments and padding. Set sp to the address of the EnterJITRegs
|
|
+ // on the stack.
|
|
+ masm.mov(FramePointer, StackPointer);
|
|
+
|
|
+ // Store the returned value into the vp.
|
|
+ masm.as_ld(reg_vp, StackPointer, offsetof(EnterJITRegs, savedvp));
|
|
+ masm.storeValue(JSReturnOperand, Address(reg_vp, 0));
|
|
+
|
|
+ // Restore non-volatile registers and return.
|
|
+ // Standard PowerPC epilogue, more or less.
|
|
+ // Load registers.
|
|
+#define LOAD(x) masm.as_ld(x, StackPointer, offsetof(EnterJITRegs, x));
|
|
+ LOAD(r14)
|
|
+ LOAD(r15)
|
|
+ LOAD(r16)
|
|
+ LOAD(r17)
|
|
+ LOAD(r18)
|
|
+ LOAD(r19)
|
|
+ LOAD(r20)
|
|
+ LOAD(r21)
|
|
+ LOAD(r22)
|
|
+ LOAD(r23)
|
|
+ LOAD(r24)
|
|
+ LOAD(r25)
|
|
+ LOAD(r26)
|
|
+ LOAD(r27)
|
|
+ LOAD(r28)
|
|
+ LOAD(r29)
|
|
+ LOAD(r30)
|
|
+ LOAD(r31)
|
|
+#undef LOAD
|
|
+#define LOAD(x) masm.as_lfd(x, StackPointer, offsetof(EnterJITRegs, x));
|
|
+ LOAD(f14)
|
|
+ LOAD(f15)
|
|
+ LOAD(f16)
|
|
+ LOAD(f17)
|
|
+ LOAD(f18)
|
|
+ LOAD(f19)
|
|
+ LOAD(f20)
|
|
+ LOAD(f21)
|
|
+ LOAD(f22)
|
|
+ LOAD(f23)
|
|
+ LOAD(f24)
|
|
+ LOAD(f25)
|
|
+ LOAD(f26)
|
|
+ LOAD(f27)
|
|
+ LOAD(f28)
|
|
+ LOAD(f29)
|
|
+ LOAD(f30)
|
|
+ LOAD(f31)
|
|
+#undef LOAD
|
|
+
|
|
+ // Tear down frame and retrieve the saved LR and CR from the caller's linkage area.
|
|
+ masm.as_addi(StackPointer, StackPointer, (uint16_t)sizeof(EnterJITRegs));
|
|
+ masm.as_ld(ScratchRegister, StackPointer, offsetof(EnterJITRegs, lr)); // caller
|
|
+ masm.xs_mtlr(ScratchRegister);
|
|
+ masm.as_ld(ScratchRegister, StackPointer, offsetof(EnterJITRegs, cr)); // caller
|
|
+ masm.xs_mtcr(ScratchRegister);
|
|
+
|
|
+ // Bye!
|
|
+ masm.as_blr();
|
|
+}
|
|
+
|
|
+// static
|
|
+mozilla::Maybe<::JS::ProfilingFrameIterator::RegisterState>
|
|
+JitRuntime::getCppEntryRegisters(JitFrameLayout* frameStackAddress) {
|
|
+ // Not supported, or not implemented yet.
|
|
+ // TODO: Implement along with the corresponding stack-walker changes, in
|
|
+ // coordination with the Gecko Profiler, see bug 1635987 and follow-ups.
|
|
+ return mozilla::Nothing{};
|
|
+}
|
|
+
|
|
+void
|
|
+JitRuntime::generateInvalidator(MacroAssembler& masm, Label* bailoutTail)
|
|
+{
|
|
+ AutoCreatedBy acb(masm, "JitRuntime::generateInvalidator");
|
|
+ ADBlock("generateInvalidator");
|
|
+
|
|
+ invalidatorOffset_ = startTrampolineCode(masm);
|
|
+
|
|
+ // The InvalidationBailoutStack r3 points to must have:
|
|
+ // - osiPointReturnAddress_
|
|
+ // - ionScript_ (pushed by CodeGeneratorPPC64::generateInvalidateEpilogue())
|
|
+ // - regs_ (pushed here)
|
|
+ // - fpregs_ (pushed here) => r3
|
|
+
|
|
+ // Stack has to be aligned here. If not, we will have to fix it.
|
|
+ masm.checkStackAlignment();
|
|
+
|
|
+ // Push registers such that we can access them from [base + code].
|
|
+ masm.PushRegsInMask(AllRegs);
|
|
+
|
|
+ // Pass pointer to InvalidationBailoutStack structure.
|
|
+ masm.movePtr(StackPointer, r3);
|
|
+
|
|
+ // Reserve place for BailoutInfo pointer. Two words to ensure alignment for
|
|
+ // setupAlignedABICall.
|
|
+ masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer);
|
|
+ // Pass pointer to BailoutInfo
|
|
+ masm.movePtr(StackPointer, r4);
|
|
+
|
|
+ using Fn = bool (*)(InvalidationBailoutStack * sp,
|
|
+ BaselineBailoutInfo * *info);
|
|
+ masm.setupAlignedABICall();
|
|
+ masm.passABIArg(r3);
|
|
+ masm.passABIArg(r4);
|
|
+ masm.callWithABI<Fn, InvalidationBailout>(
|
|
+ ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
|
|
+
|
|
+ masm.pop(r5);
|
|
+
|
|
+ // Pop the machine state and the dead frame.
|
|
+ masm.moveToStackPtr(FramePointer);
|
|
+
|
|
+ // Jump to shared bailout tail.
|
|
+ // The return code is left unchanged by this routine in r3.
|
|
+ masm.jump(bailoutTail);
|
|
+}
|
|
+
|
|
+// XXX: completely rewritten, check in tests
|
|
+void
|
|
+JitRuntime::generateArgumentsRectifier(MacroAssembler& masm,
|
|
+ ArgumentsRectifierKind kind)
|
|
+{
|
|
+ // Do not erase the frame pointer in this function.
|
|
+
|
|
+ AutoCreatedBy acb(masm, "JitRuntime::generateArgumentsRectifier");
|
|
+ ADBlock("generateArgumentsRectifier");
|
|
+
|
|
+masm.xs_trap();
|
|
+
|
|
+ // MIPS uses a5-a7, t0-t3 and s3, with s3 being the only callee-save reg.
|
|
+ // We will do something similar for Power and use r4-r6, r7-r10 and r15.
|
|
+ const Register nvRectReg = r15;
|
|
+
|
|
+ const Register numArgsReg = r4;
|
|
+ const Register numActArgsReg = r5;
|
|
+ const Register calleeTokenReg = r6;
|
|
+ const Register tempValue = r7;
|
|
+ const Register numToPush = r8;
|
|
+ const Register tempCalleeTokenReg = r9;
|
|
+ const Register tempNumArgsReg = r10;
|
|
+
|
|
+ switch (kind) {
|
|
+ case ArgumentsRectifierKind::Normal:
|
|
+ argumentsRectifierOffset_ = startTrampolineCode(masm);
|
|
+ break;
|
|
+ case ArgumentsRectifierKind::TrialInlining:
|
|
+ trialInliningArgumentsRectifierOffset_ = startTrampolineCode(masm);
|
|
+ break;
|
|
+ }
|
|
+ masm.pushReturnAddress();
|
|
+
|
|
+ // Frame prologue.
|
|
+ //
|
|
+ // NOTE: if this changes, fix the Baseline bailout code too!
|
|
+ // See BaselineStackBuilder::calculatePrevFramePtr and
|
|
+ // BaselineStackBuilder::buildRectifierFrame (in BaselineBailouts.cpp).
|
|
+ masm.push(FramePointer);
|
|
+ masm.mov(StackPointer, FramePointer);
|
|
+
|
|
+ // Load argc.
|
|
+ masm.loadNumActualArgs(FramePointer, nvRectReg);
|
|
+ // Load |nformals| into numArgsReg.
|
|
+ masm.loadPtr(Address(FramePointer,
|
|
+ RectifierFrameLayout::offsetOfCalleeToken()),
|
|
+ calleeTokenReg);
|
|
+ masm.mov(calleeTokenReg, numArgsReg);
|
|
+ masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), numArgsReg);
|
|
+ masm.loadFunctionArgCount(numArgsReg, numArgsReg);
|
|
+
|
|
+ // Stash another copy since we're going to clobber numArgsReg.
|
|
+ masm.as_or(tempNumArgsReg, numArgsReg, numArgsReg);
|
|
+
|
|
+ static_assert(CalleeToken_FunctionConstructing == 1,
|
|
+ "Ensure that we can use the constructing bit to count the value");
|
|
+ masm.mov(calleeTokenReg, tempCalleeTokenReg); // t2
|
|
+ masm.ma_and(tempCalleeTokenReg, Imm32(uint32_t(CalleeToken_FunctionConstructing)));
|
|
+
|
|
+ // Including |this|, and |new.target|, there are (|nformals| + 1 +
|
|
+ // isConstructing) arguments to push to the stack. Then we push a
|
|
+ // JitFrameLayout. We compute the padding expressed in the number of extra
|
|
+ // |undefined| values to push on the stack.
|
|
+ static_assert(
|
|
+ sizeof(JitFrameLayout) % JitStackAlignment == 0,
|
|
+ "No need to consider the JitFrameLayout for aligning the stack");
|
|
+ static_assert(
|
|
+ JitStackAlignment % sizeof(Value) == 0,
|
|
+ "Ensure that we can pad the stack by pushing extra UndefinedValue");
|
|
+
|
|
+ MOZ_ASSERT(mozilla::IsPowerOfTwo(JitStackValueAlignment));
|
|
+ masm.add32(
|
|
+ Imm32(JitStackValueAlignment - 1 /* for padding */ + 1 /* for |this| */),
|
|
+ numArgsReg);
|
|
+ masm.add32(tempCalleeTokenReg, numArgsReg);
|
|
+ masm.and32(Imm32(~(JitStackValueAlignment - 1)), numArgsReg);
|
|
+
|
|
+// s3 is nvRectReg and has argc
|
|
+
|
|
+ // Load the number of |undefined|s to push (nargs - nvRectReg).
|
|
+ masm.as_subf(numToPush, nvRectReg, numArgsReg); // T = B - A
|
|
+ // ... and remove one for |this|
|
|
+ masm.as_addi(numToPush, numToPush, -1);
|
|
+
|
|
+ // Caller:
|
|
+ // [arg2] [arg1] [this] [ [argc] [callee] [descr] [raddr] ] <- sp
|
|
+ // '-nvRectReg-'
|
|
+ //
|
|
+ // Rectifier frame:
|
|
+ // [fp'][undef] [undef] [undef] [arg2] [arg1] [this] [ [argc] [callee]
|
|
+ // [descr] [raddr] ]
|
|
+ // '-------- r8 ---------' '-nvRectReg-'
|
|
+
|
|
+ // Copy number of actual arguments into numActArgsReg.
|
|
+ masm.mov(nvRectReg, numActArgsReg);
|
|
+
|
|
+ masm.moveValue(UndefinedValue(), ValueOperand(tempValue));
|
|
+
|
|
+ // Push undefined (including the padding).
|
|
+ {
|
|
+#if(0)
|
|
+ Label undefLoopTop;
|
|
+
|
|
+ masm.bind(&undefLoopTop);
|
|
+ masm.sub32(Imm32(1), numToPush);
|
|
+ masm.subPtr(Imm32(sizeof(Value)), StackPointer);
|
|
+ masm.storeValue(ValueOperand(tempValue), Address(StackPointer, 0));
|
|
+
|
|
+ masm.ma_bc(numToPush, numToPush, &undefLoopTop, Assembler::NonZero, ShortJump);
|
|
+#else
|
|
+ masm.xs_mtctr(numToPush);
|
|
+ masm.as_stdu(tempValue, StackPointer, -sizeof(Value)); // -4
|
|
+ masm.xs_bdnz(-4);
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ // Get the topmost argument.
|
|
+ static_assert(sizeof(Value) == 8, "TimesEight is used to skip arguments");
|
|
+ MOZ_ASSERT(tempValue == r7); // can clobber
|
|
+ MOZ_ASSERT(numToPush == r8);
|
|
+ MOZ_ASSERT(tempCalleeTokenReg == r9); // can clobber
|
|
+ masm.x_slwi(r7, nvRectReg, 3); // r7 <- nargs * 8
|
|
+ masm.as_add(numToPush, FramePointer, r7); // r8 <- fp + nargs * 8
|
|
+ masm.addPtr(Imm32(sizeof(RectifierFrameLayout)), numToPush);
|
|
+
|
|
+ // Push arguments, |nargs| + 1 times (to include |this|).
|
|
+ masm.as_addi(nvRectReg, nvRectReg, 1);
|
|
+ {
|
|
+#if(0)
|
|
+ Label copyLoopTop;
|
|
+
|
|
+ masm.bind(©LoopTop);
|
|
+ masm.sub32(Imm32(1), nvRectReg);
|
|
+ masm.subPtr(Imm32(sizeof(Value)), StackPointer);
|
|
+ masm.loadValue(Address(numToPush, 0), ValueOperand(tempValue));
|
|
+ masm.storeValue(ValueOperand(tempValue), Address(StackPointer, 0));
|
|
+ masm.subPtr(Imm32(sizeof(Value)), numToPush);
|
|
+
|
|
+ masm.ma_bc(nvRectReg, nvRectReg, ©LoopTop, Assembler::NonZero, ShortJump);
|
|
+#else
|
|
+ masm.xs_mtctr(nvRectReg);
|
|
+ masm.as_ld(tempValue, numToPush, 0); // -12
|
|
+ masm.as_addi(numToPush, numToPush, -8);
|
|
+ masm.as_stdu(tempValue, StackPointer, -8);
|
|
+ masm.xs_bdnz(-12);
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ // If constructing, copy newTarget also.
|
|
+ {
|
|
+ Label notConstructing;
|
|
+
|
|
+ masm.branchTest32(Assembler::Zero, calleeTokenReg, Imm32(CalleeToken_FunctionConstructing),
|
|
+ ¬Constructing);
|
|
+
|
|
+ // thisFrame[numFormals] = prevFrame[argc]
|
|
+ ValueOperand newTarget(tempValue);
|
|
+
|
|
+ // Load vp[argc]. Add sizeof(Value) for |this|.
|
|
+ BaseIndex newTargetSrc(FramePointer, numActArgsReg, TimesEight,
|
|
+ sizeof(RectifierFrameLayout) + sizeof(Value));
|
|
+ masm.loadValue(newTargetSrc, newTarget);
|
|
+
|
|
+ // Again, 1 for |this|. We bring back our saved register from above.
|
|
+ BaseIndex newTargetDest(StackPointer, tempNumArgsReg, TimesEight, sizeof(Value));
|
|
+ masm.storeValue(newTarget, newTargetDest);
|
|
+
|
|
+ masm.bind(¬Constructing);
|
|
+ }
|
|
+
|
|
+ // Caller:
|
|
+ // [arg2] [arg1] [this] [ [argc] [callee] [descr] [raddr] ]
|
|
+ //
|
|
+ //
|
|
+ // Rectifier frame:
|
|
+ // [fp'] <- fp [undef] [undef] [undef] [arg2] [arg1] [this] <- sp [ [argc]
|
|
+ // [callee] [descr] [raddr] ]
|
|
+
|
|
+ // Construct JitFrameLayout.
|
|
+ masm.push(calleeTokenReg);
|
|
+ masm.pushFrameDescriptorForJitCall(FrameType::Rectifier, numActArgsReg,
|
|
+ numActArgsReg);
|
|
+
|
|
+ // Call the target function.
|
|
+ masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), calleeTokenReg);
|
|
+ switch (kind) {
|
|
+ case ArgumentsRectifierKind::Normal:
|
|
+ masm.loadJitCodeRaw(calleeTokenReg, r8); // can clobber r8
|
|
+ argumentsRectifierReturnOffset_ = masm.callJitNoProfiler(r8);
|
|
+ break;
|
|
+ case ArgumentsRectifierKind::TrialInlining:
|
|
+ Label noBaselineScript, done;
|
|
+ masm.loadBaselineJitCodeRaw(calleeTokenReg, r8, &noBaselineScript);
|
|
+ masm.callJitNoProfiler(r8);
|
|
+ masm.ma_b(&done, ShortJump);
|
|
+
|
|
+ // See BaselineCacheIRCompiler::emitCallInlinedFunction.
|
|
+ masm.bind(&noBaselineScript);
|
|
+ masm.loadJitCodeRaw(calleeTokenReg, r8);
|
|
+ masm.callJitNoProfiler(r8);
|
|
+ masm.bind(&done);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ masm.mov(FramePointer, StackPointer);
|
|
+ masm.pop(FramePointer);
|
|
+ masm.ret();
|
|
+}
|
|
+
|
|
+/* - When bailout is done via out of line code (lazy bailout).
|
|
+ * Frame size is stored in LR (look at
|
|
+ * CodeGeneratorPPC64::generateOutOfLineCode()) and thunk code should save it
|
|
+ * on stack. In addition, snapshotOffset_ and padding_ are
|
|
+ * pushed to the stack by CodeGeneratorPPC64::visitOutOfLineBailout().
|
|
+ */
|
|
+static void
|
|
+PushBailoutFrame(MacroAssembler& masm, Register spArg)
|
|
+{
|
|
+ // Push the frameSize_ stored in LR.
|
|
+ masm.xs_mflr(ScratchRegister);
|
|
+ masm.push(ScratchRegister);
|
|
+
|
|
+ // Push registers such that we can access them from [base + code].
|
|
+ masm.PushRegsInMask(AllRegs);
|
|
+
|
|
+ // Put pointer to BailoutStack as first argument to the Bailout().
|
|
+ masm.movePtr(StackPointer, spArg);
|
|
+}
|
|
+
|
|
+static void
|
|
+GenerateBailoutThunk(MacroAssembler& masm, Label* bailoutTail)
|
|
+{
|
|
+ PushBailoutFrame(masm, r3);
|
|
+
|
|
+ // Put pointer to BailoutInfo.
|
|
+ static const uint32_t sizeOfBailoutInfo = sizeof(uintptr_t); // * 2;
|
|
+ masm.subPtr(Imm32(sizeOfBailoutInfo), StackPointer);
|
|
+ masm.movePtr(StackPointer, r4);
|
|
+
|
|
+ using Fn = bool (*)(BailoutStack * sp, BaselineBailoutInfo * *info);
|
|
+ masm.setupUnalignedABICall(r5);
|
|
+ masm.passABIArg(r3);
|
|
+ masm.passABIArg(r4);
|
|
+ masm.callWithABI<Fn, Bailout>(ABIType::General,
|
|
+ CheckUnsafeCallWithABI::DontCheckOther);
|
|
+
|
|
+ // Get BailoutInfo pointer.
|
|
+ masm.loadPtr(Address(StackPointer, 0), r5);
|
|
+
|
|
+ // Remove both the bailout frame and the topmost Ion frame's stack.
|
|
+ masm.moveToStackPtr(FramePointer);
|
|
+
|
|
+ // Jump to shared bailout tail. The BailoutInfo pointer is still in r5 and
|
|
+ // the return code is already in r3, so we can just branch.
|
|
+ masm.jump(bailoutTail);
|
|
+}
|
|
+
|
|
+void
|
|
+JitRuntime::generateBailoutHandler(MacroAssembler& masm, Label* bailoutTail)
|
|
+{
|
|
+ AutoCreatedBy acb(masm, "JitRuntime::generateBailoutHandler");
|
|
+ ADBlock("generateBailoutHandler");
|
|
+
|
|
+ bailoutHandlerOffset_ = startTrampolineCode(masm);
|
|
+
|
|
+ GenerateBailoutThunk(masm, bailoutTail);
|
|
+}
|
|
+
|
|
+bool
|
|
+JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm,
|
|
+ VMFunctionId id,
|
|
+ const VMFunctionData& f, DynFn nativeFun,
|
|
+ uint32_t* wrapperOffset)
|
|
+{
|
|
+ AutoCreatedBy acb(masm, "JitRuntime::generateVMWrapper");
|
|
+ ADBlock("generateVMWrapper");
|
|
+
|
|
+ *wrapperOffset = startTrampolineCode(masm);
|
|
+
|
|
+ // Avoid conflicts with argument registers while discarding the result
|
|
+ // after the function call.
|
|
+ AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask);
|
|
+
|
|
+ static_assert((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0,
|
|
+ "Wrapper register set should be a superset of Volatile register set.");
|
|
+
|
|
+ // The context is the first argument; r3 is the first argument register.
|
|
+ Register cxreg = r3;
|
|
+ regs.take(cxreg);
|
|
+
|
|
+ // Even if this is a tail call, still push (see SharedICHelpers-ppc64-inl.h).
|
|
+ // On link-register platforms, it is the responsibility of the VM *callee* to
|
|
+ // push the return address, while the caller must ensure that the address
|
|
+ // is stored in LR on entry. This allows the VM wrapper to work with both
|
|
+ // direct calls and tail calls.
|
|
+ masm.pushReturnAddress();
|
|
+
|
|
+
|
|
+ // Push the frame pointer to finish the exit frame, then link it up.
|
|
+ masm.Push(FramePointer);
|
|
+ masm.moveStackPtrTo(FramePointer);
|
|
+ masm.loadJSContext(cxreg);
|
|
+ masm.enterExitFrame(cxreg, regs.getAny(), id);
|
|
+
|
|
+ // Reserve space for the outparameter.
|
|
+ masm.reserveVMFunctionOutParamSpace(f);
|
|
+
|
|
+ masm.setupUnalignedABICallDontSaveRestoreSP();
|
|
+ masm.passABIArg(cxreg);
|
|
+
|
|
+ size_t argDisp = ExitFrameLayout::Size();
|
|
+
|
|
+ // Copy any arguments.
|
|
+ for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) {
|
|
+ switch (f.argProperties(explicitArg)) {
|
|
+ case VMFunctionData::WordByValue:
|
|
+ if (f.argPassedInFloatReg(explicitArg)) {
|
|
+ masm.passABIArg(MoveOperand(FramePointer, argDisp), ABIType::Float64);
|
|
+ } else {
|
|
+ masm.passABIArg(MoveOperand(FramePointer, argDisp), ABIType::General);
|
|
+ }
|
|
+ argDisp += sizeof(void*);
|
|
+ break;
|
|
+ case VMFunctionData::WordByRef:
|
|
+ masm.passABIArg(MoveOperand(FramePointer, argDisp,
|
|
+ MoveOperand::Kind::EffectiveAddress),
|
|
+ ABIType::General);
|
|
+ argDisp += sizeof(void*);
|
|
+ break;
|
|
+ case VMFunctionData::DoubleByValue:
|
|
+ case VMFunctionData::DoubleByRef:
|
|
+ MOZ_CRASH("NYI: riscv callVM should not be used with 128bits values.");
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Copy the implicit outparam, if any.
|
|
+ //if (InvalidReg != outReg)
|
|
+ // masm.passABIArg(outReg);
|
|
+ // It is not a C++-abi outparam, which would get passed in the
|
|
+ // outparam register, but a real parameter to the function, which
|
|
+ // was stack-allocated above.
|
|
+ const int32_t outParamOffset =
|
|
+ -int32_t(ExitFooterFrame::Size()) - f.sizeOfOutParamStackSlot();
|
|
+ if (f.outParam != Type_Void) {
|
|
+ masm.passABIArg(MoveOperand(FramePointer, outParamOffset,
|
|
+ MoveOperand::Kind::EffectiveAddress),
|
|
+ ABIType::General);
|
|
+ }
|
|
+
|
|
+ masm.callWithABI(nativeFun, ABIType::General,
|
|
+ CheckUnsafeCallWithABI::DontCheckHasExitFrame);
|
|
+
|
|
+ // Test for failure.
|
|
+ switch (f.failType()) {
|
|
+ case Type_Cell:
|
|
+ masm.branchTestPtr(Assembler::Zero, r3, r3, masm.failureLabel());
|
|
+ break;
|
|
+ case Type_Bool:
|
|
+ // Called functions return bools, which are 0/false and non-zero/true.
|
|
+ masm.branchIfFalseBool(r3, masm.failureLabel());
|
|
+ break;
|
|
+ case Type_Void:
|
|
+ break;
|
|
+ default:
|
|
+ MOZ_CRASH("unknown failure kind");
|
|
+ }
|
|
+
|
|
+ // Load the outparam. Leave the cannoli.
|
|
+ masm.loadVMFunctionOutParam(f, Address(FramePointer, outParamOffset));
|
|
+
|
|
+ // Pop frame and restore frame pointer.
|
|
+ masm.moveToStackPtr(FramePointer);
|
|
+ masm.pop(FramePointer);
|
|
+
|
|
+ // Return. Subtract sizeof(void*) for the frame pointer.
|
|
+ masm.retn(Imm32(sizeof(ExitFrameLayout) - sizeof(void*) +
|
|
+ f.explicitStackSlots() * sizeof(void*) +
|
|
+ f.extraValuesToPop * sizeof(Value)));
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+uint32_t
|
|
+JitRuntime::generatePreBarrier(JSContext* cx, MacroAssembler& masm, MIRType type)
|
|
+{
|
|
+ AutoCreatedBy acb(masm, "JitRuntime::generatePreBarrier");
|
|
+ ADBlock("generatePreBarrier");
|
|
+
|
|
+ uint32_t offset = startTrampolineCode(masm);
|
|
+
|
|
+ MOZ_ASSERT(PreBarrierReg == r4);
|
|
+ Register temp1 = r3;
|
|
+ Register temp2 = r5;
|
|
+ Register temp3 = r6;
|
|
+ // TODO: could be more efficient with multipush/pop
|
|
+ masm.push(temp1);
|
|
+ masm.push(temp2);
|
|
+ masm.push(temp3);
|
|
+
|
|
+ Label noBarrier;
|
|
+ masm.emitPreBarrierFastPath(cx->runtime(), type, temp1, temp2, temp3, &noBarrier);
|
|
+
|
|
+ // Call into C++ to mark this GC thing.
|
|
+ masm.pop(temp3);
|
|
+ masm.pop(temp2);
|
|
+ masm.pop(temp1);
|
|
+
|
|
+ // Explicitly save LR, since we can't use it in PushRegsInMask.
|
|
+ masm.xs_mflr(ScratchRegister);
|
|
+ masm.push(ScratchRegister);
|
|
+ LiveRegisterSet save;
|
|
+ save.set() = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
|
|
+ FloatRegisterSet(FloatRegisters::VolatileMask));
|
|
+ masm.PushRegsInMask(save);
|
|
+
|
|
+ masm.movePtr(ImmPtr(cx->runtime()), r3);
|
|
+
|
|
+ masm.setupUnalignedABICall(r5);
|
|
+ masm.passABIArg(r3);
|
|
+ masm.passABIArg(r4);
|
|
+ masm.callWithABI(JitPreWriteBarrier(type));
|
|
+
|
|
+ masm.PopRegsInMask(save);
|
|
+ // ret() pops LR for us.
|
|
+ masm.ret();
|
|
+
|
|
+ masm.bind(&noBarrier);
|
|
+ masm.pop(temp3);
|
|
+ masm.pop(temp2);
|
|
+ masm.pop(temp1);
|
|
+ masm.abiret();
|
|
+
|
|
+ return offset;
|
|
+}
|
|
+
|
|
+void
|
|
+JitRuntime::generateBailoutTailStub(MacroAssembler& masm, Label* bailoutTail)
|
|
+{
|
|
+ AutoCreatedBy acb(masm, "JitRuntime::generateBailoutTailStub");
|
|
+ ADBlock("generateBailoutTailStub");
|
|
+
|
|
+ masm.bind(bailoutTail);
|
|
+ masm.generateBailoutTail(r4, r5);
|
|
+}
|
|
+
|