From 330e037c93b006d8746f88dbf2a2c398b80caa96 Mon Sep 17 00:00:00 2001 From: kth5 Date: Wed, 25 Sep 2024 14:22:47 +0200 Subject: [PATCH] * update firefox-esr to 115.13.0-1 --- firefox-esr/power9-jit-744146.diff | 26140 --------------------------- firefox-esr/power9-jit-744147.diff | 2681 --- firefox-esr/power9-jit-744148.diff | 488 - firefox-esr/power9-jit-744150.diff | 146 - firefox-esr/power9-jit-744151.diff | 195 - firefox-esr/power9-jit-744152.diff | 285 - firefox-esr/power9-jit-744153.diff | 236 - firefox-esr/power9-jit-744154.diff | 70 - firefox-esr/power9-jit-744155.diff | 61 - 9 files changed, 30302 deletions(-) delete mode 100644 firefox-esr/power9-jit-744146.diff delete mode 100644 firefox-esr/power9-jit-744147.diff delete mode 100644 firefox-esr/power9-jit-744148.diff delete mode 100644 firefox-esr/power9-jit-744150.diff delete mode 100644 firefox-esr/power9-jit-744151.diff delete mode 100644 firefox-esr/power9-jit-744152.diff delete mode 100644 firefox-esr/power9-jit-744153.diff delete mode 100644 firefox-esr/power9-jit-744154.diff delete mode 100644 firefox-esr/power9-jit-744155.diff diff --git a/firefox-esr/power9-jit-744146.diff b/firefox-esr/power9-jit-744146.diff deleted file mode 100644 index ff55ac4184..0000000000 --- a/firefox-esr/power9-jit-744146.diff +++ /dev/null @@ -1,26140 +0,0 @@ -# HG changeset patch -# User Cameron Kaiser -# Date 1694141509 25200 -# Thu Sep 07 19:51:49 2023 -0700 -# Node ID 99b51ba09f3fb402a7c05948f5cb847f7ad21689 -# Parent cd548157ebca4b79b046aabef427abfe7f599f34 -initial landing, no automerge crap - -diff -r cd548157ebca -r 99b51ba09f3f js/moz.configure ---- a/js/moz.configure Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/moz.configure Thu Sep 07 19:51:49 2023 -0700 -@@ -269,27 +269,29 @@ - - set_config("JS_CODEGEN_NONE", jit_codegen.none) - set_config("JS_CODEGEN_ARM", jit_codegen.arm) - set_config("JS_CODEGEN_ARM64", jit_codegen.arm64) - set_config("JS_CODEGEN_MIPS32", jit_codegen.mips32) - set_config("JS_CODEGEN_MIPS64", jit_codegen.mips64) - set_config("JS_CODEGEN_LOONG64", jit_codegen.loong64) - set_config("JS_CODEGEN_RISCV64", jit_codegen.riscv64) -+set_config("JS_CODEGEN_PPC64", jit_codegen.ppc64) - set_config("JS_CODEGEN_X86", jit_codegen.x86) - set_config("JS_CODEGEN_X64", jit_codegen.x64) - set_config("JS_CODEGEN_WASM32", jit_codegen.wasm32) - - set_define("JS_CODEGEN_NONE", jit_codegen.none) - set_define("JS_CODEGEN_ARM", jit_codegen.arm) - set_define("JS_CODEGEN_ARM64", jit_codegen.arm64) - set_define("JS_CODEGEN_MIPS32", jit_codegen.mips32) - set_define("JS_CODEGEN_MIPS64", jit_codegen.mips64) - set_define("JS_CODEGEN_LOONG64", jit_codegen.loong64) - set_define("JS_CODEGEN_RISCV64", jit_codegen.riscv64) -+set_config("JS_CODEGEN_PPC64", jit_codegen.ppc64) - set_define("JS_CODEGEN_X86", jit_codegen.x86) - set_define("JS_CODEGEN_X64", jit_codegen.x64) - set_define("JS_CODEGEN_WASM32", jit_codegen.wasm32) - - - # Profiling - # ======================================================= - option( -diff -r cd548157ebca -r 99b51ba09f3f js/src/irregexp/RegExpNativeMacroAssembler.cpp ---- a/js/src/irregexp/RegExpNativeMacroAssembler.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/irregexp/RegExpNativeMacroAssembler.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -946,18 +946,33 @@ - // If the test fails, call an OOL handler to try growing the stack. - void SMRegExpMacroAssembler::CheckBacktrackStackLimit() { - js::jit::Label no_stack_overflow; - masm_.branchPtr( - Assembler::BelowOrEqual, - AbsoluteAddress(isolate()->regexp_stack()->limit_address_address()), - backtrack_stack_pointer_, &no_stack_overflow); - -+#ifdef JS_CODEGEN_PPC64 -+ // LR on PowerPC isn't a GPR, so we have to explicitly save it here before -+ // we call or we will end up erroneously returning after the call to the -+ // stack overflow handler when we |blr| out and inevitably underflow the -+ // irregexp stack on the next backtrack. -+ masm_.xs_mflr(temp1_); -+ masm_.as_stdu(temp1_, masm_.getStackPointer(), -8); -+#endif -+ - masm_.call(&stack_overflow_label_); - -+#ifdef JS_CODEGEN_PPC64 -+ masm_.as_ld(temp1_, masm_.getStackPointer(), 0); -+ masm_.xs_mtlr(temp1_); -+ masm_.as_addi(masm_.getStackPointer(), masm_.getStackPointer(), 8); -+#endif -+ - // Exit with an exception if the call failed - masm_.branchTest32(Assembler::Zero, temp0_, temp0_, - &exit_with_exception_label_); - - masm_.bind(&no_stack_overflow); - } - - // This is used to sneak an OOM through the V8 layer. -@@ -1280,16 +1295,20 @@ - LiveGeneralRegisterSet volatileRegs(GeneralRegisterSet::Volatile()); - - #ifdef JS_USE_LINK_REGISTER - masm_.pushReturnAddress(); - #endif - - // Adjust for the return address on the stack. - size_t frameOffset = sizeof(void*); -+#ifdef JS_CODEGEN_PPC64 -+ // We have a double return address. -+ frameOffset += sizeof(void*); -+#endif - - volatileRegs.takeUnchecked(temp0_); - volatileRegs.takeUnchecked(temp1_); - masm_.PushRegsInMask(volatileRegs); - - using Fn = bool (*)(RegExpStack* regexp_stack); - masm_.setupUnalignedABICall(temp0_); - masm_.passABIArg(temp1_); -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/Assembler.h ---- a/js/src/jit/Assembler.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/Assembler.h Thu Sep 07 19:51:49 2023 -0700 -@@ -18,16 +18,18 @@ - #elif defined(JS_CODEGEN_MIPS32) - # include "jit/mips32/Assembler-mips32.h" - #elif defined(JS_CODEGEN_MIPS64) - # include "jit/mips64/Assembler-mips64.h" - #elif defined(JS_CODEGEN_LOONG64) - # include "jit/loong64/Assembler-loong64.h" - #elif defined(JS_CODEGEN_RISCV64) - # include "jit/riscv64/Assembler-riscv64.h" -+#elif defined(JS_CODEGEN_PPC64) -+# include "jit/ppc64/Assembler-ppc64.h" - #elif defined(JS_CODEGEN_WASM32) - # include "jit/wasm32/Assembler-wasm32.h" - #elif defined(JS_CODEGEN_NONE) - # include "jit/none/Assembler-none.h" - #else - # error "Unknown architecture!" - #endif - -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/BaselineCodeGen.cpp ---- a/js/src/jit/BaselineCodeGen.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/BaselineCodeGen.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -529,16 +529,19 @@ - // address. The |masm.ret()| later will pop this into |pc| to return. - masm.push(lr); - #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) - masm.push(ra); - #elif defined(JS_CODEGEN_LOONG64) - masm.push(ra); - #elif defined(JS_CODEGEN_RISCV64) - masm.push(ra); -+#elif defined(JS_CODEGEN_PPC64) -+ masm.xs_mflr(ScratchRegister); -+ masm.push(ScratchRegister); - #endif - masm.pushValue(R0); - - using Fn = void (*)(JSRuntime* rt, js::gc::Cell* cell); - masm.setupUnalignedABICall(scratch); - masm.movePtr(ImmPtr(cx->runtime()), scratch); - masm.passABIArg(scratch); - masm.passABIArg(objReg); -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/BaselineIC.cpp ---- a/js/src/jit/BaselineIC.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/BaselineIC.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -107,17 +107,18 @@ - }; - - AllocatableGeneralRegisterSet BaselineICAvailableGeneralRegs(size_t numInputs) { - AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); - MOZ_ASSERT(!regs.has(FramePointer)); - #if defined(JS_CODEGEN_ARM) - MOZ_ASSERT(!regs.has(ICTailCallReg)); - regs.take(BaselineSecondScratchReg); --#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) -+#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ -+ defined(JS_CODEGEN_PPC64) - MOZ_ASSERT(!regs.has(ICTailCallReg)); - MOZ_ASSERT(!regs.has(BaselineSecondScratchReg)); - #elif defined(JS_CODEGEN_ARM64) - MOZ_ASSERT(!regs.has(PseudoStackPointer)); - MOZ_ASSERT(!regs.has(RealStackPointer)); - MOZ_ASSERT(!regs.has(ICTailCallReg)); - #endif - regs.take(ICStubReg); -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/CodeGenerator.cpp ---- a/js/src/jit/CodeGenerator.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/CodeGenerator.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -2122,16 +2122,23 @@ - masm.storePtr(temp2, matchesAddress); - masm.storePtr(lastIndex, startIndexAddress); - - // Execute the RegExp. - masm.computeEffectiveAddress( - Address(FramePointer, inputOutputDataStartOffset), temp2); - masm.PushRegsInMask(volatileRegs); - masm.setupUnalignedABICall(temp3); -+#if defined(JS_CODEGEN_PPC64) -+ // temp1 aliases argregs on this platform, so we need to reuse temp3 again -+ // or we'll stomp on the code pointer when we pass the first ABI argument. -+ // Everything gets clobbered anyway! -+ masm.xs_mr(temp3, codePointer); -+ codePointer = temp3; -+#endif - masm.passABIArg(temp2); - masm.callWithABI(codePointer); - masm.storeCallInt32Result(temp1); - masm.PopRegsInMask(volatileRegs); - - masm.bind(&checkSuccess); - masm.branch32(Assembler::Equal, temp1, - Imm32(RegExpRunStatus_Success_NotFound), notFound); -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/CodeGenerator.h ---- a/js/src/jit/CodeGenerator.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/CodeGenerator.h Thu Sep 07 19:51:49 2023 -0700 -@@ -21,16 +21,18 @@ - #elif defined(JS_CODEGEN_MIPS32) - # include "jit/mips32/CodeGenerator-mips32.h" - #elif defined(JS_CODEGEN_MIPS64) - # include "jit/mips64/CodeGenerator-mips64.h" - #elif defined(JS_CODEGEN_LOONG64) - # include "jit/loong64/CodeGenerator-loong64.h" - #elif defined(JS_CODEGEN_RISCV64) - # include "jit/riscv64/CodeGenerator-riscv64.h" -+#elif defined(JS_CODEGEN_PPC64) -+# include "jit/ppc64/CodeGenerator-ppc64.h" - #elif defined(JS_CODEGEN_WASM32) - # include "jit/wasm32/CodeGenerator-wasm32.h" - #elif defined(JS_CODEGEN_NONE) - # include "jit/none/CodeGenerator-none.h" - #else - # error "Unknown architecture!" - #endif - -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/FlushICache.h ---- a/js/src/jit/FlushICache.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/FlushICache.h Thu Sep 07 19:51:49 2023 -0700 -@@ -19,17 +19,18 @@ - #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) - - inline void FlushICache(void* code, size_t size) { - // No-op. Code and data caches are coherent on x86 and x64. - } - - #elif (defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)) || \ - (defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)) || \ -- defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) || \ -+ defined(JS_CODEGEN_PPC64) - - // Invalidate the given code range from the icache. This will also flush the - // execution context for this core. If this code is to be executed on another - // thread, that thread must perform an execution context flush first using - // `FlushExecutionContext` below. - extern void FlushICache(void* code, size_t size); - - #elif defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_WASM32) -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/JitFrames.h ---- a/js/src/jit/JitFrames.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/JitFrames.h Thu Sep 07 19:51:49 2023 -0700 -@@ -118,16 +118,26 @@ - - // The exception was caught by a wasm catch handler. - // Restore state and jump to it. - WasmCatch - }; - - // Data needed to recover from an exception. - struct ResumeFromException { -+#if defined(JS_CODEGEN_PPC64) -+ // This gets built on the stack as part of exception returns. Because -+ // it goes right on top of the stack, an ABI-compliant routine can wreck -+ // it, so we implement a minimum Power ISA linkage area (four doublewords). -+ void *_ppc_sp_; -+ void *_ppc_cr_; -+ void *_ppc_lr_; -+ void *_ppc_toc_; -+#endif -+ - uint8_t* framePointer; - uint8_t* stackPointer; - uint8_t* target; - ExceptionResumeKind kind; - wasm::Instance* instance; - - // Value to push when resuming into a |finally| block. - // Also used by Wasm to send the exception object to the throw stub. -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/JitOptions.cpp ---- a/js/src/jit/JitOptions.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/JitOptions.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -144,17 +144,22 @@ - // Whether the IonMonkey JIT is enabled. - SET_DEFAULT(ion, true); - - // Whether the IonMonkey and Baseline JITs are enabled for Trusted Principals. - // (Ignored if ion or baselineJit is set to true.) - SET_DEFAULT(jitForTrustedPrincipals, false); - - // Whether the RegExp JIT is enabled. -+#if defined(JS_CODEGEN_PPC64) -+ // This may generate ISA 3 instructions. The other JIT tiers gate on it too. -+ SET_DEFAULT(nativeRegExp, MacroAssembler::SupportsFloatingPoint()); -+#else - SET_DEFAULT(nativeRegExp, true); -+#endif - - // Whether Warp should use ICs instead of transpiling Baseline CacheIR. - SET_DEFAULT(forceInlineCaches, false); - - // Whether all ICs should be initialized as megamorphic ICs. - SET_DEFAULT(forceMegamorphicICs, false); - - // Toggles whether large scripts are rejected. -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/LIR.h ---- a/js/src/jit/LIR.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/LIR.h Thu Sep 07 19:51:49 2023 -0700 -@@ -1941,16 +1941,18 @@ - # include "jit/riscv64/LIR-riscv64.h" - #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) - # if defined(JS_CODEGEN_MIPS32) - # include "jit/mips32/LIR-mips32.h" - # elif defined(JS_CODEGEN_MIPS64) - # include "jit/mips64/LIR-mips64.h" - # endif - # include "jit/mips-shared/LIR-mips-shared.h" -+#elif defined(JS_CODEGEN_PPC64) -+# include "jit/ppc64/LIR-ppc64.h" - #elif defined(JS_CODEGEN_WASM32) - # include "jit/wasm32/LIR-wasm32.h" - #elif defined(JS_CODEGEN_NONE) - # include "jit/none/LIR-none.h" - #else - # error "Unknown architecture!" - #endif - -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/LIROps.yaml ---- a/js/src/jit/LIROps.yaml Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/LIROps.yaml Thu Sep 07 19:51:49 2023 -0700 -@@ -3955,16 +3955,57 @@ - - - name: WasmAtomicBinopI64 - gen_boilerplate: false - - - name: WasmAtomicExchangeI64 - gen_boilerplate: false - #endif - -+#ifdef JS_CODEGEN_PPC64 -+- name: DivOrModI64 -+ gen_boilerplate: false -+ -+- name: UDivOrMod -+ gen_boilerplate: false -+ -+- name: UDivOrModI64 -+ gen_boilerplate: false -+ -+- name: ModMaskI -+ gen_boilerplate: false -+ -+- name: WasmTruncateToInt64 -+ gen_boilerplate: false -+ -+- name: Int64ToFloatingPoint -+ gen_boilerplate: false -+ -+- name: WasmUnalignedLoad -+ gen_boilerplate: false -+ -+- name: WasmUnalignedLoadI64 -+ gen_boilerplate: false -+ -+- name: WasmUnalignedStore -+ gen_boilerplate: false -+ -+- name: WasmUnalignedStoreI64 -+ gen_boilerplate: false -+ -+- name: WasmCompareExchangeI64 -+ gen_boilerplate: false -+ -+- name: WasmAtomicBinopI64 -+ gen_boilerplate: false -+ -+- name: WasmAtomicExchangeI64 -+ gen_boilerplate: false -+#endif -+ - #ifdef FUZZING_JS_FUZZILLI - - name: FuzzilliHashT - gen_boilerplate: false - - - name: FuzzilliHashV - gen_boilerplate: false - - - name: FuzzilliHashStore -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/Label.h ---- a/js/src/jit/Label.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/Label.h Thu Sep 07 19:51:49 2023 -0700 -@@ -22,17 +22,18 @@ - - // offset_ < INVALID_OFFSET means that the label is either bound or has - // incoming uses and needs to be bound. - uint32_t offset_ : 31; - - void operator=(const LabelBase& label) = delete; - - #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ -- defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) || \ -+ defined(JS_CODEGEN_PPC64) - public: - #endif - static const uint32_t INVALID_OFFSET = 0x7fffffff; // UINT31_MAX. - - public: - LabelBase() : bound_(false), offset_(INVALID_OFFSET) {} - - // If the label is bound, all incoming edges have been patched and any -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/Lowering.h ---- a/js/src/jit/Lowering.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/Lowering.h Thu Sep 07 19:51:49 2023 -0700 -@@ -22,16 +22,18 @@ - #elif defined(JS_CODEGEN_MIPS32) - # include "jit/mips32/Lowering-mips32.h" - #elif defined(JS_CODEGEN_MIPS64) - # include "jit/mips64/Lowering-mips64.h" - #elif defined(JS_CODEGEN_LOONG64) - # include "jit/loong64/Lowering-loong64.h" - #elif defined(JS_CODEGEN_RISCV64) - # include "jit/riscv64/Lowering-riscv64.h" -+#elif defined(JS_CODEGEN_PPC64) -+# include "jit/ppc64/Lowering-ppc64.h" - #elif defined(JS_CODEGEN_WASM32) - # include "jit/wasm32/Lowering-wasm32.h" - #elif defined(JS_CODEGEN_NONE) - # include "jit/none/Lowering-none.h" - #else - # error "Unknown architecture!" - #endif - -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/MacroAssembler-inl.h ---- a/js/src/jit/MacroAssembler-inl.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/MacroAssembler-inl.h Thu Sep 07 19:51:49 2023 -0700 -@@ -37,16 +37,18 @@ - #elif defined(JS_CODEGEN_MIPS32) - # include "jit/mips32/MacroAssembler-mips32-inl.h" - #elif defined(JS_CODEGEN_MIPS64) - # include "jit/mips64/MacroAssembler-mips64-inl.h" - #elif defined(JS_CODEGEN_LOONG64) - # include "jit/loong64/MacroAssembler-loong64-inl.h" - #elif defined(JS_CODEGEN_RISCV64) - # include "jit/riscv64/MacroAssembler-riscv64-inl.h" -+#elif defined(JS_CODEGEN_PPC64) -+# include "jit/ppc64/MacroAssembler-ppc64-inl.h" - #elif defined(JS_CODEGEN_WASM32) - # include "jit/wasm32/MacroAssembler-wasm32-inl.h" - #elif !defined(JS_CODEGEN_NONE) - # error "Unknown architecture!" - #endif - - #include "wasm/WasmBuiltins.h" - -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/MacroAssembler.cpp ---- a/js/src/jit/MacroAssembler.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/MacroAssembler.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -3170,17 +3170,18 @@ - wasm::BytecodeOffset callOffset) { - if (compilingWasm) { - Push(InstanceReg); - } - int32_t framePushedAfterInstance = framePushed(); - - #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ - defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ -- defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) || \ -+ defined(JS_CODEGEN_PPC64) - ScratchDoubleScope fpscratch(*this); - if (widenFloatToDouble) { - convertFloat32ToDouble(src, fpscratch); - src = fpscratch; - } - #elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) - FloatRegister srcSingle; - if (widenFloatToDouble) { -@@ -3209,17 +3210,18 @@ - passABIArg(src, MoveOp::DOUBLE); - callWithABI(MoveOp::GENERAL, - CheckUnsafeCallWithABI::DontCheckOther); - } - storeCallInt32Result(dest); - - #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ - defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ -- defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) || \ -+ defined(JS_CODEGEN_PPC64) - // Nothing - #elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) - if (widenFloatToDouble) { - Pop(srcSingle); - } - #else - MOZ_CRASH("MacroAssembler platform hook: outOfLineTruncateSlow"); - #endif -@@ -5180,16 +5182,18 @@ - #elif JS_CODEGEN_MIPS32 - ma_sll(temp1, temp1, temp3); - #elif JS_CODEGEN_MIPS64 - ma_dsll(temp1, temp1, temp3); - #elif JS_CODEGEN_LOONG64 - as_sll_d(temp1, temp1, temp3); - #elif JS_CODEGEN_RISCV64 - sll(temp1, temp1, temp3); -+#elif JS_CODEGEN_PPC64 -+ as_sld(temp1, temp1, temp3) - #elif JS_CODEGEN_WASM32 - MOZ_CRASH(); - #elif JS_CODEGEN_NONE - MOZ_CRASH(); - #else - # error "Unknown architecture" - #endif - -@@ -6411,16 +6415,25 @@ - assumeUnreachable("Unexpected BigInt"); - } else if (isBigInt == IsBigInt::Yes) { - branchTestBigInt(Assembler::Equal, value, &ok); - assumeUnreachable("Unexpected non-BigInt"); - } - bind(&ok); - #endif - -+#if defined(JS_CODEGEN_PPC64) -+ // If this was preceded by a MoveGroup instruction, the hash may have been -+ // loaded algebraically since it's an Int32 (and thus sign-extended); the -+ // operation doesn't know to keep the upper bits clear, failing the assert. -+ if (isBigInt == IsBigInt::No) { -+ as_rldicl(hash, hash, 0, 32); // "clrldi" -+ } -+#endif -+ - #ifdef DEBUG - PushRegsInMask(LiveRegisterSet(RegisterSet::Volatile())); - - pushValue(value); - moveStackPtrTo(temp2); - - setupUnalignedABICall(temp1); - loadJSContext(temp1); -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/MacroAssembler.h ---- a/js/src/jit/MacroAssembler.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/MacroAssembler.h Thu Sep 07 19:51:49 2023 -0700 -@@ -24,16 +24,18 @@ - #elif defined(JS_CODEGEN_MIPS32) - # include "jit/mips32/MacroAssembler-mips32.h" - #elif defined(JS_CODEGEN_MIPS64) - # include "jit/mips64/MacroAssembler-mips64.h" - #elif defined(JS_CODEGEN_LOONG64) - # include "jit/loong64/MacroAssembler-loong64.h" - #elif defined(JS_CODEGEN_RISCV64) - # include "jit/riscv64/MacroAssembler-riscv64.h" -+#elif defined(JS_CODEGEN_PPC64) -+# include "jit/ppc64/MacroAssembler-ppc64.h" - #elif defined(JS_CODEGEN_WASM32) - # include "jit/wasm32/MacroAssembler-wasm32.h" - #elif defined(JS_CODEGEN_NONE) - # include "jit/none/MacroAssembler-none.h" - #else - # error "Unknown architecture!" - #endif - #include "jit/ABIArgGenerator.h" -@@ -92,19 +94,20 @@ - // //{{{ check_macroassembler_style - // inline uint32_t - // MacroAssembler::framePushed() const - // { - // return framePushed_; - // } - // ////}}} check_macroassembler_style - --#define ALL_ARCH mips32, mips64, arm, arm64, x86, x64, loong64, riscv64, wasm32 -+#define ALL_ARCH mips32, mips64, arm, arm64, x86, x64, loong64, riscv64, \ -+ ppc64, wasm32 - #define ALL_SHARED_ARCH \ -- arm, arm64, loong64, riscv64, x86_shared, mips_shared, wasm32 -+ arm, arm64, loong64, riscv64, ppc64, x86_shared, mips_shared, wasm32 - - // * How this macro works: - // - // DEFINED_ON is a macro which check if, for the current architecture, the - // method is defined on the macro assembler or not. - // - // For each architecture, we have a macro named DEFINED_ON_arch. This macro is - // empty if this is not the current architecture. Otherwise it must be either -@@ -142,16 +145,17 @@ - #define DEFINED_ON_x86_shared - #define DEFINED_ON_arm - #define DEFINED_ON_arm64 - #define DEFINED_ON_mips32 - #define DEFINED_ON_mips64 - #define DEFINED_ON_mips_shared - #define DEFINED_ON_loong64 - #define DEFINED_ON_riscv64 -+#define DEFINED_ON_ppc64 - #define DEFINED_ON_wasm32 - #define DEFINED_ON_none - - // Specialize for each architecture. - #if defined(JS_CODEGEN_X86) - # undef DEFINED_ON_x86 - # define DEFINED_ON_x86 define - # undef DEFINED_ON_x86_shared -@@ -178,16 +182,19 @@ - # undef DEFINED_ON_mips_shared - # define DEFINED_ON_mips_shared define - #elif defined(JS_CODEGEN_LOONG64) - # undef DEFINED_ON_loong64 - # define DEFINED_ON_loong64 define - #elif defined(JS_CODEGEN_RISCV64) - # undef DEFINED_ON_riscv64 - # define DEFINED_ON_riscv64 define -+#elif defined(JS_CODEGEN_PPC64) -+# undef DEFINED_ON_ppc64 -+# define DEFINED_ON_ppc64 define - #elif defined(JS_CODEGEN_WASM32) - # undef DEFINED_ON_wasm32 - # define DEFINED_ON_wasm32 define - #elif defined(JS_CODEGEN_NONE) - # undef DEFINED_ON_none - # define DEFINED_ON_none crash - #else - # error "Unknown architecture!" -@@ -508,39 +515,39 @@ - // the same layout to be used in machineState, and therefore in all other code - // that can spill registers that are recovered on bailout. Implementations of - // JitRuntime::generate{Invalidator,BailoutHandler} should either call - // PushRegsInMask, or check carefully to be sure that they generate the same - // layout. - - // The size of the area used by PushRegsInMask. - size_t PushRegsInMaskSizeInBytes(LiveRegisterSet set) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - void PushRegsInMask(LiveRegisterSet set) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - void PushRegsInMask(LiveGeneralRegisterSet set); - - // Like PushRegsInMask, but instead of pushing the registers, store them to - // |dest|. |dest| should point to the end of the reserved space, so the - // first register will be stored at |dest.offset - sizeof(register)|. It is - // required that |dest.offset| is at least as large as the value computed by - // PushRegsInMaskSizeInBytes for this |set|. In other words, |dest.base| - // must point to either the lowest address in the save area, or some address - // below that. - void storeRegsInMask(LiveRegisterSet set, Address dest, Register scratch) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - void PopRegsInMask(LiveRegisterSet set); - void PopRegsInMask(LiveGeneralRegisterSet set); - void PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - // =============================================================== - // Stack manipulation functions -- single registers/values. - - void Push(const Operand op) DEFINED_ON(x86_shared); - void Push(Register reg) PER_SHARED_ARCH; - void Push(Register reg1, Register reg2, Register reg3, Register reg4) -@@ -565,17 +572,17 @@ - inline CodeOffset PushWithPatch(ImmPtr imm); - - void Pop(const Operand op) DEFINED_ON(x86_shared); - void Pop(Register reg) PER_SHARED_ARCH; - void Pop(FloatRegister t) PER_SHARED_ARCH; - void Pop(const ValueOperand& val) PER_SHARED_ARCH; - void PopFlags() DEFINED_ON(x86_shared); - void PopStackPtr() -- DEFINED_ON(arm, mips_shared, x86_shared, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, mips_shared, x86_shared, loong64, riscv64, ppc64, wasm32); - void popRooted(VMFunctionData::RootType rootType, Register cellReg, - const ValueOperand& valueReg); - - // Move the stack pointer based on the requested amount. - void adjustStack(int amount); - void freeStack(uint32_t amount); - - // Warning: This method does not update the framePushed() counter. -@@ -624,19 +631,19 @@ - // Push the return address and make a call. On platforms where this function - // is not defined, push the link register (pushReturnAddress) at the entry - // point of the callee. - void callAndPushReturnAddress(Register reg) DEFINED_ON(x86_shared); - void callAndPushReturnAddress(Label* label) DEFINED_ON(x86_shared); - - // These do not adjust framePushed(). - void pushReturnAddress() -- DEFINED_ON(mips_shared, arm, arm64, loong64, riscv64, wasm32); -+ DEFINED_ON(mips_shared, arm, arm64, loong64, riscv64, ppc64, wasm32); - void popReturnAddress() -- DEFINED_ON(mips_shared, arm, arm64, loong64, riscv64, wasm32); -+ DEFINED_ON(mips_shared, arm, arm64, loong64, riscv64, ppc64, wasm32); - - // Useful for dealing with two-valued returns. - void moveRegPair(Register src0, Register src1, Register dst0, Register dst1, - MoveOp::Type type = MoveOp::GENERAL); - - public: - // =============================================================== - // Patchable near/far jumps. -@@ -659,17 +666,17 @@ - // using pc-relative addressing on certain platforms (RIP-relative LEA on x64, - // ADR instruction on arm64). - // - // Note: "Near" applies to ARM64 where the target must be within 1 MB (this is - // release-asserted). - CodeOffset moveNearAddressWithPatch(Register dest) PER_ARCH; - static void patchNearAddressMove(CodeLocationLabel loc, - CodeLocationLabel target) -- DEFINED_ON(x86, x64, arm, arm64, loong64, riscv64, wasm32, mips_shared); -+ DEFINED_ON(x86, x64, arm, arm64, loong64, riscv64, ppc64, wasm32, mips_shared); - - public: - // =============================================================== - // [SMDOC] JIT-to-C++ Function Calls (callWithABI) - // - // callWithABI is used to make a call using the standard C/C++ system ABI. - // - // callWithABI is a low level interface for making calls, as such every call -@@ -1033,21 +1040,21 @@ - inline void xor32(Imm32 imm, Register dest) PER_SHARED_ARCH; - inline void xor32(Imm32 imm, const Address& dest) PER_SHARED_ARCH; - inline void xor32(const Address& src, Register dest) PER_SHARED_ARCH; - - inline void xorPtr(Register src, Register dest) PER_ARCH; - inline void xorPtr(Imm32 imm, Register dest) PER_ARCH; - - inline void and64(const Operand& src, Register64 dest) -- DEFINED_ON(x64, mips64, loong64, riscv64); -+ DEFINED_ON(x64, mips64, loong64, riscv64, ppc64); - inline void or64(const Operand& src, Register64 dest) -- DEFINED_ON(x64, mips64, loong64, riscv64); -+ DEFINED_ON(x64, mips64, loong64, riscv64, ppc64); - inline void xor64(const Operand& src, Register64 dest) -- DEFINED_ON(x64, mips64, loong64, riscv64); -+ DEFINED_ON(x64, mips64, loong64, riscv64, ppc64); - - // =============================================================== - // Swap instructions - - // Swap the two lower bytes and sign extend the result to 32-bit. - inline void byteSwap16SignExtend(Register reg) PER_SHARED_ARCH; - - // Swap the two lower bytes and zero extend the result to 32-bit. -@@ -1076,27 +1083,27 @@ - inline void addPtr(Register src, Register dest) PER_ARCH; - inline void addPtr(Register src1, Register src2, Register dest) - DEFINED_ON(arm64); - inline void addPtr(Imm32 imm, Register dest) PER_ARCH; - inline void addPtr(Imm32 imm, Register src, Register dest) DEFINED_ON(arm64); - inline void addPtr(ImmWord imm, Register dest) PER_ARCH; - inline void addPtr(ImmPtr imm, Register dest); - inline void addPtr(Imm32 imm, const Address& dest) -- DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64, riscv64, ppc64, wasm32); - inline void addPtr(Imm32 imm, const AbsoluteAddress& dest) - DEFINED_ON(x86, x64); - inline void addPtr(const Address& src, Register dest) -- DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64, riscv64, ppc64, wasm32); - - inline void add64(Register64 src, Register64 dest) PER_ARCH; - inline void add64(Imm32 imm, Register64 dest) PER_ARCH; - inline void add64(Imm64 imm, Register64 dest) PER_ARCH; - inline void add64(const Operand& src, Register64 dest) -- DEFINED_ON(x64, mips64, loong64, riscv64); -+ DEFINED_ON(x64, mips64, loong64, riscv64, ppc64); - - inline void addFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH; - - // Compute dest=SP-imm where dest is a pointer registers and not SP. The - // offset returned from sub32FromStackPtrWithPatch() must be passed to - // patchSub32FromStackPtr(). - inline CodeOffset sub32FromStackPtrWithPatch(Register dest) PER_ARCH; - inline void patchSub32FromStackPtr(CodeOffset offset, Imm32 imm) PER_ARCH; -@@ -1105,26 +1112,26 @@ - inline void addConstantDouble(double d, FloatRegister dest) DEFINED_ON(x86); - - inline void sub32(const Address& src, Register dest) PER_SHARED_ARCH; - inline void sub32(Register src, Register dest) PER_SHARED_ARCH; - inline void sub32(Imm32 imm, Register dest) PER_SHARED_ARCH; - - inline void subPtr(Register src, Register dest) PER_ARCH; - inline void subPtr(Register src, const Address& dest) -- DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64, riscv64, ppc64, wasm32); - inline void subPtr(Imm32 imm, Register dest) PER_ARCH; - inline void subPtr(ImmWord imm, Register dest) DEFINED_ON(x64); - inline void subPtr(const Address& addr, Register dest) -- DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64, riscv64, ppc64, wasm32); - - inline void sub64(Register64 src, Register64 dest) PER_ARCH; - inline void sub64(Imm64 imm, Register64 dest) PER_ARCH; - inline void sub64(const Operand& src, Register64 dest) -- DEFINED_ON(x64, mips64, loong64, riscv64); -+ DEFINED_ON(x64, mips64, loong64, riscv64, ppc64); - - inline void subFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH; - - inline void subDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH; - - inline void mul32(Register rhs, Register srcDest) PER_SHARED_ARCH; - inline void mul32(Imm32 imm, Register srcDest) PER_SHARED_ARCH; - -@@ -1135,91 +1142,91 @@ - inline void mulHighUnsigned32(Imm32 imm, Register src, - Register dest) PER_ARCH; - - inline void mulPtr(Register rhs, Register srcDest) PER_ARCH; - - inline void mul64(const Operand& src, const Register64& dest) DEFINED_ON(x64); - inline void mul64(const Operand& src, const Register64& dest, - const Register temp) -- DEFINED_ON(x64, mips64, loong64, riscv64); -+ DEFINED_ON(x64, mips64, loong64, riscv64, ppc64); - inline void mul64(Imm64 imm, const Register64& dest) PER_ARCH; - inline void mul64(Imm64 imm, const Register64& dest, const Register temp) -- DEFINED_ON(x86, x64, arm, mips32, mips64, loong64, riscv64); -+ DEFINED_ON(x86, x64, arm, mips32, mips64, loong64, riscv64, ppc64); - inline void mul64(const Register64& src, const Register64& dest, - const Register temp) PER_ARCH; - inline void mul64(const Register64& src1, const Register64& src2, - const Register64& dest) DEFINED_ON(arm64); - inline void mul64(Imm64 src1, const Register64& src2, const Register64& dest) - DEFINED_ON(arm64); - - inline void mulBy3(Register src, Register dest) PER_ARCH; - - inline void mulFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH; - inline void mulDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH; - - inline void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) -- DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64, riscv64, ppc64, wasm32); - - // Perform an integer division, returning the integer part rounded toward - // zero. rhs must not be zero, and the division must not overflow. - // - // On ARM, the chip must have hardware division instructions. - inline void quotient32(Register rhs, Register srcDest, bool isUnsigned) -- DEFINED_ON(mips_shared, arm, arm64, loong64, riscv64, wasm32); -+ DEFINED_ON(mips_shared, arm, arm64, loong64, riscv64, ppc64, wasm32); - - // As above, but srcDest must be eax and tempEdx must be edx. - inline void quotient32(Register rhs, Register srcDest, Register tempEdx, - bool isUnsigned) DEFINED_ON(x86_shared); - - // Perform an integer division, returning the remainder part. - // rhs must not be zero, and the division must not overflow. - // - // On ARM, the chip must have hardware division instructions. - inline void remainder32(Register rhs, Register srcDest, bool isUnsigned) -- DEFINED_ON(mips_shared, arm, arm64, loong64, riscv64, wasm32); -+ DEFINED_ON(mips_shared, arm, arm64, loong64, riscv64, ppc64, wasm32); - - // As above, but srcDest must be eax and tempEdx must be edx. - inline void remainder32(Register rhs, Register srcDest, Register tempEdx, - bool isUnsigned) DEFINED_ON(x86_shared); - - // Perform an integer division, returning the integer part rounded toward - // zero. rhs must not be zero, and the division must not overflow. - // - // This variant preserves registers, and doesn't require hardware division - // instructions on ARM (will call out to a runtime routine). - // - // rhs is preserved, srdDest is clobbered. - void flexibleRemainder32(Register rhs, Register srcDest, bool isUnsigned, - const LiveRegisterSet& volatileLiveRegs) -- DEFINED_ON(mips_shared, arm, arm64, x86_shared, loong64, riscv64, wasm32); -+ DEFINED_ON(mips_shared, arm, arm64, x86_shared, loong64, riscv64, ppc64, wasm32); - - // Perform an integer division, returning the integer part rounded toward - // zero. rhs must not be zero, and the division must not overflow. - // - // This variant preserves registers, and doesn't require hardware division - // instructions on ARM (will call out to a runtime routine). - // - // rhs is preserved, srdDest is clobbered. - void flexibleQuotient32(Register rhs, Register srcDest, bool isUnsigned, - const LiveRegisterSet& volatileLiveRegs) -- DEFINED_ON(mips_shared, arm, arm64, x86_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, arm, arm64, x86_shared, loong64, riscv64, ppc64); - - // Perform an integer division, returning the integer part rounded toward - // zero. rhs must not be zero, and the division must not overflow. The - // remainder is stored into the third argument register here. - // - // This variant preserves registers, and doesn't require hardware division - // instructions on ARM (will call out to a runtime routine). - // - // rhs is preserved, srdDest and remOutput are clobbered. - void flexibleDivMod32(Register rhs, Register srcDest, Register remOutput, - bool isUnsigned, - const LiveRegisterSet& volatileLiveRegs) -- DEFINED_ON(mips_shared, arm, arm64, x86_shared, loong64, riscv64, wasm32); -+ DEFINED_ON(mips_shared, arm, arm64, x86_shared, loong64, riscv64, ppc64, wasm32); - - inline void divFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH; - inline void divDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH; - - inline void inc64(AbsoluteAddress dest) PER_ARCH; - - inline void neg32(Register reg) PER_SHARED_ARCH; - inline void neg64(Register64 reg) PER_ARCH; -@@ -1420,17 +1427,17 @@ - Register dest) PER_SHARED_ARCH; - - inline void cmp16Set(Condition cond, Address lhs, Imm32 rhs, - Register dest) PER_SHARED_ARCH; - - template - inline void cmp32Set(Condition cond, T1 lhs, T2 rhs, Register dest) - DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64, riscv64, -- wasm32); -+ ppc64, wasm32); - - // Only the NotEqual and Equal conditions are allowed. - inline void cmp64Set(Condition cond, Address lhs, Imm64 rhs, - Register dest) PER_ARCH; - - template - inline void cmpPtrSet(Condition cond, T1 lhs, T2 rhs, Register dest) PER_ARCH; - -@@ -1462,34 +1469,34 @@ - - inline void branch32(Condition cond, const Address& lhs, Register rhs, - Label* label) PER_SHARED_ARCH; - inline void branch32(Condition cond, const Address& lhs, Imm32 rhs, - Label* label) PER_SHARED_ARCH; - - inline void branch32(Condition cond, const AbsoluteAddress& lhs, Register rhs, - Label* label) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - inline void branch32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, - Label* label) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - - inline void branch32(Condition cond, const BaseIndex& lhs, Register rhs, - Label* label) DEFINED_ON(arm, x86_shared); - inline void branch32(Condition cond, const BaseIndex& lhs, Imm32 rhs, - Label* label) PER_SHARED_ARCH; - - inline void branch32(Condition cond, const Operand& lhs, Register rhs, - Label* label) DEFINED_ON(x86_shared); - inline void branch32(Condition cond, const Operand& lhs, Imm32 rhs, - Label* label) DEFINED_ON(x86_shared); - - inline void branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, - Label* label) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - - // The supported condition are Equal, NotEqual, LessThan(orEqual), - // GreaterThan(orEqual), Below(orEqual) and Above(orEqual). When a fail label - // is not defined it will fall through to next instruction, else jump to the - // fail label. - inline void branch64(Condition cond, Register64 lhs, Imm64 val, - Label* success, Label* fail = nullptr) PER_ARCH; - inline void branch64(Condition cond, Register64 lhs, Register64 rhs, -@@ -1530,32 +1537,32 @@ - - inline void branchPtr(Condition cond, const BaseIndex& lhs, ImmWord rhs, - Label* label) PER_SHARED_ARCH; - inline void branchPtr(Condition cond, const BaseIndex& lhs, Register rhs, - Label* label) PER_SHARED_ARCH; - - inline void branchPtr(Condition cond, const AbsoluteAddress& lhs, - Register rhs, Label* label) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - inline void branchPtr(Condition cond, const AbsoluteAddress& lhs, ImmWord rhs, - Label* label) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - - inline void branchPtr(Condition cond, wasm::SymbolicAddress lhs, Register rhs, - Label* label) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - - // Given a pointer to a GC Cell, retrieve the StoreBuffer pointer from its - // chunk header, or nullptr if it is in the tenured heap. - void loadStoreBuffer(Register ptr, Register buffer) PER_ARCH; - - void branchPtrInNurseryChunk(Condition cond, Register ptr, Register temp, - Label* label) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - void branchPtrInNurseryChunk(Condition cond, const Address& address, - Register temp, Label* label) DEFINED_ON(x86); - void branchValueIsNurseryCell(Condition cond, const Address& address, - Register temp, Label* label) PER_ARCH; - void branchValueIsNurseryCell(Condition cond, ValueOperand value, - Register temp, Label* label) PER_ARCH; - - // This function compares a Value (lhs) which is having a private pointer -@@ -1567,33 +1574,33 @@ - FloatRegister rhs, Label* label) PER_SHARED_ARCH; - - // Truncate a double/float32 to int32 and when it doesn't fit an int32 it will - // jump to the failure label. This particular variant is allowed to return the - // value module 2**32, which isn't implemented on all architectures. E.g. the - // x64 variants will do this only in the int64_t range. - inline void branchTruncateFloat32MaybeModUint32(FloatRegister src, - Register dest, Label* fail) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - inline void branchTruncateDoubleMaybeModUint32(FloatRegister src, - Register dest, Label* fail) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - - // Truncate a double/float32 to intptr and when it doesn't fit jump to the - // failure label. - inline void branchTruncateFloat32ToPtr(FloatRegister src, Register dest, - Label* fail) DEFINED_ON(x86, x64); - inline void branchTruncateDoubleToPtr(FloatRegister src, Register dest, - Label* fail) DEFINED_ON(x86, x64); - - // Truncate a double/float32 to int32 and when it doesn't fit jump to the - // failure label. - inline void branchTruncateFloat32ToInt32(FloatRegister src, Register dest, - Label* fail) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - inline void branchTruncateDoubleToInt32(FloatRegister src, Register dest, - Label* fail) PER_ARCH; - - inline void branchDouble(DoubleCondition cond, FloatRegister lhs, - FloatRegister rhs, Label* label) PER_SHARED_ARCH; - - inline void branchDoubleNotInInt64Range(Address src, Register temp, - Label* fail); -@@ -1642,17 +1649,17 @@ - L label) PER_SHARED_ARCH; - template - inline void branchTest32(Condition cond, Register lhs, Imm32 rhs, - L label) PER_SHARED_ARCH; - inline void branchTest32(Condition cond, const Address& lhs, Imm32 rhh, - Label* label) PER_SHARED_ARCH; - inline void branchTest32(Condition cond, const AbsoluteAddress& lhs, - Imm32 rhs, Label* label) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - - template - inline void branchTestPtr(Condition cond, Register lhs, Register rhs, - L label) PER_SHARED_ARCH; - inline void branchTestPtr(Condition cond, Register lhs, Imm32 rhs, - Label* label) PER_SHARED_ARCH; - inline void branchTestPtr(Condition cond, const Address& lhs, Imm32 rhs, - Label* label) PER_SHARED_ARCH; -@@ -1811,17 +1818,17 @@ - - // Perform a type-test on a tag of a Value (32bits boxing), or the tagged - // value (64bits boxing). - inline void branchTestUndefined(Condition cond, Register tag, - Label* label) PER_SHARED_ARCH; - inline void branchTestInt32(Condition cond, Register tag, - Label* label) PER_SHARED_ARCH; - inline void branchTestDouble(Condition cond, Register tag, Label* label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - inline void branchTestNumber(Condition cond, Register tag, - Label* label) PER_SHARED_ARCH; - inline void branchTestBoolean(Condition cond, Register tag, - Label* label) PER_SHARED_ARCH; - inline void branchTestString(Condition cond, Register tag, - Label* label) PER_SHARED_ARCH; - inline void branchTestSymbol(Condition cond, Register tag, -@@ -1844,117 +1851,117 @@ - // BaseIndex and ValueOperand variants clobber the ScratchReg on x64. - // All Variants clobber the ScratchReg on arm64. - inline void branchTestUndefined(Condition cond, const Address& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestUndefined(Condition cond, const BaseIndex& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestUndefined(Condition cond, const ValueOperand& value, - Label* label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - inline void branchTestInt32(Condition cond, const Address& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestInt32(Condition cond, const BaseIndex& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestInt32(Condition cond, const ValueOperand& value, - Label* label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - inline void branchTestDouble(Condition cond, const Address& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestDouble(Condition cond, const BaseIndex& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestDouble(Condition cond, const ValueOperand& value, - Label* label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - inline void branchTestNumber(Condition cond, const ValueOperand& value, - Label* label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - inline void branchTestBoolean(Condition cond, const Address& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestBoolean(Condition cond, const BaseIndex& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestBoolean(Condition cond, const ValueOperand& value, - Label* label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - inline void branchTestString(Condition cond, const Address& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestString(Condition cond, const BaseIndex& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestString(Condition cond, const ValueOperand& value, - Label* label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - inline void branchTestSymbol(Condition cond, const Address& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestSymbol(Condition cond, const BaseIndex& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestSymbol(Condition cond, const ValueOperand& value, - Label* label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - inline void branchTestBigInt(Condition cond, const Address& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestBigInt(Condition cond, const BaseIndex& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestBigInt(Condition cond, const ValueOperand& value, - Label* label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - inline void branchTestNull(Condition cond, const Address& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestNull(Condition cond, const BaseIndex& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestNull(Condition cond, const ValueOperand& value, - Label* label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - // Clobbers the ScratchReg on x64. - inline void branchTestObject(Condition cond, const Address& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestObject(Condition cond, const BaseIndex& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestObject(Condition cond, const ValueOperand& value, - Label* label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - inline void branchTestGCThing(Condition cond, const Address& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestGCThing(Condition cond, const BaseIndex& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestGCThing(Condition cond, const ValueOperand& value, - Label* label) PER_SHARED_ARCH; - - inline void branchTestPrimitive(Condition cond, const ValueOperand& value, - Label* label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - inline void branchTestMagic(Condition cond, const Address& address, - Label* label) PER_SHARED_ARCH; - inline void branchTestMagic(Condition cond, const BaseIndex& address, - Label* label) PER_SHARED_ARCH; - template - inline void branchTestMagic(Condition cond, const ValueOperand& value, - L label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - inline void branchTestMagic(Condition cond, const Address& valaddr, - JSWhyMagic why, Label* label) PER_ARCH; - - inline void branchTestMagicValue(Condition cond, const ValueOperand& val, - JSWhyMagic why, Label* label); - -@@ -1963,45 +1970,45 @@ - - inline void branchTestValue(Condition cond, const BaseIndex& lhs, - const ValueOperand& rhs, Label* label) PER_ARCH; - - // Checks if given Value is evaluated to true or false in a condition. - // The type of the value should match the type of the method. - inline void branchTestInt32Truthy(bool truthy, const ValueOperand& value, - Label* label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, x86_shared, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, x86_shared, - wasm32); - inline void branchTestDoubleTruthy(bool truthy, FloatRegister reg, - Label* label) PER_SHARED_ARCH; - inline void branchTestBooleanTruthy(bool truthy, const ValueOperand& value, - Label* label) PER_ARCH; - inline void branchTestStringTruthy(bool truthy, const ValueOperand& value, - Label* label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - inline void branchTestBigIntTruthy(bool truthy, const ValueOperand& value, - Label* label) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, wasm32, -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, wasm32, - x86_shared); - - // Create an unconditional branch to the address given as argument. - inline void branchToComputedAddress(const BaseIndex& address) PER_ARCH; - - private: - template - inline void branchPtrImpl(Condition cond, const T& lhs, const S& rhs, L label) - DEFINED_ON(x86_shared); - - void branchPtrInNurseryChunkImpl(Condition cond, Register ptr, Label* label) - DEFINED_ON(x86); - template - void branchValueIsNurseryCellImpl(Condition cond, const T& value, - Register temp, Label* label) -- DEFINED_ON(arm64, x64, mips64, loong64, riscv64); -+ DEFINED_ON(arm64, x64, mips64, loong64, riscv64, ppc64); - - template - inline void branchTestUndefinedImpl(Condition cond, const T& t, Label* label) - DEFINED_ON(arm, arm64, x86_shared); - template - inline void branchTestInt32Impl(Condition cond, const T& t, Label* label) - DEFINED_ON(arm, arm64, x86_shared); - template -@@ -2078,119 +2085,119 @@ - inline void fallibleUnboxString(const T& src, Register dest, Label* fail); - template - inline void fallibleUnboxSymbol(const T& src, Register dest, Label* fail); - template - inline void fallibleUnboxBigInt(const T& src, Register dest, Label* fail); - - inline void cmp32Move32(Condition cond, Register lhs, Register rhs, - Register src, Register dest) -- DEFINED_ON(arm, arm64, loong64, riscv64, wasm32, mips_shared, x86_shared); -+ DEFINED_ON(arm, arm64, loong64, riscv64, ppc64, wasm32, mips_shared, x86_shared); - - inline void cmp32Move32(Condition cond, Register lhs, const Address& rhs, - Register src, Register dest) -- DEFINED_ON(arm, arm64, loong64, riscv64, wasm32, mips_shared, x86_shared); -+ DEFINED_ON(arm, arm64, loong64, riscv64, ppc64, wasm32, mips_shared, x86_shared); - - inline void cmpPtrMovePtr(Condition cond, Register lhs, Register rhs, - Register src, Register dest) PER_ARCH; - - inline void cmpPtrMovePtr(Condition cond, Register lhs, const Address& rhs, - Register src, Register dest) PER_ARCH; - - inline void cmp32Load32(Condition cond, Register lhs, const Address& rhs, - const Address& src, Register dest) -- DEFINED_ON(arm, arm64, loong64, riscv64, mips_shared, x86_shared); -+ DEFINED_ON(arm, arm64, loong64, riscv64, ppc64, mips_shared, x86_shared); - - inline void cmp32Load32(Condition cond, Register lhs, Register rhs, - const Address& src, Register dest) -- DEFINED_ON(arm, arm64, loong64, riscv64, mips_shared, x86_shared); -+ DEFINED_ON(arm, arm64, loong64, riscv64, ppc64, mips_shared, x86_shared); - - inline void cmp32LoadPtr(Condition cond, const Address& lhs, Imm32 rhs, - const Address& src, Register dest) -- DEFINED_ON(arm, arm64, loong64, riscv64, wasm32, mips_shared, x86, x64); -+ DEFINED_ON(arm, arm64, loong64, riscv64, ppc64, wasm32, mips_shared, x86, x64); - - inline void cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs, - Register src, Register dest) -- DEFINED_ON(arm, arm64, loong64, riscv64, wasm32, mips_shared, x86, x64); -+ DEFINED_ON(arm, arm64, loong64, riscv64, ppc64, wasm32, mips_shared, x86, x64); - - inline void test32LoadPtr(Condition cond, const Address& addr, Imm32 mask, - const Address& src, Register dest) -- DEFINED_ON(arm, arm64, loong64, riscv64, wasm32, mips_shared, x86, x64); -+ DEFINED_ON(arm, arm64, loong64, riscv64, ppc64, wasm32, mips_shared, x86, x64); - - inline void test32MovePtr(Condition cond, const Address& addr, Imm32 mask, - Register src, Register dest) -- DEFINED_ON(arm, arm64, loong64, riscv64, wasm32, mips_shared, x86, x64); -+ DEFINED_ON(arm, arm64, loong64, riscv64, ppc64, wasm32, mips_shared, x86, x64); - - // Conditional move for Spectre mitigations. - inline void spectreMovePtr(Condition cond, Register src, Register dest) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - - // Zeroes dest if the condition is true. - inline void spectreZeroRegister(Condition cond, Register scratch, - Register dest) -- DEFINED_ON(arm, arm64, mips_shared, x86_shared, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86_shared, loong64, riscv64, ppc64, wasm32); - - // Performs a bounds check and zeroes the index register if out-of-bounds - // (to mitigate Spectre). - private: - inline void spectreBoundsCheck32(Register index, const Operand& length, - Register maybeScratch, Label* failure) - DEFINED_ON(x86); - - public: - inline void spectreBoundsCheck32(Register index, Register length, - Register maybeScratch, Label* failure) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - inline void spectreBoundsCheck32(Register index, const Address& length, - Register maybeScratch, Label* failure) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - - inline void spectreBoundsCheckPtr(Register index, Register length, - Register maybeScratch, Label* failure) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - inline void spectreBoundsCheckPtr(Register index, const Address& length, - Register maybeScratch, Label* failure) -- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64, ppc64, wasm32); - - // ======================================================================== - // Canonicalization primitives. - inline void canonicalizeDouble(FloatRegister reg); - inline void canonicalizeDoubleIfDeterministic(FloatRegister reg); - - inline void canonicalizeFloat(FloatRegister reg); - inline void canonicalizeFloatIfDeterministic(FloatRegister reg); - - public: - // ======================================================================== - // Memory access primitives. - inline void storeUncanonicalizedDouble(FloatRegister src, const Address& dest) -- DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64, riscv64, -+ DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64, riscv64, ppc64, - wasm32); - inline void storeUncanonicalizedDouble(FloatRegister src, - const BaseIndex& dest) -- DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64, riscv64, -+ DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64, riscv64, ppc64, - wasm32); - inline void storeUncanonicalizedDouble(FloatRegister src, const Operand& dest) - DEFINED_ON(x86_shared); - - template - inline void storeDouble(FloatRegister src, const T& dest); - - template - inline void boxDouble(FloatRegister src, const T& dest); - - using MacroAssemblerSpecific::boxDouble; - - inline void storeUncanonicalizedFloat32(FloatRegister src, - const Address& dest) -- DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64, riscv64, -+ DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64, riscv64, ppc64, - wasm32); - inline void storeUncanonicalizedFloat32(FloatRegister src, - const BaseIndex& dest) -- DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64, riscv64, -+ DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64, riscv64, ppc64, - wasm32); - inline void storeUncanonicalizedFloat32(FloatRegister src, - const Operand& dest) - DEFINED_ON(x86_shared); - - template - inline void storeFloat32(FloatRegister src, const T& dest); - -@@ -3584,20 +3591,20 @@ - DEFINED_ON(x86, x64); - - public: - // ======================================================================== - // Convert floating point. - - // temp required on x86 and x64; must be undefined on mips64 and loong64. - void convertUInt64ToFloat32(Register64 src, FloatRegister dest, Register temp) -- DEFINED_ON(arm64, mips64, loong64, riscv64, wasm32, x64, x86); -+ DEFINED_ON(arm64, mips64, loong64, riscv64, ppc64, wasm32, x64, x86); - - void convertInt64ToFloat32(Register64 src, FloatRegister dest) -- DEFINED_ON(arm64, mips64, loong64, riscv64, wasm32, x64, x86); -+ DEFINED_ON(arm64, mips64, loong64, riscv64, ppc64, wasm32, x64, x86); - - bool convertUInt64ToDoubleNeedsTemp() PER_ARCH; - - // temp required when convertUInt64ToDoubleNeedsTemp() returns true. - void convertUInt64ToDouble(Register64 src, FloatRegister dest, - Register temp) PER_ARCH; - - void convertInt64ToDouble(Register64 src, FloatRegister dest) PER_ARCH; -@@ -3640,30 +3647,30 @@ - // On 32-bit systems for both wasm and asm.js, and on 64-bit systems for - // asm.js, heap lengths are limited to 2GB. On 64-bit systems for wasm, - // 32-bit heap lengths are limited to 4GB, and 64-bit heap lengths will be - // limited to something much larger. - - void wasmBoundsCheck32(Condition cond, Register index, - Register boundsCheckLimit, Label* ok) - DEFINED_ON(arm, arm64, mips32, mips64, x86_shared, loong64, riscv64, -- wasm32); -+ ppc64, wasm32); - - void wasmBoundsCheck32(Condition cond, Register index, - Address boundsCheckLimit, Label* ok) - DEFINED_ON(arm, arm64, mips32, mips64, x86_shared, loong64, riscv64, -- wasm32); -+ ppc64, wasm32); - - void wasmBoundsCheck64(Condition cond, Register64 index, - Register64 boundsCheckLimit, Label* ok) -- DEFINED_ON(arm64, mips64, x64, x86, arm, loong64, riscv64, wasm32); -+ DEFINED_ON(arm64, mips64, x64, x86, arm, loong64, riscv64, ppc64, wasm32); - - void wasmBoundsCheck64(Condition cond, Register64 index, - Address boundsCheckLimit, Label* ok) -- DEFINED_ON(arm64, mips64, x64, x86, arm, loong64, riscv64, wasm32); -+ DEFINED_ON(arm64, mips64, x64, x86, arm, loong64, riscv64, ppc64, wasm32); - - // Each wasm load/store instruction appends its own wasm::Trap::OutOfBounds. - void wasmLoad(const wasm::MemoryAccessDesc& access, Operand srcAddr, - AnyRegister out) DEFINED_ON(x86, x64); - void wasmLoadI64(const wasm::MemoryAccessDesc& access, Operand srcAddr, - Register64 out) DEFINED_ON(x86, x64); - void wasmStore(const wasm::MemoryAccessDesc& access, AnyRegister value, - Operand dstAddr) DEFINED_ON(x86, x64); -@@ -3673,26 +3680,26 @@ - // For all the ARM/MIPS/LOONG64 wasmLoad and wasmStore functions below, `ptr` - // MUST equal `ptrScratch`, and that register will be updated based on - // conditions listed below (where it is only mentioned as `ptr`). - - // `ptr` will be updated if access.offset() != 0 or access.type() == - // Scalar::Int64. - void wasmLoad(const wasm::MemoryAccessDesc& access, Register memoryBase, - Register ptr, Register ptrScratch, AnyRegister output) -- DEFINED_ON(arm, loong64, riscv64, mips_shared); -+ DEFINED_ON(arm, loong64, riscv64, ppc64, mips_shared); - void wasmLoadI64(const wasm::MemoryAccessDesc& access, Register memoryBase, - Register ptr, Register ptrScratch, Register64 output) -- DEFINED_ON(arm, mips32, mips64, loong64, riscv64); -+ DEFINED_ON(arm, mips32, mips64, loong64, riscv64, ppc64); - void wasmStore(const wasm::MemoryAccessDesc& access, AnyRegister value, - Register memoryBase, Register ptr, Register ptrScratch) -- DEFINED_ON(arm, loong64, riscv64, mips_shared); -+ DEFINED_ON(arm, loong64, riscv64, ppc64, mips_shared); - void wasmStoreI64(const wasm::MemoryAccessDesc& access, Register64 value, - Register memoryBase, Register ptr, Register ptrScratch) -- DEFINED_ON(arm, mips32, mips64, loong64, riscv64); -+ DEFINED_ON(arm, mips32, mips64, loong64, riscv64, ppc64); - - // These accept general memoryBase + ptr + offset (in `access`); the offset is - // always smaller than the guard region. They will insert an additional add - // if the offset is nonzero, and of course that add may require a temporary - // register for the offset if the offset is large, and instructions to set it - // up. - void wasmLoad(const wasm::MemoryAccessDesc& access, Register memoryBase, - Register ptr, AnyRegister output) DEFINED_ON(arm64); -@@ -3702,99 +3709,99 @@ - Register memoryBase, Register ptr) DEFINED_ON(arm64); - void wasmStoreI64(const wasm::MemoryAccessDesc& access, Register64 value, - Register memoryBase, Register ptr) DEFINED_ON(arm64); - - // `ptr` will always be updated. - void wasmUnalignedLoad(const wasm::MemoryAccessDesc& access, - Register memoryBase, Register ptr, Register ptrScratch, - Register output, Register tmp) -- DEFINED_ON(mips32, mips64); -+ DEFINED_ON(mips32, mips64, ppc64); - - // MIPS: `ptr` will always be updated. - void wasmUnalignedLoadFP(const wasm::MemoryAccessDesc& access, - Register memoryBase, Register ptr, - Register ptrScratch, FloatRegister output, -- Register tmp1) DEFINED_ON(mips32, mips64); -+ Register tmp1) DEFINED_ON(mips32, mips64, ppc64); - - // `ptr` will always be updated. - void wasmUnalignedLoadI64(const wasm::MemoryAccessDesc& access, - Register memoryBase, Register ptr, - Register ptrScratch, Register64 output, -- Register tmp) DEFINED_ON(mips32, mips64); -+ Register tmp) DEFINED_ON(mips32, mips64, ppc64); - - // MIPS: `ptr` will always be updated. - void wasmUnalignedStore(const wasm::MemoryAccessDesc& access, Register value, - Register memoryBase, Register ptr, - Register ptrScratch, Register tmp) -- DEFINED_ON(mips32, mips64); -+ DEFINED_ON(mips32, mips64, ppc64); - - // `ptr` will always be updated. - void wasmUnalignedStoreFP(const wasm::MemoryAccessDesc& access, - FloatRegister floatValue, Register memoryBase, - Register ptr, Register ptrScratch, Register tmp) -- DEFINED_ON(mips32, mips64); -+ DEFINED_ON(mips32, mips64, ppc64); - - // `ptr` will always be updated. - void wasmUnalignedStoreI64(const wasm::MemoryAccessDesc& access, - Register64 value, Register memoryBase, - Register ptr, Register ptrScratch, Register tmp) -- DEFINED_ON(mips32, mips64); -+ DEFINED_ON(mips32, mips64, ppc64); - - // wasm specific methods, used in both the wasm baseline compiler and ion. - - // The truncate-to-int32 methods do not bind the rejoin label; clients must - // do so if oolWasmTruncateCheckF64ToI32() can jump to it. - void wasmTruncateDoubleToUInt32(FloatRegister input, Register output, - bool isSaturating, Label* oolEntry) PER_ARCH; - void wasmTruncateDoubleToInt32(FloatRegister input, Register output, - bool isSaturating, - Label* oolEntry) PER_SHARED_ARCH; - void oolWasmTruncateCheckF64ToI32(FloatRegister input, Register output, - TruncFlags flags, wasm::BytecodeOffset off, - Label* rejoin) -- DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64, riscv64, ppc64, wasm32); - - void wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, - bool isSaturating, Label* oolEntry) PER_ARCH; - void wasmTruncateFloat32ToInt32(FloatRegister input, Register output, - bool isSaturating, - Label* oolEntry) PER_SHARED_ARCH; - void oolWasmTruncateCheckF32ToI32(FloatRegister input, Register output, - TruncFlags flags, wasm::BytecodeOffset off, - Label* rejoin) -- DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64, riscv64, ppc64, wasm32); - - // The truncate-to-int64 methods will always bind the `oolRejoin` label - // after the last emitted instruction. - void wasmTruncateDoubleToInt64(FloatRegister input, Register64 output, - bool isSaturating, Label* oolEntry, - Label* oolRejoin, FloatRegister tempDouble) -- DEFINED_ON(arm64, x86, x64, mips64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm64, x86, x64, mips64, loong64, riscv64, ppc64, wasm32); - void wasmTruncateDoubleToUInt64(FloatRegister input, Register64 output, - bool isSaturating, Label* oolEntry, - Label* oolRejoin, FloatRegister tempDouble) -- DEFINED_ON(arm64, x86, x64, mips64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm64, x86, x64, mips64, loong64, riscv64, ppc64, wasm32); - void oolWasmTruncateCheckF64ToI64(FloatRegister input, Register64 output, - TruncFlags flags, wasm::BytecodeOffset off, - Label* rejoin) -- DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64, riscv64, ppc64, wasm32); - - void wasmTruncateFloat32ToInt64(FloatRegister input, Register64 output, - bool isSaturating, Label* oolEntry, - Label* oolRejoin, FloatRegister tempDouble) -- DEFINED_ON(arm64, x86, x64, mips64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm64, x86, x64, mips64, loong64, riscv64, ppc64, wasm32); - void wasmTruncateFloat32ToUInt64(FloatRegister input, Register64 output, - bool isSaturating, Label* oolEntry, - Label* oolRejoin, FloatRegister tempDouble) -- DEFINED_ON(arm64, x86, x64, mips64, loong64, riscv64, wasm32); -+ DEFINED_ON(arm64, x86, x64, mips64, loong64, riscv64, ppc64, wasm32); - void oolWasmTruncateCheckF32ToI64(FloatRegister input, Register64 output, - TruncFlags flags, wasm::BytecodeOffset off, - Label* rejoin) -- DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64, riscv64, wasm32); -+ DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64, riscv64, ppc64, wasm32); - - // This function takes care of loading the callee's instance and pinned regs - // but it is the caller's responsibility to save/restore instance or pinned - // regs. - CodeOffset wasmCallImport(const wasm::CallSiteDesc& desc, - const wasm::CalleeDesc& callee); - - // WasmTableCallIndexReg must contain the index of the indirect call. This is -@@ -3882,17 +3889,17 @@ - // The System ABI frequently states that the high bits of a 64-bit register - // that holds a 32-bit return value are unpredictable, and C++ compilers will - // indeed generate code that leaves garbage in the upper bits. - // - // Adjust the contents of the 64-bit register `r` to conform to our internal - // convention, which requires predictable high bits. In practice, this means - // that the 32-bit value will be zero-extended or sign-extended to 64 bits as - // appropriate for the platform. -- void widenInt32(Register r) DEFINED_ON(arm64, x64, mips64, loong64, riscv64); -+ void widenInt32(Register r) DEFINED_ON(arm64, x64, mips64, loong64, riscv64, ppc64); - - // As enterFakeExitFrame(), but using register conventions appropriate for - // wasm stubs. - void enterFakeExitFrameForWasm(Register cxreg, Register scratch, - ExitFrameType type) PER_SHARED_ARCH; - - public: - // ======================================================================== -@@ -3941,72 +3948,72 @@ - const BaseIndex& mem, Register expected, - Register replacement, Register output) - DEFINED_ON(arm, arm64, x86_shared); - - void compareExchange(Scalar::Type type, const Synchronization& sync, - const Address& mem, Register expected, - Register replacement, Register valueTemp, - Register offsetTemp, Register maskTemp, Register output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void compareExchange(Scalar::Type type, const Synchronization& sync, - const BaseIndex& mem, Register expected, - Register replacement, Register valueTemp, - Register offsetTemp, Register maskTemp, Register output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - // x86: `expected` and `output` must be edx:eax; `replacement` is ecx:ebx. - // x64: `output` must be rax. - // ARM: Registers must be distinct; `replacement` and `output` must be - // (even,odd) pairs. - - void compareExchange64(const Synchronization& sync, const Address& mem, - Register64 expected, Register64 replacement, - Register64 output) -- DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64); -+ DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64, ppc64); - - void compareExchange64(const Synchronization& sync, const BaseIndex& mem, - Register64 expected, Register64 replacement, - Register64 output) -- DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64); -+ DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64, ppc64); - - // Exchange with memory. Return the value initially in memory. - // MIPS: `valueTemp`, `offsetTemp` and `maskTemp` must be defined for 8-bit - // and 16-bit wide operations. - - void atomicExchange(Scalar::Type type, const Synchronization& sync, - const Address& mem, Register value, Register output) - DEFINED_ON(arm, arm64, x86_shared); - - void atomicExchange(Scalar::Type type, const Synchronization& sync, - const BaseIndex& mem, Register value, Register output) - DEFINED_ON(arm, arm64, x86_shared); - - void atomicExchange(Scalar::Type type, const Synchronization& sync, - const Address& mem, Register value, Register valueTemp, - Register offsetTemp, Register maskTemp, Register output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void atomicExchange(Scalar::Type type, const Synchronization& sync, - const BaseIndex& mem, Register value, Register valueTemp, - Register offsetTemp, Register maskTemp, Register output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - // x86: `value` must be ecx:ebx; `output` must be edx:eax. - // ARM: `value` and `output` must be distinct and (even,odd) pairs. - // ARM64: `value` and `output` must be distinct. - - void atomicExchange64(const Synchronization& sync, const Address& mem, - Register64 value, Register64 output) -- DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64); -+ DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64, ppc64); - - void atomicExchange64(const Synchronization& sync, const BaseIndex& mem, - Register64 value, Register64 output) -- DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64); -+ DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64, ppc64); - - // Read-modify-write with memory. Return the value in memory before the - // operation. - // - // x86-shared: - // For 8-bit operations, `value` and `output` must have a byte subregister. - // For Add and Sub, `temp` must be invalid. - // For And, Or, and Xor, `output` must be eax and `temp` must have a byte -@@ -4032,46 +4039,46 @@ - - void atomicFetchOp(Scalar::Type type, const Synchronization& sync, - AtomicOp op, Imm32 value, const BaseIndex& mem, - Register temp, Register output) DEFINED_ON(x86_shared); - - void atomicFetchOp(Scalar::Type type, const Synchronization& sync, - AtomicOp op, Register value, const Address& mem, - Register valueTemp, Register offsetTemp, Register maskTemp, -- Register output) DEFINED_ON(mips_shared, loong64, riscv64); -+ Register output) DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void atomicFetchOp(Scalar::Type type, const Synchronization& sync, - AtomicOp op, Register value, const BaseIndex& mem, - Register valueTemp, Register offsetTemp, Register maskTemp, -- Register output) DEFINED_ON(mips_shared, loong64, riscv64); -+ Register output) DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - // x86: - // `temp` must be ecx:ebx; `output` must be edx:eax. - // x64: - // For Add and Sub, `temp` is ignored. - // For And, Or, and Xor, `output` must be rax. - // ARM: - // `temp` and `output` must be (even,odd) pairs and distinct from `value`. - // ARM64: - // Registers `value`, `temp`, and `output` must all differ. - - void atomicFetchOp64(const Synchronization& sync, AtomicOp op, - Register64 value, const Address& mem, Register64 temp, - Register64 output) -- DEFINED_ON(arm, arm64, x64, mips64, loong64, riscv64); -+ DEFINED_ON(arm, arm64, x64, mips64, loong64, riscv64, ppc64); - - void atomicFetchOp64(const Synchronization& sync, AtomicOp op, - const Address& value, const Address& mem, - Register64 temp, Register64 output) DEFINED_ON(x86); - - void atomicFetchOp64(const Synchronization& sync, AtomicOp op, - Register64 value, const BaseIndex& mem, Register64 temp, - Register64 output) -- DEFINED_ON(arm, arm64, x64, mips64, loong64, riscv64); -+ DEFINED_ON(arm, arm64, x64, mips64, loong64, riscv64, ppc64); - - void atomicFetchOp64(const Synchronization& sync, AtomicOp op, - const Address& value, const BaseIndex& mem, - Register64 temp, Register64 output) DEFINED_ON(x86); - - // x64: - // `value` can be any register. - // ARM: -@@ -4079,24 +4086,24 @@ - // ARM64: - // Registers `value` and `temp` must differ. - - void atomicEffectOp64(const Synchronization& sync, AtomicOp op, - Register64 value, const Address& mem) DEFINED_ON(x64); - - void atomicEffectOp64(const Synchronization& sync, AtomicOp op, - Register64 value, const Address& mem, Register64 temp) -- DEFINED_ON(arm, arm64, mips64, loong64, riscv64); -+ DEFINED_ON(arm, arm64, mips64, loong64, riscv64, ppc64); - - void atomicEffectOp64(const Synchronization& sync, AtomicOp op, - Register64 value, const BaseIndex& mem) DEFINED_ON(x64); - - void atomicEffectOp64(const Synchronization& sync, AtomicOp op, - Register64 value, const BaseIndex& mem, Register64 temp) -- DEFINED_ON(arm, arm64, mips64, loong64, riscv64); -+ DEFINED_ON(arm, arm64, mips64, loong64, riscv64, ppc64); - - // 64-bit atomic load. On 64-bit systems, use regular load with - // Synchronization::Load, not this method. - // - // x86: `temp` must be ecx:ebx; `output` must be edx:eax. - // ARM: `output` must be (even,odd) pair. - - void atomicLoad64(const Synchronization& sync, const Address& mem, -@@ -4139,44 +4146,44 @@ - Register replacement, Register output) - DEFINED_ON(arm, arm64, x86_shared); - - void wasmCompareExchange(const wasm::MemoryAccessDesc& access, - const Address& mem, Register expected, - Register replacement, Register valueTemp, - Register offsetTemp, Register maskTemp, - Register output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void wasmCompareExchange(const wasm::MemoryAccessDesc& access, - const BaseIndex& mem, Register expected, - Register replacement, Register valueTemp, - Register offsetTemp, Register maskTemp, - Register output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void wasmAtomicExchange(const wasm::MemoryAccessDesc& access, - const Address& mem, Register value, Register output) - DEFINED_ON(arm, arm64, x86_shared); - - void wasmAtomicExchange(const wasm::MemoryAccessDesc& access, - const BaseIndex& mem, Register value, Register output) - DEFINED_ON(arm, arm64, x86_shared); - - void wasmAtomicExchange(const wasm::MemoryAccessDesc& access, - const Address& mem, Register value, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void wasmAtomicExchange(const wasm::MemoryAccessDesc& access, - const BaseIndex& mem, Register value, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, AtomicOp op, - Register value, const Address& mem, Register temp, - Register output) DEFINED_ON(arm, arm64, x86_shared); - - void wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, AtomicOp op, - Imm32 value, const Address& mem, Register temp, - Register output) DEFINED_ON(x86_shared); -@@ -4188,23 +4195,23 @@ - void wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, AtomicOp op, - Imm32 value, const BaseIndex& mem, Register temp, - Register output) DEFINED_ON(x86_shared); - - void wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, AtomicOp op, - Register value, const Address& mem, Register valueTemp, - Register offsetTemp, Register maskTemp, - Register output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, AtomicOp op, - Register value, const BaseIndex& mem, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - // Read-modify-write with memory. Return no value. - // - // MIPS: `valueTemp`, `offsetTemp` and `maskTemp` must be defined for 8-bit - // and 16-bit wide operations. - - void wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access, AtomicOp op, - Register value, const Address& mem, Register temp) -@@ -4221,23 +4228,23 @@ - void wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access, AtomicOp op, - Imm32 value, const BaseIndex& mem, Register temp) - DEFINED_ON(x86_shared); - - void wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access, AtomicOp op, - Register value, const Address& mem, - Register valueTemp, Register offsetTemp, - Register maskTemp) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access, AtomicOp op, - Register value, const BaseIndex& mem, - Register valueTemp, Register offsetTemp, - Register maskTemp) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - // 64-bit wide operations. - - // 64-bit atomic load. On 64-bit systems, use regular wasm load with - // Synchronization::Load, not this method. - // - // x86: `temp` must be ecx:ebx; `output` must be edx:eax. - // ARM: `temp` should be invalid; `output` must be (even,odd) pair. -@@ -4287,22 +4294,22 @@ - // ARM: Registers must be distinct; `temp` and `output` must be (even,odd) - // pairs. - // MIPS: Registers must be distinct. - // MIPS32: `temp` should be invalid. - - void wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, AtomicOp op, - Register64 value, const Address& mem, - Register64 temp, Register64 output) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, x64); -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, x64); - - void wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, AtomicOp op, - Register64 value, const BaseIndex& mem, - Register64 temp, Register64 output) -- DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, x64); -+ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, ppc64, x64); - - void wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, AtomicOp op, - const Address& value, const Address& mem, - Register64 temp, Register64 output) DEFINED_ON(x86); - - void wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, AtomicOp op, - const Address& value, const BaseIndex& mem, - Register64 temp, Register64 output) DEFINED_ON(x86); -@@ -4345,44 +4352,44 @@ - Register replacement, Register temp, - AnyRegister output) DEFINED_ON(arm, arm64, x86_shared); - - void compareExchangeJS(Scalar::Type arrayType, const Synchronization& sync, - const Address& mem, Register expected, - Register replacement, Register valueTemp, - Register offsetTemp, Register maskTemp, Register temp, - AnyRegister output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void compareExchangeJS(Scalar::Type arrayType, const Synchronization& sync, - const BaseIndex& mem, Register expected, - Register replacement, Register valueTemp, - Register offsetTemp, Register maskTemp, Register temp, - AnyRegister output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void atomicExchangeJS(Scalar::Type arrayType, const Synchronization& sync, - const Address& mem, Register value, Register temp, - AnyRegister output) DEFINED_ON(arm, arm64, x86_shared); - - void atomicExchangeJS(Scalar::Type arrayType, const Synchronization& sync, - const BaseIndex& mem, Register value, Register temp, - AnyRegister output) DEFINED_ON(arm, arm64, x86_shared); - - void atomicExchangeJS(Scalar::Type arrayType, const Synchronization& sync, - const Address& mem, Register value, Register valueTemp, - Register offsetTemp, Register maskTemp, Register temp, - AnyRegister output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void atomicExchangeJS(Scalar::Type arrayType, const Synchronization& sync, - const BaseIndex& mem, Register value, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register temp, AnyRegister output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void atomicFetchOpJS(Scalar::Type arrayType, const Synchronization& sync, - AtomicOp op, Register value, const Address& mem, - Register temp1, Register temp2, AnyRegister output) - DEFINED_ON(arm, arm64, x86_shared); - - void atomicFetchOpJS(Scalar::Type arrayType, const Synchronization& sync, - AtomicOp op, Register value, const BaseIndex& mem, -@@ -4398,23 +4405,23 @@ - AtomicOp op, Imm32 value, const BaseIndex& mem, - Register temp1, Register temp2, AnyRegister output) - DEFINED_ON(x86_shared); - - void atomicFetchOpJS(Scalar::Type arrayType, const Synchronization& sync, - AtomicOp op, Register value, const Address& mem, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register temp, AnyRegister output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void atomicFetchOpJS(Scalar::Type arrayType, const Synchronization& sync, - AtomicOp op, Register value, const BaseIndex& mem, - Register valueTemp, Register offsetTemp, - Register maskTemp, Register temp, AnyRegister output) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, - AtomicOp op, Register value, const Address& mem, - Register temp) DEFINED_ON(arm, arm64, x86_shared); - - void atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, - AtomicOp op, Register value, const BaseIndex& mem, - Register temp) DEFINED_ON(arm, arm64, x86_shared); -@@ -4426,23 +4433,23 @@ - void atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, - AtomicOp op, Imm32 value, const BaseIndex& mem, - Register temp) DEFINED_ON(x86_shared); - - void atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, - AtomicOp op, Register value, const Address& mem, - Register valueTemp, Register offsetTemp, - Register maskTemp) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, - AtomicOp op, Register value, const BaseIndex& mem, - Register valueTemp, Register offsetTemp, - Register maskTemp) -- DEFINED_ON(mips_shared, loong64, riscv64); -+ DEFINED_ON(mips_shared, loong64, riscv64, ppc64); - - void atomicIsLockFreeJS(Register value, Register output); - - // ======================================================================== - // Spectre Mitigations. - // - // Spectre attacks are side-channel attacks based on cache pollution or - // slow-execution of some instructions. We have multiple spectre mitigations -@@ -5234,17 +5241,17 @@ - // On ARM64, the StackPointer is implemented as two synchronized registers. - // Code shared across platforms must use these functions to be valid. - template - inline void addToStackPtr(T t); - template - inline void addStackPtrTo(T t); - - void subFromStackPtr(Imm32 imm32) -- DEFINED_ON(mips32, mips64, loong64, riscv64, wasm32, arm, x86, x64); -+ DEFINED_ON(mips32, mips64, loong64, riscv64, ppc64, wasm32, arm, x86, x64); - void subFromStackPtr(Register reg); - - template - void subStackPtrFrom(T t) { - subPtr(getStackPointer(), t); - } - - template -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/MoveEmitter.h ---- a/js/src/jit/MoveEmitter.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/MoveEmitter.h Thu Sep 07 19:51:49 2023 -0700 -@@ -16,16 +16,18 @@ - #elif defined(JS_CODEGEN_MIPS32) - # include "jit/mips32/MoveEmitter-mips32.h" - #elif defined(JS_CODEGEN_MIPS64) - # include "jit/mips64/MoveEmitter-mips64.h" - #elif defined(JS_CODEGEN_LOONG64) - # include "jit/loong64/MoveEmitter-loong64.h" - #elif defined(JS_CODEGEN_RISCV64) - # include "jit/riscv64/MoveEmitter-riscv64.h" -+#elif defined(JS_CODEGEN_PPC64) -+# include "jit/ppc64/MoveEmitter-ppc64.h" - #elif defined(JS_CODEGEN_WASM32) - # include "jit/wasm32/MoveEmitter-wasm32.h" - #elif defined(JS_CODEGEN_NONE) - # include "jit/none/MoveEmitter-none.h" - #else - # error "Unknown architecture!" - #endif - -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/RegisterAllocator.h ---- a/js/src/jit/RegisterAllocator.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/RegisterAllocator.h Thu Sep 07 19:51:49 2023 -0700 -@@ -289,17 +289,17 @@ - void dumpInstructions(const char* who); - - public: - template - static void takeWasmRegisters(TakeableSet& regs) { - #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || \ - defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32) || \ - defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || \ -- defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_RISCV64) || defined(JS_CODEGEN_PPC64) - regs.take(HeapReg); - #endif - MOZ_ASSERT(!regs.has(FramePointer)); - } - }; - - static inline AnyRegister GetFixedRegister(const LDefinition* def, - const LUse* use) { -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/Registers.h ---- a/js/src/jit/Registers.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/Registers.h Thu Sep 07 19:51:49 2023 -0700 -@@ -19,16 +19,18 @@ - #elif defined(JS_CODEGEN_MIPS32) - # include "jit/mips32/Architecture-mips32.h" - #elif defined(JS_CODEGEN_MIPS64) - # include "jit/mips64/Architecture-mips64.h" - #elif defined(JS_CODEGEN_LOONG64) - # include "jit/loong64/Architecture-loong64.h" - #elif defined(JS_CODEGEN_RISCV64) - # include "jit/riscv64/Architecture-riscv64.h" -+#elif defined(JS_CODEGEN_PPC64) -+# include "jit/ppc64/Architecture-ppc64.h" - #elif defined(JS_CODEGEN_WASM32) - # include "jit/wasm32/Architecture-wasm32.h" - #elif defined(JS_CODEGEN_NONE) - # include "jit/none/Architecture-none.h" - #else - # error "Unknown architecture!" - #endif - -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/SharedICHelpers-inl.h ---- a/js/src/jit/SharedICHelpers-inl.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/SharedICHelpers-inl.h Thu Sep 07 19:51:49 2023 -0700 -@@ -16,16 +16,18 @@ - #elif defined(JS_CODEGEN_ARM64) - # include "jit/arm64/SharedICHelpers-arm64-inl.h" - #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) - # include "jit/mips-shared/SharedICHelpers-mips-shared-inl.h" - #elif defined(JS_CODEGEN_LOONG64) - # include "jit/loong64/SharedICHelpers-loong64-inl.h" - #elif defined(JS_CODEGEN_RISCV64) - # include "jit/riscv64/SharedICHelpers-riscv64-inl.h" -+#elif defined(JS_CODEGEN_PPC64) -+# include "jit/ppc64/SharedICHelpers-ppc64-inl.h" - #elif defined(JS_CODEGEN_WASM32) - # include "jit/wasm32/SharedICHelpers-wasm32-inl.h" - #elif defined(JS_CODEGEN_NONE) - # include "jit/none/SharedICHelpers-none-inl.h" - #else - # error "Unknown architecture!" - #endif - -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/SharedICHelpers.h ---- a/js/src/jit/SharedICHelpers.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/SharedICHelpers.h Thu Sep 07 19:51:49 2023 -0700 -@@ -16,16 +16,18 @@ - #elif defined(JS_CODEGEN_ARM64) - # include "jit/arm64/SharedICHelpers-arm64.h" - #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) - # include "jit/mips-shared/SharedICHelpers-mips-shared.h" - #elif defined(JS_CODEGEN_LOONG64) - # include "jit/loong64/SharedICHelpers-loong64.h" - #elif defined(JS_CODEGEN_RISCV64) - # include "jit/riscv64/SharedICHelpers-riscv64.h" -+#elif defined(JS_CODEGEN_PPC64) -+# include "jit/ppc64/SharedICHelpers-ppc64.h" - #elif defined(JS_CODEGEN_WASM32) - # include "jit/wasm32/SharedICHelpers-wasm32.h" - #elif defined(JS_CODEGEN_NONE) - # include "jit/none/SharedICHelpers-none.h" - #else - # error "Unknown architecture!" - #endif - -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/SharedICRegisters.h ---- a/js/src/jit/SharedICRegisters.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/SharedICRegisters.h Thu Sep 07 19:51:49 2023 -0700 -@@ -18,16 +18,18 @@ - #elif defined(JS_CODEGEN_MIPS32) - # include "jit/mips32/SharedICRegisters-mips32.h" - #elif defined(JS_CODEGEN_MIPS64) - # include "jit/mips64/SharedICRegisters-mips64.h" - #elif defined(JS_CODEGEN_LOONG64) - # include "jit/loong64/SharedICRegisters-loong64.h" - #elif defined(JS_CODEGEN_RISCV64) - # include "jit/riscv64/SharedICRegisters-riscv64.h" -+#elif defined(JS_CODEGEN_PPC64) -+# include "jit/ppc64/SharedICRegisters-ppc64.h" - #elif defined(JS_CODEGEN_WASM32) - # include "jit/wasm32/SharedICRegisters-wasm32.h" - #elif defined(JS_CODEGEN_NONE) - # include "jit/none/SharedICRegisters-none.h" - #else - # error "Unknown architecture!" - #endif - -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/moz.build ---- a/js/src/jit/moz.build Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/moz.build Thu Sep 07 19:51:49 2023 -0700 -@@ -247,16 +247,28 @@ - "riscv64/extension/extension-riscv-zifencei.cc", - "riscv64/Lowering-riscv64.cpp", - "riscv64/MacroAssembler-riscv64.cpp", - "riscv64/MoveEmitter-riscv64.cpp", - "riscv64/Trampoline-riscv64.cpp", - ] - if CONFIG["JS_SIMULATOR_RISCV64"]: - UNIFIED_SOURCES += ["riscv64/Simulator-riscv64.cpp"] -+elif CONFIG["JS_CODEGEN_PPC64"]: -+ UNIFIED_SOURCES += [ -+ "ppc64/Architecture-ppc64.cpp", -+ "ppc64/Assembler-ppc64.cpp", -+ "ppc64/Bailouts-ppc64.cpp", -+ "ppc64/CodeGenerator-ppc64.cpp", -+ "ppc64/Lowering-ppc64.cpp", -+ "ppc64/MacroAssembler-ppc64.cpp", -+ "ppc64/MoveEmitter-ppc64.cpp", -+ "ppc64/Trampoline-ppc64.cpp", -+ "shared/AtomicOperations-shared-jit.cpp", -+ ] - elif CONFIG["JS_CODEGEN_WASM32"]: - UNIFIED_SOURCES += [ - "wasm32/CodeGenerator-wasm32.cpp", - "wasm32/MacroAssembler-wasm32.cpp", - "wasm32/Trampoline-wasm32.cpp", - ] - - # Generate jit/MIROpsGenerated.h from jit/MIROps.yaml -diff -r cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -0700 -@@ -0,0 +1,137 @@ -+/* -*- 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 -+#include -+ -+#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 -+# 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, bool codeIsThreadLocal) { -+ intptr_t end = reinterpret_cast(code) + size; -+ __builtin___clear_cache(reinterpret_cast(code), -+ reinterpret_cast(end)); -+} -+ -+// 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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -0700 -@@ -0,0 +1,625 @@ -+/* -*- 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 { -+ -+// Not used on PPC. -+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 PowerPC 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 = 24; -+ -+ 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). -+ -+ // 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! -+ ) & 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 -+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 -+ static SetType LiveAsIndexableSet(SetType s) { -+ return SetType(0); -+ } -+ -+ template -+ static SetType AllocatableAsIndexableSet(SetType s) { -+ static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable"); -+ return LiveAsIndexableSet(s); -+ } -+ -+ static TypedRegisterSet ReduceSetForPush( -+ const TypedRegisterSet& s); -+ static uint32_t GetPushSizeInBytes(const TypedRegisterSet& s); -+ uint32_t getRegisterDumpOffsetInBytes(); -+}; -+ -+template <> -+inline FloatRegister::SetType -+FloatRegister::LiveAsIndexableSet(SetType set) { -+ return set & FloatRegisters::AllSingleMask; -+} -+ -+template <> -+inline FloatRegister::SetType -+FloatRegister::LiveAsIndexableSet(SetType set) { -+ return set & FloatRegisters::AllDoubleMask; -+} -+ -+template <> -+inline FloatRegister::SetType -+FloatRegister::LiveAsIndexableSet(SetType set) { -+ return set; -+} -+ -+inline bool hasUnaliasedDouble() { return false; } -+inline bool hasMultiAlias() { return false; } -+ -+// 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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -0700 -@@ -0,0 +1,1995 @@ -+/* -*- 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::RefOrNull: -+ case MIRType::StackResults: { -+ if (usedGPRs_ > 7) { -+ MOZ_ASSERT(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(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"); -+ } -+} -+ -+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(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(&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 awjc; -+ -+ while (reader.more()) { -+ size_t offset = reader.readUnsigned(); -+ Instruction* inst = (Instruction*)(code->raw() + offset); -+ if (awjc.isNothing()) { -+ awjc.emplace(code); -+ } -+ TraceOneDataRelocation(trc, inst); -+ } -+} -+ -+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(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(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(inst0)->setImm16(value >> 16); -+ reinterpret_cast(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 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 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(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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -0700 -@@ -0,0 +1,1831 @@ -+/* -*- 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; // wasm -+ -+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 in RegExpMatcher instruction (do not use JSReturnOperand). -+static constexpr Register RegExpMatcherRegExpReg = CallTempReg0; -+static constexpr Register RegExpMatcherStringReg = CallTempReg1; -+static constexpr Register RegExpMatcherLastIndexReg = CallTempReg2; -+ -+// Registers used in RegExpTester instruction (do not use ReturnReg). -+static constexpr Register RegExpTesterRegExpReg = CallTempReg0; -+static constexpr Register RegExpTesterStringReg = CallTempReg1; -+static constexpr Register RegExpTesterLastIndexReg = 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 WasmTlsReg = 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; -+ -+// 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_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, -+ -+ 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 codeLabels_; -+ js::Vector jumps_; -+ //js::Vector 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(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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -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 -+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 -+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 -+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 -+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 -+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 -+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 -+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 -+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 -+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 -+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 -+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 cd548157ebca -r 99b51ba09f3f js/src/jit/ppc64/Bailouts-ppc64.cpp ---- /dev/null Thu Jan 01 00:00:00 1970 +0000 -+++ b/js/src/jit/ppc64/Bailouts-ppc64.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -0,0 +1,46 @@ -+/* -*- 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/Bailouts-ppc64.h" -+#include "jit/Bailouts.h" -+#include "jit/SafepointIndex.h" -+#include "jit/ScriptFromCalleeToken.h" -+ -+#include "vm/JSContext.h" -+#include "vm/Realm.h" -+#include "vm/JSScript-inl.h" -+ -+using namespace js; -+using namespace js::jit; -+ -+BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& activations, -+ BailoutStack* bailout) -+ : machine_(bailout->machineState()) -+{ -+ uint8_t* sp = bailout->parentStackPointer(); -+ framePointer_ = sp + bailout->frameSize(); -+ topFrameSize_ = framePointer_ - sp; -+ -+ JSScript* script = ScriptFromCalleeToken(((JitFrameLayout*) framePointer_)->calleeToken()); -+ topIonScript_ = script->ionScript(); -+ -+ attachOnJitActivation(activations); -+ snapshotOffset_ = bailout->snapshotOffset(); -+} -+ -+BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& activations, -+ InvalidationBailoutStack* bailout) -+ : machine_(bailout->machine()) -+{ -+ framePointer_ = (uint8_t*) bailout->fp(); -+ topFrameSize_ = framePointer_ - bailout->sp(); -+ topIonScript_ = bailout->ionScript(); -+ attachOnJitActivation(activations); -+ -+ uint8_t* returnAddressToFp_ = bailout->osiPointReturnAddress(); -+ const OsiIndex* osiIndex = topIonScript_->getOsiIndex(returnAddressToFp_); -+ snapshotOffset_ = osiIndex->snapshotOffset(); -+} -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/ppc64/Bailouts-ppc64.h ---- /dev/null Thu Jan 01 00:00:00 1970 +0000 -+++ b/js/src/jit/ppc64/Bailouts-ppc64.h Thu Sep 07 19:51:49 2023 -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_ppc64_Bailouts_ppc64_h -+#define jit_ppc64_Bailouts_ppc64_h -+ -+#include "jit/Bailouts.h" -+#include "jit/JitRealm.h" -+ -+namespace js { -+namespace jit { -+ -+class BailoutStack -+{ -+ RegisterDump::FPUArray fpregs_; -+ RegisterDump::GPRArray regs_; -+ uintptr_t frameSize_; -+ uintptr_t snapshotOffset_; -+ -+ public: -+ MachineState machineState() { -+ return MachineState::FromBailout(regs_, fpregs_); -+ } -+ uint32_t snapshotOffset() const { -+ return snapshotOffset_; -+ } -+ uint32_t frameSize() const { -+ return frameSize_; -+ } -+ uint8_t* parentStackPointer() { -+ return (uint8_t*)this + sizeof(BailoutStack); -+ } -+ static size_t offsetOfFrameSize() { -+ return offsetof(BailoutStack, frameSize_); -+ } -+}; -+ -+} // namespace jit -+} // namespace js -+ -+#endif /* jit_ppc64_Bailouts_ppc64_h */ -diff -r cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -0700 -@@ -0,0 +1,3631 @@ -+/* -*- 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 "vm/TraceLogging.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 -+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 -+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(masm.getStackPointer(), ToStackOffset(&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 -+#error does this actually get compiled? -+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); -+} -+ -+FrameSizeClass -+FrameSizeClass::FromDepth(uint32_t frameDepth) -+{ -+ return FrameSizeClass::None(); -+} -+ -+FrameSizeClass -+FrameSizeClass::ClassLimit() -+{ -+ return FrameSizeClass(0); -+} -+ -+uint32_t -+FrameSizeClass::frameSize() const -+{ -+ MOZ_CRASH("PPC64 does not use frame size classes"); -+} -+ -+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); -+ -+ // Though the assembler doesn't track all frame pushes, at least make sure -+ // the known value makes sense. We can't use bailout tables if the stack -+ // isn't properly aligned to the static frame size. -+ MOZ_ASSERT_IF(frameClass_ != FrameSizeClass::None(), -+ frameClass_.frameSize() == masm.framePushed()); -+ -+ // We don't use table bailouts because retargeting is easier this way. -+ 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<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(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(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 { -+ if (lhs != dest) -+ 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<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 if (dest != lhs) -+ masm.move32(lhs, dest); -+ break; -+ case JSOp::Rsh: -+ if (shift) -+ masm.as_srawi(dest, lhs, shift); -+ else if (dest != lhs) -+ masm.move32(lhs, dest); -+ break; -+ case JSOp::Ursh: -+ if (shift) { -+ masm.x_srwi(dest, lhs, shift); -+#if(0) -+ } else if (ins->mir()->toUrsh()->fallible()) { -+ // x >>> 0 can overflow. -+ masm.as_extsw(ScratchRegister, lhs); -+ bailoutCmp32(Assembler::LessThan, ScratchRegister, Imm32(0), ins->snapshot()); -+ } else { -+ masm.move32(lhs, dest); -+ } -+#else -+ } else { -+ // x >>> 0 can overflow. -+ if (ins->mir()->toUrsh()->fallible()) -+ bailoutCmp32(Assembler::LessThan, lhs, Imm32(0), ins->snapshot()); -+ if (dest != lhs) -+ masm.move32(lhs, dest); -+ } -+#endif -+ 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. -+#if(0) -+ masm.as_extsw(ScratchRegister, lhs); -+ bailoutCmp32(Assembler::LessThan, ScratchRegister, Imm32(0), ins->snapshot()); -+#else -+ bailoutCmp32(Assembler::LessThan, dest, Imm32(0), ins->snapshot()); -+#endif -+ } -+ 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(), 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)); -+ } -+ int32_t offset = ToStackOffset(a); -+ MOZ_ASSERT((offset & 3) == 0); -+ -+ return MoveOperand(StackPointer, offset); -+} -+ -+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); -+} -+ -+void -+CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) -+{ -+ ADBlock(); -+ masm.memoryBarrier(ins->type()); -+} -+ -+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 -+{ -+ 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 -+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 -+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(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(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(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; -+ 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(size), isSigned ? SignExtend : ZeroExtend); -+ } -+ 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(size), isSigned ? SignExtend : ZeroExtend); -+ } -+ 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(size), isSigned ? SignExtend : ZeroExtend); -+ } -+ -+ 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 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_CRASH("NYI"); -+} -+ -+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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -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; -+ -+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 -+ void bailoutCmp32(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot* snapshot) { -+ Label bail; -+ masm.branch32(c, lhs, rhs, &bail); -+ bailoutFrom(&bail, snapshot); -+ } -+ template -+ void bailoutTest32(Assembler::Condition c, Register lhs, T rhs, LSnapshot* snapshot) { -+ Label bail; -+ masm.branchTest32(c, lhs, rhs, &bail); -+ bailoutFrom(&bail, snapshot); -+ } -+ template -+ 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 -+ 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 -+ 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 -+ void emitWasmLoad(T* ins); -+ template -+ void emitWasmStore(T* ins); -+ -+ void generateInvalidateEpilogue(); -+ -+ // Generating a result. -+ template -+ 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 -+ 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 -+ void emitWasmLoadI64(T* ins); -+ template -+ 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 -+{ -+ 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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -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(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(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(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 -+class LWasmUnalignedLoadBase : public details::LWasmLoadBase -+{ -+ public: -+ typedef LWasmLoadBase 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 -+{ -+ 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 -+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 -+{ -+ 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 -+{ -+ 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 -+{ -+ 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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -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* ins, MDefinition* mir, -+ MDefinition* input) { -+ ins->setInt64Operand(0, useInt64RegisterAtStart(input)); -+ defineInt64ReuseInput(ins, mir, 0); -+} -+ -+void LIRGeneratorPPC64::lowerForALUInt64( -+ LInstructionHelper* 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 -+void -+LIRGeneratorPPC64::lowerForShiftInt64(LInstructionHelper* 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* ins, MDefinition* mir, -+ MDefinition* lhs, MDefinition* rhs); -+template void LIRGeneratorPPC64::lowerForShiftInt64( -+ LInstructionHelper* 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 -+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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -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* ins, -+ MDefinition* mir, MDefinition* input); -+ void lowerForALUInt64( -+ LInstructionHelper* ins, -+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs); -+ -+ void lowerForMulInt64(LMulI64* ins, MMul* mir, MDefinition* lhs, MDefinition* rhs); -+ template -+ void lowerForShiftInt64(LInstructionHelper* 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 -+ 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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -0700 -@@ -0,0 +1,2681 @@ -+/* -*- 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::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); -+} -+ -+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 -+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 -+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 -+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 -+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 -+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 -+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 -+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 -+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 -+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::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::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 -+void -+MacroAssembler::branch32(Condition cond, Register lhs, Register rhs, L label) -+{ -+ ma_bc(lhs, rhs, label, cond); -+} -+ -+template -+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 -+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 -+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) -+{ -+ MOZ_CRASH(); -+} -+ -+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) -+{ -+ MOZ_CRASH(); -+} -+ -+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 -+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 -+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 -+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 -+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 -+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 -+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 -+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 -+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 -+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::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_CRASH("No known use cases"); -+} -+ -+void -+MacroAssembler::cmp32Load32(Condition cond, Register lhs, Register rhs, -+ const Address& src, Register dest) -+{ -+ MOZ_CRASH("No known use cases"); -+} -+ -+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. -+ -+void -+MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr) -+{ -+ ma_sd(src, addr); -+} -+void -+MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& addr) -+{ -+ ma_sd(src, addr); -+} -+ -+void -+MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr) -+{ -+ ma_ss(src, addr); -+} -+void -+MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr) -+{ -+ 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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -0700 -@@ -0,0 +1,5843 @@ -+/* -*- 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 -+ -+#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 "vm/JitActivation.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. -+ -+uint32_t -+MacroAssemblerPPC64::ma_load(Register dest, Address address, -+ LoadStoreSize size, LoadStoreExtension extension) -+{ -+ // ADBlock(); // spammy -+ int16_t encodedOffset; -+ uint32_t loadInst; -+ Register base; -+ MOZ_ASSERT(extension == ZeroExtend || extension == SignExtend); -+ MOZ_ASSERT(address.base != ScratchRegister); -+ -+ // XXX: Consider spinning this off into a separate function since the -+ // logic gets repeated. -+ // XXX: Consider, for lwa/ld, adding any unaligned constant offset to r12 -+ // so we can use lwa/ld and decomplexify this code and wasmLoad/StoreImpl -+ // by not having to return aberrant values to mark a split access. -+ // (See also ma_store below.) -+ 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 must not assert. -+ switch (size) { -+ case SizeByte: -+ as_lbz(dest, base, encodedOffset); -+ loadInst = asMasm().size() - 4; -+ if (SignExtend == extension) -+ as_extsb(dest, dest); -+ break; -+ case SizeHalfWord: -+ if (SignExtend == extension) -+ as_lha(dest, base, encodedOffset); -+ else -+ as_lhz(dest, base, encodedOffset); -+ loadInst = asMasm().size() - 4; -+ break; -+ case SizeWord: -+ if (ZeroExtend == extension) { -+ as_lwz(dest, base, encodedOffset); -+ loadInst = asMasm().size() - 4; -+ } else { -+ // lwa only valid if the offset is word-aligned. -+ if (encodedOffset & 0x03) { -+ as_lwz(dest, base, encodedOffset); -+ loadInst = asMasm().size() - 4; -+ as_extsw(dest, dest); -+ } else { -+ as_lwa(dest, base, encodedOffset); -+ loadInst = asMasm().size() - 4; -+ } -+ } -+ break; -+ case SizeDouble: -+ // ld only valid if the offset is word-aligned. -+ if (encodedOffset & 0x03) { -+ // Load as two word halves. ENDIAN! -+ Register t = (dest == ScratchRegister && base == SecondScratchReg) ? ThirdScratchReg : (dest == ScratchRegister) ? SecondScratchReg : ScratchRegister; -+ MOZ_ASSERT(base != t); -+ -+ // There are two loads here, so mark the highest address. -+ as_lwz(t, base, encodedOffset+4); // hi -+ loadInst = asMasm().size() - 4; -+ // Load dest last, in case dest == base (not rare). -+ as_lwz(dest, base, encodedOffset); // lo -+ as_rldicr(t, t, 32, 31); // shift -+ as_or(dest, dest, t); // merge -+ loadInst |= 1; // advise two marks required -+ } else { -+ as_ld(dest, base, encodedOffset); -+ loadInst = asMasm().size() - 4; -+ } -+ break; -+ default: -+ MOZ_CRASH("Invalid argument for ma_load"); -+ } -+ -+ return loadInst; -+} -+ -+// XXX: LoadStoreExtension not used -+uint32_t -+MacroAssemblerPPC64::ma_store(Register data, Address address, LoadStoreSize size, -+ LoadStoreExtension extension) -+{ -+ //ADBlock(); // spammy -+ int16_t encodedOffset; -+ uint32_t loadInst = 0; -+ Register base; -+ MOZ_ASSERT(address.base != ScratchRegister); -+ -+ // XXX: as above -+ // Use worst-case here in case we have to break a double store apart. -+ if (!Imm16::IsInSignedRange(address.offset + 4)) { -+ 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: -+ as_stb(data, base, encodedOffset); -+ loadInst = asMasm().size() - 4; -+ break; -+ case SizeHalfWord: -+ as_sth(data, base, encodedOffset); -+ loadInst = asMasm().size() - 4; -+ break; -+ case SizeWord: -+ as_stw(data, base, encodedOffset); -+ loadInst = asMasm().size() - 4; -+ break; -+ case SizeDouble: -+ // std only valid if the offset is word-aligned. -+ if (encodedOffset & 0x03) { -+ // Store as two word halves. ENDIAN! -+ Register t = (data == ScratchRegister && base == SecondScratchReg) ? ThirdScratchReg : (data == ScratchRegister) ? SecondScratchReg : ScratchRegister; -+ MOZ_ASSERT(base != t); -+ MOZ_ASSERT(data != t); -+ -+ as_stw(data, base, encodedOffset); // lo -+ loadInst = asMasm().size() - 4; -+ as_rldicl(t, data, 32, 32); // "srdi" -+ as_stw(t, base, encodedOffset+4); // hi -+ loadInst |= 1; // advise two marks required -+ } else { -+ as_std(data, base, encodedOffset); -+ loadInst = asMasm().size() - 4; -+ } -+ break; -+ default: -+ MOZ_CRASH("Invalid argument for ma_store"); -+ } -+ -+ return loadInst; -+} -+ -+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 -+void -+MacroAssemblerPPC64::ma_bc(CRegisterID cr, T c, Label* label, JumpKind jumpKind) -+{ -+ ADBlock(); -+ // Branch on the condition bit in the specified condition register. -+ // XXX: Likely bits NYI. -+ 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(value)); -+ -+ ma_li(ScratchRegister, imm); -+ asMasm().moveToDouble(ScratchRegister, dest); -+} -+ -+void -+MacroAssemblerPPC64::ma_ls(FloatRegister ft, Address address) -+{ -+ if (Imm16::IsInSignedRange(address.offset)) { -+ as_lfs(ft, address.base, address.offset); -+ } else { -+ MOZ_ASSERT(address.base != ScratchRegister); -+ ma_li(ScratchRegister, Imm32(address.offset)); -+ as_lfsx(ft, address.base, ScratchRegister); -+ } -+} -+ -+void -+MacroAssemblerPPC64::ma_ld(FloatRegister ft, Address address) -+{ -+ if (Imm16::IsInSignedRange(address.offset)) { -+ as_lfd(ft, address.base, address.offset); -+ } else { -+ MOZ_ASSERT(address.base != ScratchRegister); -+ ma_li(ScratchRegister, Imm32(address.offset)); -+ as_lfdx(ft, address.base, ScratchRegister); -+ } -+} -+ -+void -+MacroAssemblerPPC64::ma_sd(FloatRegister ft, Address address) -+{ -+ if (Imm16::IsInSignedRange(address.offset)) { -+ as_stfd(ft, address.base, address.offset); -+ } else { -+ MOZ_ASSERT(address.base != ScratchRegister); -+ ma_li(ScratchRegister, Imm32(address.offset)); -+ as_stfdx(ft, address.base, ScratchRegister); -+ } -+} -+ -+void -+MacroAssemblerPPC64::ma_ss(FloatRegister ft, Address address) -+{ -+ if (Imm16::IsInSignedRange(address.offset)) { -+ as_stfs(ft, address.base, address.offset); -+ } else { -+ MOZ_ASSERT(address.base != ScratchRegister); -+ ma_li(ScratchRegister, Imm32(address.offset)); -+ as_stfsx(ft, address.base, ScratchRegister); -+ } -+} -+ -+// 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) -+{ -+ uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), FrameType::IonJS, -+ ExitFrameLayout::Size()); -+ -+ asMasm().Push(Imm32(descriptor)); // descriptor_ -+ asMasm().Push(ImmPtr(fakeReturnAddr)); -+ -+ 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)); -+} -+ -+void -+MacroAssemblerPPC64Compat::load8ZeroExtend(const Address& address, Register dest) -+{ -+ ma_load(dest, address, SizeByte, ZeroExtend); -+} -+ -+void -+MacroAssemblerPPC64Compat::load8ZeroExtend(const BaseIndex& src, Register dest) -+{ -+ ma_load(dest, src, SizeByte, ZeroExtend); -+} -+ -+void -+MacroAssemblerPPC64Compat::load8SignExtend(const Address& address, Register dest) -+{ -+ ma_load(dest, address, SizeByte, SignExtend); -+} -+ -+void -+MacroAssemblerPPC64Compat::load8SignExtend(const BaseIndex& src, Register dest) -+{ -+ ma_load(dest, src, SizeByte, SignExtend); -+} -+ -+void -+MacroAssemblerPPC64Compat::load16ZeroExtend(const Address& address, Register dest) -+{ -+ ma_load(dest, address, SizeHalfWord, ZeroExtend); -+} -+ -+void -+MacroAssemblerPPC64Compat::load16ZeroExtend(const BaseIndex& src, Register dest) -+{ -+ ma_load(dest, src, SizeHalfWord, ZeroExtend); -+} -+ -+void -+MacroAssemblerPPC64Compat::load16SignExtend(const Address& address, Register dest) -+{ -+ ma_load(dest, address, SizeHalfWord, SignExtend); -+} -+ -+void -+MacroAssemblerPPC64Compat::load16SignExtend(const BaseIndex& src, Register dest) -+{ -+ ma_load(dest, src, SizeHalfWord, SignExtend); -+} -+ -+void -+MacroAssemblerPPC64Compat::load32(const Address& address, Register dest) -+{ -+ // This must sign-extend for arithmetic to function correctly. -+ ma_load(dest, address, SizeWord); -+} -+ -+void -+MacroAssemblerPPC64Compat::load32(const BaseIndex& address, Register dest) -+{ -+ // This must sign-extend for arithmetic to function correctly. -+ ma_load(dest, address, SizeWord); -+} -+ -+// Zero-extend versions, mostly for wasm. -+void -+MacroAssemblerPPC64Compat::load32ZeroExtend(const Address& address, Register dest) -+{ -+ // This must sign-extend for arithmetic to function correctly. -+ ma_load(dest, address, SizeWord, ZeroExtend); -+} -+ -+void -+MacroAssemblerPPC64Compat::load32ZeroExtend(const BaseIndex& address, Register dest) -+{ -+ // This must sign-extend for arithmetic to function correctly. -+ ma_load(dest, address, SizeWord, ZeroExtend); -+} -+ -+void -+MacroAssemblerPPC64Compat::load32(AbsoluteAddress address, Register dest) -+{ -+ movePtr(ImmPtr(address.addr), SecondScratchReg); -+ load32(Address(SecondScratchReg, 0), dest); -+} -+ -+void -+MacroAssemblerPPC64Compat::load32(wasm::SymbolicAddress address, Register dest) -+{ -+ movePtr(address, SecondScratchReg); -+ load32(Address(SecondScratchReg, 0), dest); -+} -+ -+void -+MacroAssemblerPPC64Compat::loadPtr(Register src, Register dest) -+{ -+ if (src != dest) -+ xs_mr(dest, src); -+} -+ -+void -+MacroAssemblerPPC64Compat::loadPtr(const Address& address, Register dest) -+{ -+ ma_load(dest, address, SizeDouble); -+} -+ -+void -+MacroAssemblerPPC64Compat::loadPtr(const BaseIndex& src, Register dest) -+{ -+ ma_load(dest, src, SizeDouble); -+} -+ -+void -+MacroAssemblerPPC64Compat::loadPtr(AbsoluteAddress address, Register dest) -+{ -+ movePtr(ImmPtr(address.addr), SecondScratchReg); -+ loadPtr(Address(SecondScratchReg, 0), dest); -+} -+ -+void -+MacroAssemblerPPC64Compat::loadPtr(wasm::SymbolicAddress address, Register dest) -+{ -+ movePtr(address, SecondScratchReg); -+ 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 -+} -+ -+void -+MacroAssemblerPPC64Compat::store8(Imm32 imm, const Address& address) -+{ -+ MOZ_ASSERT(address.base != ScratchRegister); -+ ma_li(ScratchRegister, imm); -+ ma_store(ScratchRegister, address, SizeByte); -+} -+ -+void -+MacroAssemblerPPC64Compat::store8(Register src, const Address& address) -+{ -+ ma_store(src, address, SizeByte); -+} -+ -+void -+MacroAssemblerPPC64Compat::store8(Imm32 imm, const BaseIndex& dest) -+{ -+ ma_store(imm, dest, SizeByte); -+} -+ -+void -+MacroAssemblerPPC64Compat::store8(Register src, const BaseIndex& dest) -+{ -+ ma_store(src, dest, SizeByte); -+} -+ -+void -+MacroAssemblerPPC64Compat::store16(Imm32 imm, const Address& address) -+{ -+ MOZ_ASSERT(address.base != ScratchRegister); -+ ma_li(ScratchRegister, imm); -+ ma_store(ScratchRegister, address, SizeHalfWord); -+} -+ -+void -+MacroAssemblerPPC64Compat::store16(Register src, const Address& address) -+{ -+ ma_store(src, address, SizeHalfWord); -+} -+ -+void -+MacroAssemblerPPC64Compat::store16(Imm32 imm, const BaseIndex& dest) -+{ -+ ma_store(imm, dest, SizeHalfWord); -+} -+ -+void -+MacroAssemblerPPC64Compat::store16(Register src, const BaseIndex& address) -+{ -+ ma_store(src, address, SizeHalfWord); -+} -+ -+void -+MacroAssemblerPPC64Compat::store32(Register src, AbsoluteAddress address) -+{ -+ MOZ_ASSERT(src != SecondScratchReg); -+ movePtr(ImmPtr(address.addr), SecondScratchReg); -+ store32(src, Address(SecondScratchReg, 0)); -+} -+ -+void -+MacroAssemblerPPC64Compat::store32(Register src, const Address& address) -+{ -+ ma_store(src, address, SizeWord); -+} -+ -+void -+MacroAssemblerPPC64Compat::store32(Imm32 src, const Address& address) -+{ -+ MOZ_ASSERT(address.base != ScratchRegister); -+ move32(src, ScratchRegister); -+ ma_store(ScratchRegister, address, SizeWord); -+} -+ -+void -+MacroAssemblerPPC64Compat::store32(Imm32 imm, const BaseIndex& dest) -+{ -+ ma_store(imm, dest, SizeWord); -+} -+ -+void -+MacroAssemblerPPC64Compat::store32(Register src, const BaseIndex& dest) -+{ -+ ma_store(src, dest, SizeWord); -+} -+ -+template -+void -+MacroAssemblerPPC64Compat::storePtr(ImmWord imm, T address) -+{ -+ ma_li(ScratchRegister, imm); -+ ma_store(ScratchRegister, address, SizeDouble); -+} -+ -+template void MacroAssemblerPPC64Compat::storePtr
(ImmWord imm, Address address); -+template void MacroAssemblerPPC64Compat::storePtr(ImmWord imm, BaseIndex address); -+ -+template -+void -+MacroAssemblerPPC64Compat::storePtr(ImmPtr imm, T address) -+{ -+ storePtr(ImmWord(uintptr_t(imm.value)), address); -+} -+ -+template void MacroAssemblerPPC64Compat::storePtr
(ImmPtr imm, Address address); -+template void MacroAssemblerPPC64Compat::storePtr(ImmPtr imm, BaseIndex address); -+ -+template -+void -+MacroAssemblerPPC64Compat::storePtr(ImmGCPtr imm, T address) -+{ -+ movePtr(imm, ScratchRegister); -+ storePtr(ScratchRegister, address); -+} -+ -+template void MacroAssemblerPPC64Compat::storePtr
(ImmGCPtr imm, Address address); -+template void MacroAssemblerPPC64Compat::storePtr(ImmGCPtr imm, BaseIndex address); -+ -+void -+MacroAssemblerPPC64Compat::storePtr(Register src, const Address& address) -+{ -+ ma_store(src, address, SizeDouble); -+} -+ -+void -+MacroAssemblerPPC64Compat::storePtr(Register src, const BaseIndex& address) -+{ -+ ma_store(src, address, SizeDouble); -+} -+ -+void -+MacroAssemblerPPC64Compat::storePtr(Register src, AbsoluteAddress dest) -+{ -+ MOZ_ASSERT(src != SecondScratchReg); -+ movePtr(ImmPtr(dest.addr), SecondScratchReg); -+ 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) -+{ -+ // 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 -+ -+ using Fn = void (*)(ResumeFromException * rfe); -+ // Call the handler. -+ asMasm().setupUnalignedABICall(r4); -+ asMasm().passABIArg(r3); -+ asMasm().callWithABI(MoveOp::GENERAL, -+ CheckUnsafeCallWithABI::DontCheckHasExitFrame); -+ -+ Label entryFrame; -+ Label catch_; -+ Label finally; -+ Label return_; -+ Label bailout; -+ Label wasm; -+ -+ // Already clobbered r3, so use it... -+ load32(Address(StackPointer, offsetof(ResumeFromException, kind)), r3); -+ asMasm().branch32(Assembler::Equal, r3, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), -+ &entryFrame); -+ asMasm().branch32(Assembler::Equal, r3, Imm32(ResumeFromException::RESUME_CATCH), &catch_); -+ asMasm().branch32(Assembler::Equal, r3, Imm32(ResumeFromException::RESUME_FINALLY), &finally); -+ asMasm().branch32(Assembler::Equal, r3, Imm32(ResumeFromException::RESUME_FORCED_RETURN), -+ &return_); -+ asMasm().branch32(Assembler::Equal, r3, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout); -+ asMasm().branch32(Assembler::Equal, r3, Imm32(ResumeFromException::RESUME_WASM), &wasm); -+ -+ xs_trap(); // Invalid kind. -+ -+ // No exception handler. Load the error value, load the new stack pointer -+ // and return from the entry frame. -+ bind(&entryFrame); -+ asMasm().moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); -+ loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)), 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_); -+ loadPtr(Address(StackPointer, offsetof(ResumeFromException, target)), r3); -+ loadPtr(Address(StackPointer, offsetof(ResumeFromException, framePointer)), BaselineFrameReg); -+ loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)), StackPointer); -+ jump(r3); -+ -+ // If we found a finally block, this must be a baseline frame. Push -+ // two values expected by JSOP_RETSUB: BooleanValue(true) and the -+ // exception. -+ bind(&finally); -+ ValueOperand exception = ValueOperand(r4); -+ loadValue(Address(sp, offsetof(ResumeFromException, exception)), exception); -+ -+ loadPtr(Address(sp, offsetof(ResumeFromException, target)), r3); -+ loadPtr(Address(sp, offsetof(ResumeFromException, framePointer)), BaselineFrameReg); -+ loadPtr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp); -+ -+ pushValue(BooleanValue(true)); -+ pushValue(exception); -+ jump(r3); -+ -+ // Only used in debug mode. Return BaselineFrame->returnValue() to the -+ // caller. -+ bind(&return_); -+ loadPtr(Address(StackPointer, offsetof(ResumeFromException, framePointer)), BaselineFrameReg); -+ loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)), StackPointer); -+ loadValue(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfReturnValue()), -+ JSReturnOperand); -+ ma_move(StackPointer, BaselineFrameReg); -+ pop(BaselineFrameReg); -+ -+ // If profiling is enabled, then update the lastProfilingFrame to refer to caller -+ // frame before returning. -+ { -+ Label skipProfilingInstrumentation; -+ // Test if profiler enabled. -+ AbsoluteAddress addressOfEnabled(GetJitContext()->runtime->geckoProfiler().addressOfEnabled()); -+ asMasm().branch32(Assembler::Equal, addressOfEnabled, Imm32(0), -+ &skipProfilingInstrumentation); -+ jump(profilerExitTail); -+ bind(&skipProfilingInstrumentation); -+ } -+ -+ ret(); -+ -+ // If we are bailing out to baseline to handle an exception, jump to -+ // the bailout tail stub. -+ bind(&bailout); -+ loadPtr(Address(sp, offsetof(ResumeFromException, bailoutInfo)), r5); -+ ma_li(ReturnReg, Imm32(1)); -+ loadPtr(Address(sp, offsetof(ResumeFromException, target)), r4); -+ jump(r4); -+ -+ // 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, offsetof(ResumeFromException, framePointer)), FramePointer); -+ loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)), StackPointer); -+ ret(); -+} -+ -+// 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, MoveOp::Type 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, MoveOp::Type result) -+{ -+ ADBlock(); -+ -+ uint32_t stackAdjust; -+ callWithABIPre(&stackAdjust); -+ call(fun); -+ callWithABIPost(stackAdjust, result); -+} -+ -+void -+MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type 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); -+ -+ if (temp != InvalidReg) { -+ unboxGCThingForGCBarrier(value, temp); -+ orPtr(Imm32(gc::ChunkMask), temp); -+ loadPtr(Address(temp, gc::ChunkStoreBufferOffsetFromLastByte), temp); -+ branchPtr(InvertCondition(cond), temp, ImmWord(0), label); -+ } else { -+ // Honey, Ion stole the temp register again. Get out the baseball -+ // bat, would you? -+ // -+ // Both constants are too large to be immediates. -+ unboxGCThingForGCBarrier(value, ScratchRegister); -+ ma_li(SecondScratchReg, gc::ChunkMask); -+ as_or(SecondScratchReg, ScratchRegister, SecondScratchReg); -+ ma_li(ScratchRegister, gc::ChunkStoreBufferOffsetFromLastByte); -+ as_add(SecondScratchReg, SecondScratchReg, ScratchRegister); -+ as_ld(ScratchRegister, SecondScratchReg, 0); -+ as_cmpdi(ScratchRegister, 0); -+ ma_bc(InvertCondition(cond), 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 -+void -+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, -+ const T& dest, MIRType slotType) -+{ -+ if (valueType == MIRType::Double) { -+ storeDouble(value.reg().typedReg().fpu(), dest); -+ return; -+ } -+ -+ // For known integers and booleans, we can just store the unboxed value if -+ // the slot has the same type. -+ if ((valueType == MIRType::Int32 || valueType == MIRType::Boolean) && slotType == valueType) { -+ if (value.constant()) { -+ Value val = value.value(); -+ if (valueType == MIRType::Int32) -+ store32(Imm32(val.toInt32()), dest); -+ else -+ store32(Imm32(val.toBoolean() ? 1 : 0), dest); -+ } else { -+ store32(value.reg().typedReg().gpr(), 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, MIRType slotType); -+template void -+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, -+ const BaseIndex& dest, MIRType slotType); -+template void -+MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, -+ const BaseObjectElementIndex& dest, MIRType slotType); -+ -+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) -+{ -+ uint32_t offset = access.offset(); -+ MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit()); -+ 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"); -+ } -+ -+ BaseIndex address(memoryBase, ptr, TimesOne); -+/* -+ if (IsUnaligned(access)) { -+ asMasm().ma_load(output.reg, address, static_cast(8 * byteSize), isSigned ? SignExtend : ZeroExtend); -+ return; -+ } -+*/ -+ -+ // threadsafe -+ asMasm().memoryBarrierBefore(access.sync()); -+ uint32_t loadSize = asMasm().ma_load(output.reg, address, static_cast(8 * byteSize), -+ isSigned ? SignExtend : ZeroExtend); -+ if (loadSize & 0x01) { -+ // Split load emitted. -+ asMasm().append(access, loadSize - 1); -+ // The second load immediately follows the first load. -+ asMasm().append(access, loadSize + 3); -+ } else { -+ asMasm().append(access, loadSize); -+ } -+ asMasm().memoryBarrierAfter(access.sync()); -+} -+ -+void -+MacroAssemblerPPC64Compat::wasmStoreI64Impl(const wasm::MemoryAccessDesc& access, Register64 value, -+ Register memoryBase, Register ptr, Register ptrScratch, -+ Register tmp) -+{ -+ uint32_t offset = access.offset(); -+ MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit()); -+ 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"); -+ } -+ -+ BaseIndex address(memoryBase, ptr, TimesOne); -+/* -+ if (IsUnaligned(access)) { -+ asMasm().ma_store(value.reg, address, static_cast(8 * byteSize), isSigned ? SignExtend : ZeroExtend); -+ return; -+ } -+*/ -+ -+ // threadsafe -+ asMasm().memoryBarrierBefore(access.sync()); -+ uint32_t loadSize = asMasm().ma_store(value.reg, address, static_cast(8 * byteSize), -+ isSigned ? SignExtend : ZeroExtend); -+ 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().memoryBarrierAfter(access.sync()); -+} -+ -+template -+static void -+CompareExchange64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, const 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, masm.size()); -+ // '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, masm.size()); -+ 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(const Synchronization& sync, const Address& mem, -+ Register64 expect, Register64 replace, Register64 output) -+{ -+ CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); -+} -+ -+void MacroAssembler::compareExchange64(const Synchronization& sync, -+ const BaseIndex& mem, Register64 expect, -+ Register64 replace, Register64 output) { -+ CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); -+} -+ -+template -+static void -+AtomicExchange64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, const 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, masm.size()); -+ // 'r0' for 'ra' indicates hard 0, not GPR r0 -+ masm.as_ldarx(output.reg, r0, SecondScratchReg); -+ if (access) masm.append(*access, masm.size()); -+ masm.as_stdcx(src.reg, r0, SecondScratchReg); -+ masm.ma_bc(cr0, Assembler::NotEqual, &tryAgain, ShortJump); -+ -+ //masm.as_isync(); -+ masm.memoryBarrierAfter(sync); -+} -+ -+void -+MacroAssembler::atomicExchange64(const Synchronization& sync, const Address& mem, Register64 src, -+ Register64 output) -+{ -+ AtomicExchange64(*this, nullptr, sync, mem, src, output); -+} -+ -+void -+MacroAssembler::atomicExchange64(const Synchronization& sync, const BaseIndex& mem, Register64 src, -+ Register64 output) -+{ -+ AtomicExchange64(*this, nullptr, sync, mem, src, output); -+} -+ -+template -+static void -+AtomicFetchOp64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, const 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, masm.size()); -+ // 'r0' for 'ra' indicates hard 0, not GPR r0 -+ masm.as_ldarx(output.reg, r0, SecondScratchReg); -+ -+ switch(op) { -+ case AtomicFetchAddOp: -+ masm.as_add(temp.reg, output.reg, value.reg); -+ break; -+ case AtomicFetchSubOp: -+ masm.as_subf(temp.reg, value.reg, output.reg); -+ break; -+ case AtomicFetchAndOp: -+ masm.as_and(temp.reg, output.reg, value.reg); -+ break; -+ case AtomicFetchOrOp: -+ masm.as_or(temp.reg, output.reg, value.reg); -+ break; -+ case AtomicFetchXorOp: -+ 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(const 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(const 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(const Synchronization& sync, AtomicOp op, -+ Register64 value, const Address& mem, -+ Register64 temp) { -+ AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp); -+} -+ -+void MacroAssembler::atomicEffectOp64(const 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 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 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 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 pansy 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::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| does not -+ // 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. -+ -+uint32_t -+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); -+ } -+} -+ -+// 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 -+} -+ -+uint32_t -+MacroAssemblerPPC64::ma_store(Register data, const BaseIndex& dest, -+ LoadStoreSize size, LoadStoreExtension extension) -+{ -+ // 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, extension); -+ } else { -+ return asMasm().ma_store(data, Address(SecondScratchReg, dest.offset), size, extension); -+ } -+} -+ -+uint32_t -+MacroAssemblerPPC64::ma_store(Imm32 imm, const BaseIndex& dest, -+ LoadStoreSize size, LoadStoreExtension extension) -+{ -+ // Make sure that SecondScratchReg contains absolute address so that -+ // offset is 0. -+ asMasm().computeEffectiveAddress(dest, SecondScratchReg); -+ -+ // Scrach register is free now, use it for loading imm value -+ ma_li(ScratchRegister, imm); -+ -+ // with offset=0 ScratchRegister will not be used in ma_store() -+ // so we can use it as a parameter here -+ return asMasm().ma_store(ScratchRegister, Address(SecondScratchReg, 0), size, extension); -+} -+ -+// 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 -+} -+ -+// 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(value)); -+ -+ ma_li(ScratchRegister, imm); -+ asMasm().moveToFloat32(ScratchRegister, dest); -+} -+ -+void -+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); -+ asMasm().ma_sd(ft, Address(SecondScratchReg, address.offset)); -+} -+ -+void -+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); -+ asMasm().ma_ss(ft, Address(SecondScratchReg, address.offset)); -+} -+ -+void -+MacroAssemblerPPC64::ma_ld(FloatRegister ft, const BaseIndex& src) -+{ -+ asMasm().computeScaledAddress(src, SecondScratchReg); -+ asMasm().ma_ld(ft, Address(SecondScratchReg, src.offset)); -+} -+ -+void -+MacroAssemblerPPC64::ma_ls(FloatRegister ft, const BaseIndex& src) -+{ -+ asMasm().computeScaledAddress(src, SecondScratchReg); -+ 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); -+} -+ -+void -+MacroAssemblerPPC64::loadDouble(const Address& address, FloatRegister dest) -+{ -+ ma_ld(dest, address); -+} -+ -+void -+MacroAssemblerPPC64::loadDouble(const BaseIndex& src, FloatRegister dest) -+{ -+ 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); -+} -+ -+void -+MacroAssemblerPPC64::loadFloat32(const Address& address, FloatRegister dest) -+{ -+ ma_ls(dest, address); -+} -+ -+void -+MacroAssemblerPPC64::loadFloat32(const BaseIndex& src, FloatRegister dest) -+{ -+ 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(this); -+} -+ -+const MacroAssembler& -+MacroAssemblerPPC64::asMasm() const -+{ -+ return *static_cast(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(sizeof(double)); // Keep stack aligned to double, even if it's a float. -+} -+ -+void -+MacroAssembler::Pop(Register reg) -+{ -+ ma_pop(reg); -+ adjustFrame(-int32_t(sizeof(intptr_t))); -+} -+ -+void -+MacroAssembler::Pop(FloatRegister f) -+{ -+ ma_pop(f); -+ adjustFrame(-sizeof(double)); -+} -+ -+void -+MacroAssembler::Pop(const ValueOperand& val) -+{ -+ popValue(val); -+ adjustFrame(-int32_t(sizeof(Value))); -+} -+ -+void -+MacroAssembler::PopStackPtr() -+{ -+ loadPtr(Address(StackPointer, 0), StackPointer); -+ adjustFrame(-int32_t(sizeof(intptr_t))); -+} -+ -+ -+// =============================================================== -+// 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) -+{ -+ movePtr(target, CallReg); -+ return call(CallReg); -+} -+ -+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) -+{ -+ if (ptr != buffer) -+ movePtr(ptr, buffer); -+ orPtr(Imm32(gc::ChunkMask), buffer); -+ loadPtr(Address(buffer, gc::ChunkStoreBufferOffsetFromLastByte), 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 != SecondScratchReg); -+ -+ if (temp != InvalidReg) { -+ movePtr(ptr, temp); -+ orPtr(Imm32(gc::ChunkMask), temp); -+ branchPtr(InvertCondition(cond), -+ Address(temp, gc::ChunkStoreBufferOffsetFromLastByte), -+ ImmWord(0), label); -+ } else { -+ // Why, those cheapskates. We have to provide our own temp too? -+ // Did the bean counters cut our temp register budget this year? -+ // (Ion hits this.) -+ MOZ_ASSERT(ptr != ScratchRegister); -+ -+ // Both offsets are too big to be immediate displacements. -+ ma_li(ScratchRegister, gc::ChunkMask); -+ as_or(SecondScratchReg, ptr, ScratchRegister); -+ ma_li(ScratchRegister, gc::ChunkStoreBufferOffsetFromLastByte); -+ as_add(SecondScratchReg, SecondScratchReg, ScratchRegister); -+ as_ld(ScratchRegister, SecondScratchReg, 0); -+ as_cmpdi(ScratchRegister, 0); -+ ma_bc(InvertCondition(cond), label); -+ } -+} -+ -+void -+MacroAssembler::comment(const char* msg) -+{ -+ Assembler::comment(msg); -+} -+ -+// =============================================================== -+// WebAssembly -+ -+CodeOffset -+MacroAssembler::wasmTrapInstruction() -+{ -+ CodeOffset 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(); -+ uint32_t offset = access.offset(); -+ uint32_t loadInst = 0; -+ MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit()); -+ 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(8 * byteSize), isSigned ? SignExtend : ZeroExtend); -+ } -+ return; -+ } -+*/ -+ -+ asMasm().memoryBarrierBefore(access.sync()); -+ if (isFloat) { -+ // The load will always be at the end, so we tag that access. -+ if (byteSize == 4) -+ asMasm().ma_ls(output.fpu(), address); -+ else -+ asMasm().ma_ld(output.fpu(), address); -+ asMasm().append(access, asMasm().size() - 4); -+ } 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); -+ loadInst = asMasm().ma_load(output.gpr(), address, static_cast(8 * byteSize), -+ isSigned ? SignExtend : ZeroExtend); -+ asMasm().append(access, loadInst); -+ } -+ asMasm().memoryBarrierAfter(access.sync()); -+} -+ -+void -+MacroAssemblerPPC64::wasmStoreImpl(const wasm::MemoryAccessDesc& access, AnyRegister value, -+ Register memoryBase, Register ptr, Register ptrScratch, -+ Register tmp) -+{ -+ ADBlock(); -+ uint32_t offset = access.offset(); -+ MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit()); -+ 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(8 * byteSize), -+ isSigned ? SignExtend : ZeroExtend); -+ } -+ return; -+ } -+*/ -+ -+ asMasm().memoryBarrierBefore(access.sync()); -+ if (isFloat) { -+ // The store instruction will always be last. -+ if (byteSize == 4) -+ asMasm().ma_ss(value.fpu(), address); -+ else -+ asMasm().ma_sd(value.fpu(), address); -+ asMasm().append(access, asMasm().size() - 4); -+ } else { -+ uint32_t loadSize = asMasm().ma_store(value.gpr(), address, -+ static_cast(8 * byteSize), -+ isSigned ? SignExtend : ZeroExtend); -+ 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().memoryBarrierAfter(access.sync()); -+} -+ -+void -+MacroAssembler::enterFakeExitFrameForWasm(Register cxreg, Register scratch, ExitFrameType type) -+{ -+ enterFakeExitFrame(cxreg, scratch, type); -+} -+ -+// ======================================================================== -+// Primitive atomic operations. -+ -+template -+static void -+CompareExchange(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, -+Scalar::Type type, const 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, masm.size()); -+ 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, masm.size()); -+ 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, const 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, const 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 -+static void -+AtomicExchange(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, -+Scalar::Type type, const 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, masm.size()); -+ 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, masm.size()); -+ 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, const 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, const 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 -+static void -+AtomicFetchOp(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, -+Scalar::Type type, const 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, masm.size()); -+ masm.as_lwarx(output, r0, SecondScratchReg); -+ -+ switch (op) { -+ case AtomicFetchAddOp: -+ masm.as_add(ScratchRegister, output, value); -+ break; -+ case AtomicFetchSubOp: -+ masm.as_subf(ScratchRegister, value, output); -+ break; -+ case AtomicFetchAndOp: -+ masm.as_and(ScratchRegister, output, value); -+ break; -+ case AtomicFetchOrOp: -+ masm.as_or(ScratchRegister, output, value); -+ break; -+ case AtomicFetchXorOp: -+ 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, masm.size()); -+ masm.as_lwarx(ScratchRegister, r0, SecondScratchReg); -+ masm.as_srd(output, ScratchRegister, offsetTemp); -+ -+ switch (op) { -+ case AtomicFetchAddOp: -+ masm.as_add(valueTemp, output, value); -+ break; -+ case AtomicFetchSubOp: -+ masm.as_subf(valueTemp, value, output); -+ break; -+ case AtomicFetchAndOp: -+ masm.as_and(valueTemp, output, value); -+ break; -+ case AtomicFetchOrOp: -+ masm.as_or(valueTemp, output, value); -+ break; -+ case AtomicFetchXorOp: -+ 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, const 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, const 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 -+static void -+AtomicEffectOp(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, Scalar::Type type, const 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, masm.size()); -+ masm.as_lwarx(ScratchRegister, r0, SecondScratchReg); -+ -+ switch (op) { -+ case AtomicFetchAddOp: -+ masm.as_add(ScratchRegister, ScratchRegister, value); -+ break; -+ case AtomicFetchSubOp: -+ masm.as_subf(ScratchRegister, value, ScratchRegister); -+ break; -+ case AtomicFetchAndOp: -+ masm.as_and(ScratchRegister, ScratchRegister, value); -+ break; -+ case AtomicFetchOrOp: -+ masm.as_or(ScratchRegister, ScratchRegister, value); -+ break; -+ case AtomicFetchXorOp: -+ 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, masm.size()); -+ masm.as_lwarx(ScratchRegister, r0, SecondScratchReg); -+ masm.as_srd(valueTemp, ScratchRegister, offsetTemp); -+ -+ switch (op) { -+ case AtomicFetchAddOp: -+ masm.as_add(valueTemp, valueTemp, value); -+ break; -+ case AtomicFetchSubOp: -+ masm.as_subf(valueTemp, value, valueTemp); -+ break; -+ case AtomicFetchAndOp: -+ masm.as_and(valueTemp, valueTemp, value); -+ break; -+ case AtomicFetchOrOp: -+ masm.as_or(valueTemp, valueTemp, value); -+ break; -+ case AtomicFetchXorOp: -+ 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, const 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, const 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 -+static void -+CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, const 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, const 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, const 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 -+static void -+AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, const 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, const 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, const 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 -+static void -+AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, const 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, const 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, const 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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -0700 -@@ -0,0 +1,1151 @@ -+/* -*- 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 -+ uint32_t ma_load(Register dest, const BaseIndex& src, LoadStoreSize size = SizeWord, -+ LoadStoreExtension extension = SignExtend); -+ void ma_load_unaligned(const wasm::MemoryAccessDesc& access, Register dest, const BaseIndex& src, Register temp, -+ LoadStoreSize size, LoadStoreExtension extension); -+ -+ // store -+ uint32_t ma_store(Register data, const BaseIndex& dest, LoadStoreSize size = SizeWord, -+ LoadStoreExtension extension = SignExtend); -+ uint32_t ma_store(Imm32 imm, const BaseIndex& dest, LoadStoreSize size = SizeWord, -+ LoadStoreExtension extension = SignExtend); -+ void ma_store_unaligned(Register data, const Address& dest, -+ LoadStoreSize size = SizeWord); -+ void ma_store_unaligned(Register data, const BaseIndex& dest, -+ LoadStoreSize size = SizeWord); -+ void ma_store_unaligned(const wasm::MemoryAccessDesc& access, Register data, const BaseIndex& dest, Register temp, -+ LoadStoreSize size, LoadStoreExtension extension); -+ -+ // 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 -+ 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); -+ -+ void ma_sd(FloatRegister src, BaseIndex address); -+ void ma_ss(FloatRegister src, BaseIndex address); -+ -+ void ma_ld(FloatRegister dest, const BaseIndex& src); -+ void 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{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); -+ -+ void loadDouble(const Address& addr, FloatRegister dest); -+ void 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); -+ -+ void loadFloat32(const Address& addr, FloatRegister dest); -+ void 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 -+ uint32_t ma_load(Register dest, Address address, LoadStoreSize size = SizeWord, -+ LoadStoreExtension extension = SignExtend); -+ -+ // store -+ uint32_t ma_store(Register data, Address address, LoadStoreSize size = SizeWord, -+ LoadStoreExtension extension = SignExtend); -+ -+ // 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_ls(FloatRegister ft, Address address); -+ void ma_ld(FloatRegister ft, Address address); -+ void ma_sd(FloatRegister ft, Address address); -+ void ma_ss(FloatRegister ft, Address address); -+ -+ 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 -+ 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 -+ 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 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 -+ 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 handleFailureWithHandlerTail(Label* profilerExitTail); -+ -+ ///////////////////////////////////////////////////////////////// -+ // 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); -+ -+ void load8SignExtend(const Address& address, Register dest); -+ void load8SignExtend(const BaseIndex& src, Register dest); -+ -+ void load8ZeroExtend(const Address& address, Register dest); -+ void load8ZeroExtend(const BaseIndex& src, Register dest); -+ -+ void load16SignExtend(const Address& address, Register dest); -+ void load16SignExtend(const BaseIndex& src, Register dest); -+ -+ template -+ void load16UnalignedSignExtend(const S& src, Register dest) { -+ ma_load(dest, src, SizeHalfWord, SignExtend); -+ } -+ -+ void load16ZeroExtend(const Address& address, Register dest); -+ void load16ZeroExtend(const BaseIndex& src, Register dest); -+ -+ template -+ void load16UnalignedZeroExtend(const S& src, Register dest) { -+ ma_load(dest, src, SizeHalfWord, ZeroExtend); -+ } -+ -+ void load32(const Address& address, Register dest); -+ void load32(const BaseIndex& address, Register dest); -+ void load32ZeroExtend(const Address& address, Register dest); -+ void load32ZeroExtend(const BaseIndex& address, Register dest); -+ void load32(AbsoluteAddress address, Register dest); -+ void load32(wasm::SymbolicAddress address, Register dest); -+ -+ void load64(const Address& address, Register64 dest) { -+ loadPtr(address, dest.reg); -+ } -+ -+ template -+ void load32Unaligned(const S& src, Register dest) { -+ ma_load(dest, src, SizeWord, SignExtend); -+ } -+ -+ void load64(const BaseIndex& address, Register64 dest) { -+ loadPtr(address, dest.reg); -+ } -+ -+ template -+ void load64Unaligned(const S& src, Register64 dest) { -+ ma_load(dest.reg, src, SizeDouble, ZeroExtend); -+ } -+ -+ void loadPtr(Register src, Register dest); -+ void loadPtr(const Address& address, Register dest); -+ void loadPtr(const BaseIndex& src, Register dest); -+ void loadPtr(AbsoluteAddress address, Register dest); -+ void 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); -+ -+ void store8(Register src, const Address& address); -+ void store8(Imm32 imm, const Address& address); -+ void store8(Register src, const BaseIndex& address); -+ void store8(Imm32 imm, const BaseIndex& address); -+ -+ void store16(Register src, const Address& address); -+ void store16(Imm32 imm, const Address& address); -+ void store16(Register src, const BaseIndex& address); -+ void store16(Imm32 imm, const BaseIndex& address); -+ -+ template -+ void store16Unaligned(Register src, const T& dest) { -+ //ma_store_unaligned(src, dest, SizeHalfWord); -+ store16(src, dest); -+ } -+ -+ void store32(Register src, AbsoluteAddress address); -+ void store32(Register src, const Address& address); -+ void store32(Register src, const BaseIndex& address); -+ void store32(Imm32 src, const Address& address); -+ void store32(Imm32 src, const BaseIndex& address); -+ -+ // NOTE: This will use second scratch on PPC64. Only ARM needs the -+ // implementation without second scratch. -+ void store32_NoSecondScratch(Imm32 src, const Address& address) { -+ store32(src, address); -+ } -+ -+ template -+ void store32Unaligned(Register src, const T& dest) { -+ //ma_store_unaligned(src, dest, SizeWord); -+ store32(src, dest); -+ } -+ -+ void store64(Imm64 imm, Address address) { -+ storePtr(ImmWord(imm.value), address); -+ } -+ -+ void store64(Register64 src, Address address) { -+ storePtr(src.reg, address); -+ } -+ -+ void store64(Register64 src, const BaseIndex& address) { -+ storePtr(src.reg, address); -+ } -+ -+ template -+ void store64Unaligned(Register64 src, const T& dest) { -+ //ma_store_unaligned(src.reg, dest, SizeDouble); -+ store64(src, dest); -+ } -+ -+ template void storePtr(ImmWord imm, T address); -+ template void storePtr(ImmPtr imm, T address); -+ template void storePtr(ImmGCPtr imm, T address); -+ void storePtr(Register src, const Address& address); -+ void storePtr(Register src, const BaseIndex& address); -+ void 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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -0700 -@@ -0,0 +1,128 @@ -+/* -*- 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) -+{ -+ Register scratch = R2.scratchReg(); -+ -+ // Compute frame size. -+ masm.movePtr(BaselineFrameReg, scratch); -+ masm.addPtr(Imm32(BaselineFrame::FramePointerOffset), scratch); -+ masm.subPtr(BaselineStackReg, scratch); -+ -+#ifdef DEBUG -+ // Store frame size without VMFunction arguments for debug assertions. -+ masm.subPtr(Imm32(argSize), scratch); -+ Address frameSizeAddr(BaselineFrameReg, -+ BaselineFrame::reverseOffsetOfDebugFrameSize()); -+ masm.store32(scratch, frameSizeAddr); -+ masm.addPtr(Imm32(argSize), scratch); -+#endif -+ -+ // Push frame descriptor and perform the tail call. -+ // ICTailCallReg (LR) already contains the return address (as we -+ // keep it there through the stub calls), but the VMWrapper code being -+ // called expects the return address to also be pushed on the stack. -+ masm.makeFrameDescriptor(scratch, FrameType::BaselineJS, ExitFrameLayout::Size()); -+ masm.subPtr(Imm32(sizeof(CommonFrameLayout)), StackPointer); -+ // Keep the tail call register current (i.e., don't just use r0). -+ masm.xs_mflr(ICTailCallReg); -+ masm.storePtr(scratch, Address(StackPointer, CommonFrameLayout::offsetOfDescriptor())); -+ masm.storePtr(ICTailCallReg, Address(StackPointer, CommonFrameLayout::offsetOfReturnAddress())); -+ -+ masm.jump(target); -+} -+ -+/* -+inline void -+EmitIonTailCallVM(TrampolinePtr target, MacroAssembler& masm, uint32_t stackSize) -+{ -+ Register scratch = R2.scratchReg(); -+ -+ masm.loadPtr(Address(sp, stackSize), scratch); -+ masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch); -+ masm.addPtr(Imm32(stackSize + JitStubFrameLayout::Size() - sizeof(intptr_t)), scratch); -+ -+ // Push frame descriptor and return address, perform the tail call. -+ masm.makeFrameDescriptor(scratch, FrameType::IonJS, ExitFrameLayout::Size()); -+ masm.xs_mflr(ScratchRegister); -+ masm.push(scratch); -+ masm.push(ScratchRegister); -+ masm.jump(target); -+} -+*/ -+ -+inline void -+EmitBaselineCreateStubFrameDescriptor(MacroAssembler& masm, Register reg, uint32_t headerSize) -+{ -+ // Compute stub frame size. We have to add two pointers: the stub reg and -+ // previous frame pointer pushed by EmitEnterStubFrame. -+ masm.as_addi(reg, BaselineFrameReg, sizeof(intptr_t)*2); -+ masm.subPtr(BaselineStackReg, reg); -+ -+ masm.makeFrameDescriptor(reg, FrameType::BaselineStub, headerSize); -+} -+ -+inline void -+EmitBaselineCallVM(TrampolinePtr target, MacroAssembler& masm) -+{ -+ Register scratch = R2.scratchReg(); -+ EmitBaselineCreateStubFrameDescriptor(masm, scratch, ExitFrameLayout::Size()); -+ masm.push(scratch); -+ masm.call(target); -+} -+ -+inline void -+EmitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch) -+{ -+ // Compute frame size. -+ masm.as_addi(scratch, BaselineFrameReg, BaselineFrame::FramePointerOffset); -+ masm.subPtr(BaselineStackReg, scratch); -+ -+#ifdef DEBUG -+ Address frameSizeAddr(BaselineFrameReg, -+ 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.makeFrameDescriptor(scratch, FrameType::BaselineJS, BaselineStubFrameLayout::Size()); -+ // Keep the tail call register current (i.e., don't just use r0). -+ masm.xs_mflr(ICTailCallReg); -+ masm.subPtr(Imm32(STUB_FRAME_SIZE), StackPointer); -+ masm.storePtr(scratch, Address(StackPointer, offsetof(BaselineStubFrame, descriptor))); -+ masm.storePtr(ICTailCallReg, Address(StackPointer, -+ offsetof(BaselineStubFrame, returnAddress))); -+ -+ // Save old frame pointer, stack pointer and stub reg. -+ masm.storePtr(ICStubReg, Address(StackPointer, -+ offsetof(BaselineStubFrame, savedStub))); -+ masm.storePtr(BaselineFrameReg, Address(StackPointer, -+ offsetof(BaselineStubFrame, savedFrame))); -+ masm.movePtr(BaselineStackReg, BaselineFrameReg); -+ -+ // Stack should remain aligned. -+ masm.assertStackAlignment(sizeof(Value), 0); -+} -+ -+} // namespace jit -+} // namespace js -+ -+#endif /* jit_ppc64le_SharedICHelpers_ppc64le_inl_h */ -diff -r cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -0700 -@@ -0,0 +1,128 @@ -+/* -*- 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; -+}; -+ -+// Size of values pushed by EmitBaselineEnterStubFrame. -+static const uint32_t STUB_FRAME_SIZE = sizeof(BaselineStubFrame); -+static const uint32_t STUB_FRAME_SAVED_STUB_OFFSET = offsetof(BaselineStubFrame, savedStub); -+ -+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 -+EmitChangeICReturnAddress(MacroAssembler& masm, Register reg) -+{ -+ masm.xs_mtlr(reg); -+} -+ -+inline void -+EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false) -+{ -+ // Ion frames do not save and restore the frame pointer. If we called -+ // into Ion, we have to restore the stack pointer from the frame descriptor. -+ // If we performed a VM call, the descriptor has been popped already so -+ // in that case we use the frame pointer. -+ if (calledIntoIon) { -+ masm.pop(ScratchRegister); -+ masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), ScratchRegister); -+ masm.addPtr(ScratchRegister, BaselineStackReg); -+ } else { -+ masm.movePtr(BaselineFrameReg, BaselineStackReg); -+ } -+ -+ masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, savedFrame)), -+ BaselineFrameReg); -+ masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, savedStub)), -+ ICStubReg); -+ -+ // Load the return address. -+ // This is different on PowerPC because LR is not a GPR. However, we -+ // still need to have it in a GPR in case Ion or Baseline relies on it. -+ masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, returnAddress)), -+ ICTailCallReg); -+ masm.xs_mtlr(ICTailCallReg); -+ -+ // Discard the frame descriptor and the rest of the frame. -+ //masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, descriptor)), ScratchRegister); -+ masm.addPtr(Imm32(STUB_FRAME_SIZE), StackPointer); -+ masm.checkStackAlignment(); -+} -+ -+template -+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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -0700 -@@ -0,0 +1,46 @@ -+/* -*- 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 { -+ -+// The frame register should be allocatable but non-volatile. -+static constexpr Register BaselineFrameReg = r20; -+// 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 cd548157ebca -r 99b51ba09f3f 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 Sep 07 19:51:49 2023 -0700 -@@ -0,0 +1,1443 @@ -+/* -*- 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/DebugOnly.h" -+ -+#include "jit/Bailouts.h" -+#include "jit/JitFrames.h" -+#include "jit/JitRealm.h" -+#include "jit/JitSpewer.h" -+#include "jit/Linker.h" -+#include "jit/ppc64/Bailouts-ppc64.h" -+#include "jit/ppc64/SharedICHelpers-ppc64.h" -+#ifdef JS_ION_PERF -+# include "jit/PerfSpewer.h" -+#endif -+#include "jit/VMFunctions.h" -+#include "vm/Realm.h" -+ -+#include "jit/MacroAssembler-inl.h" -+#include "jit/SharedICHelpers-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) -+{ -+ ADBlock("generateEnterJIT"); -+ enterJITOffset_ = startTrampolineCode(masm); -+ -+ const Register reg_code = IntArgReg0; // r3 -+ const Register reg_argc = IntArgReg1; // r4 -+ const Register reg_argv = IntArgReg2; // r5 -+ const 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. -+ masm.as_std(reg_vp, StackPointer, offsetof(EnterJITRegs, savedvp)); -+ -+ // Hold stack pointer in a random clobberable register for computing -+ // the frame descriptor later. Arbitrarily, let's choose r31. -+ const Register frameDescSP = r31; -+ masm.movePtr(StackPointer, frameDescSP); -+ -+ // Save stack pointer as baseline frame. -+ masm.movePtr(StackPointer, BaselineFrameReg); -+ -+ /*************************************************************** -+ 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); -+ -+ masm.ma_bc(SecondScratchReg, reg_argv, &header, Assembler::Above, ShortJump); -+ } -+ masm.bind(&footer); -+ -+ masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer); -+ // Load the number of actual arguments. -+ // This is a 32-bit quantity. -+ // Then store it and the callee token on the stack. -+ masm.as_lwz(ScratchRegister, reg_vp, 0); -+ masm.storePtr(reg_token, Address(StackPointer, 0)); // callee token -+ masm.storePtr(ScratchRegister, Address(StackPointer, sizeof(uintptr_t))); // actual arguments -+ -+ // Push frame descriptor. -+ masm.subPtr(StackPointer, frameDescSP); -+ masm.makeFrameDescriptor(frameDescSP, FrameType::CppToJSJit, JitFrameLayout::Size()); -+ masm.push(frameDescSP); -+ -+ CodeLabel returnLabel; -+ CodeLabel oomReturnLabel; -+ { -+ // Handle Interpreter -> Baseline OSR. -+ AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); -+ regs.take(OsrFrameReg); -+ regs.take(BaselineFrameReg); -+ regs.take(reg_code); -+ MOZ_ASSERT(reg_code == ReturnReg); // regs.take(ReturnReg); -+ regs.take(JSReturnOperand); -+ -+ Label notOsr; -+ masm.ma_bc(OsrFrameReg, OsrFrameReg, ¬Osr, Assembler::Zero, ShortJump); -+ -+ Register numStackValues = reg_values; -+ regs.take(numStackValues); -+ Register scratch = regs.takeAny(); -+ -+ // Push return address. -+ masm.subPtr(Imm32(sizeof(uintptr_t)), StackPointer); -+ masm.ma_li(scratch, &returnLabel); -+ masm.storePtr(scratch, Address(StackPointer, 0)); -+ -+ // Push previous frame pointer. -+ masm.subPtr(Imm32(sizeof(uintptr_t)), StackPointer); -+ masm.storePtr(BaselineFrameReg, Address(StackPointer, 0)); -+ -+ // Reserve frame. -+ Register framePtr = BaselineFrameReg; -+ masm.subPtr(Imm32(BaselineFrame::Size()), StackPointer); -+ masm.movePtr(StackPointer, framePtr); -+ -+ // Reserve space for locals and stack values. -+ masm.x_sldi(scratch, numStackValues, 3); -+ masm.subPtr(scratch, StackPointer); -+ -+ // Enter exit frame. -+ masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch); -+ masm.makeFrameDescriptor(scratch, FrameType::BaselineJS, ExitFrameLayout::Size()); -+ -+ // Push frame descriptor and fake return address. -+ masm.reserveStack(2 * sizeof(uintptr_t)); -+ masm.storePtr(scratch, Address(StackPointer, sizeof(uintptr_t))); // Frame descriptor -+ masm.storePtr(scratch, Address(StackPointer, 0)); // fake return address -+ -+ // 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(framePtr, Address(StackPointer, sizeof(uintptr_t))); // BaselineFrameReg -+ masm.storePtr(reg_code, Address(StackPointer, 0)); // jitcode -+ -+ // Initialize the frame, including filling in the slots. -+ using Fn = bool (*)(BaselineFrame * frame, InterpreterFrame * interpFrame, -+ uint32_t numStackValues); -+ masm.setupUnalignedABICall(scratch); -+ masm.passABIArg(BaselineFrameReg); // BaselineFrame -+ masm.passABIArg(OsrFrameReg); // InterpreterFrame -+ masm.passABIArg(numStackValues); -+ masm.callWithABI( -+ MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame); -+ -+ regs.add(OsrFrameReg); -+ Register jitcode = regs.takeAny(); -+ masm.loadPtr(Address(StackPointer, 0), jitcode); -+ masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), framePtr); -+ masm.freeStack(2 * sizeof(uintptr_t)); -+ -+ Label error; -+ masm.freeStack(ExitFrameLayout::SizeWithFooter()); -+ masm.addPtr(Imm32(BaselineFrame::Size()), framePtr); -+ masm.branchIfFalseBool(ReturnReg, &error); -+ -+ // If OSR-ing, then emit instrumentation for setting lastProfilerFrame -+ // if profiler instrumentation is enabled. -+ { -+ Label skipProfilingInstrumentation; -+ Register realFramePtr = numStackValues; -+ AbsoluteAddress addressOfEnabled(cx->runtime()->geckoProfiler().addressOfEnabled()); -+ masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), -+ &skipProfilingInstrumentation); -+ masm.as_addi(realFramePtr, framePtr, sizeof(void*)); -+ masm.profilerEnterFrame(realFramePtr, scratch); -+ masm.bind(&skipProfilingInstrumentation); -+ } -+ -+//masm.xs_trap_tagged(Assembler::DebugTag0); -+ masm.jump(jitcode); -+ -+ // OOM: load error value, discard return address and previous frame -+ // pointer and return. -+ masm.bind(&error); -+ masm.movePtr(framePtr, StackPointer); -+ masm.addPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer); -+ masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); -+ masm.ma_li(scratch, &oomReturnLabel); -+ masm.jump(scratch); -+ -+ 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); -+ masm.addCodeLabel(oomReturnLabel); -+ } -+ -+ // Pop arguments off the stack. -+ // scratch <- 8*argc (size of all arguments we pushed on the stack) -+ masm.pop(ScratchRegister); -+ masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), ScratchRegister); -+ masm.addPtr(ScratchRegister, 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(); -+} -+ -+void -+JitRuntime::generateInvalidator(MacroAssembler& masm, Label* bailoutTail) -+{ -+ 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 alligned 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 return value and BailoutInfo pointer -+ masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer); -+ // Pass pointer to return value. -+ masm.as_addi(r4, StackPointer, (uint16_t)sizeof(uintptr_t)); -+ // Pass pointer to BailoutInfo -+ masm.movePtr(StackPointer, r5); -+ -+ using Fn = bool (*)(InvalidationBailoutStack * sp, size_t * frameSizeOut, -+ BaselineBailoutInfo * *info); -+ masm.setupAlignedABICall(); -+ masm.passABIArg(r3); -+ masm.passABIArg(r4); -+ masm.passABIArg(r5); -+ masm.callWithABI( -+ MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther); -+ -+ masm.loadPtr(Address(StackPointer, 0), r5); -+ masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), r4); -+ // Remove the return address, the IonScript, the register state -+ // (InvaliationBailoutStack) and the space that was allocated for the -+ // return value. -+ masm.addPtr(Imm32(sizeof(InvalidationBailoutStack) + 2 * sizeof(uintptr_t)), StackPointer); -+ // Remove the space that this frame was using before the bailout -+ // (computed by InvalidationBailout). -+ masm.addPtr(r4, StackPointer); -+ -+ // Jump to shared bailout tail. The BailoutInfo pointer remains in r5. -+ // The return code is left unchanged by this routine in r3. -+ masm.jump(bailoutTail); -+} -+ -+void -+JitRuntime::generateArgumentsRectifier(MacroAssembler& masm, -+ ArgumentsRectifierKind kind) -+{ -+ ADBlock("generateArgumentsRectifier"); -+ // MIPS uses a5-a7, t0-t3 and s3, with s3 being the only callee-save register. -+ // We will do something similar for Power and use r4-r6, r7-r10 and r15. -+ const Register nvRectReg = r15; -+ -+ // Do not erase the frame pointer in this function. -+ -+ switch (kind) { -+ case ArgumentsRectifierKind::Normal: -+ argumentsRectifierOffset_ = startTrampolineCode(masm); -+ break; -+ case ArgumentsRectifierKind::TrialInlining: -+ trialInliningArgumentsRectifierOffset_ = startTrampolineCode(masm); -+ break; -+ } -+ masm.pushReturnAddress(); -+ -+ // Caller: -+ // [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- sp -+ -+ // Get the |nargs| from the RectifierFrame. -+ masm.loadPtr(Address(StackPointer, RectifierFrameLayout::offsetOfNumActualArgs()), nvRectReg); -+ // Add one for |this|. -+ masm.addPtr(Imm32(1), nvRectReg); -+ -+ const Register numActArgsReg = r5; -+ const Register calleeTokenReg = r6; -+ const Register tempValue = r7; -+ const Register numArgsReg = r4; -+ const Register numToPush = r8; -+ const Register tempCalleeTokenReg = r9; -+ const Register tempNumArgsReg = r10; -+ -+ // Load |nformals| into numArgsReg. -+ masm.loadPtr(Address(StackPointer, RectifierFrameLayout::offsetOfCalleeToken()), -+ calleeTokenReg); -+ masm.mov(calleeTokenReg, numArgsReg); -+ masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), numArgsReg); -+ masm.load32(Address(numArgsReg, JSFunction::offsetOfFlagsAndArgCount()), -+ numArgsReg); -+ masm.rshift32(Imm32(JSFunction::ArgCountShift), 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); -+ 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); -+ -+ // Load the number of |undefined|s to push (nargs - nvRectReg). -+ masm.as_subf(numToPush, nvRectReg, numArgsReg); // T = B - A -+ -+ // Caller: -+ // [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- sp <- r9 -+ // '--- nvRectReg ---' -+ // -+ // Rectifier frame: -+ // [undef] [undef] [undef] [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] -+ // '-------- r8 ---------' '---- nvRectReg ---' -+ -+ // Copy number of actual arguments into numActArgsReg -+ masm.loadPtr(Address(StackPointer, RectifierFrameLayout::offsetOfNumActualArgs()), -+ numActArgsReg); -+ -+ -+ masm.moveValue(UndefinedValue(), ValueOperand(tempValue)); -+ -+ masm.movePtr(StackPointer, tempCalleeTokenReg); // Save stack pointer. We can clobber it. -+ -+ // 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"); -+ -+ // | - sizeof(Value)| is used such that we can read the last -+ // argument, and not the value which is after. -+ 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, r9, r7); // r8 <- t9(saved sp) + nargs * 8 -+ masm.addPtr(Imm32(sizeof(RectifierFrameLayout) - sizeof(Value)), numToPush); -+ -+ // Copy and push arguments |nargs| + 1 times (to include |this|). -+ { -+#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); -+ -+ // +1 for |this|. We want vp[argc], so don't subtract 1. -+ BaseIndex newTargetSrc(r9, 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]] <- r9 -+ // -+ // -+ // Rectifier frame: -+ // [undef] [undef] [undef] [arg2] [arg1] [this] <- sp [[argc] [callee] [descr] [raddr]] -+ -+ MOZ_ASSERT(numToPush == r8); // can clobber -+ MOZ_ASSERT(tempCalleeTokenReg == r9); // can clobber -+ -+ // Construct sizeDescriptor. -+ masm.subPtr(StackPointer, r9); -+ masm.makeFrameDescriptor(r9, FrameType::Rectifier, JitFrameLayout::Size()); -+ -+ // Construct JitFrameLayout. -+ masm.subPtr(Imm32(3 * sizeof(uintptr_t)), StackPointer); -+ // Push actual arguments. -+ masm.storePtr(numActArgsReg, Address(StackPointer, 2 * sizeof(uintptr_t))); -+ // Push callee token. -+ masm.storePtr(calleeTokenReg, Address(StackPointer, sizeof(uintptr_t))); -+ // Push frame descriptor. -+ masm.storePtr(r9, Address(StackPointer, 0)); -+ -+ // Call the target function. -+ masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), calleeTokenReg); -+ switch (kind) { -+ case ArgumentsRectifierKind::Normal: -+ masm.loadJitCodeRaw(calleeTokenReg, 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; -+ } -+ -+ // Remove the rectifier frame. -+ masm.loadPtr(Address(StackPointer, 0), r9); -+ masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), r9); -+ -+ // Discard descriptor, calleeToken and number of actual arguments. -+ masm.addPtr(Imm32(3 * sizeof(uintptr_t)), StackPointer); -+ -+ // Discard pushed arguments. -+ masm.addPtr(r9, StackPointer); -+ -+ 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(). Field -+ * frameClassId_ is forced to be NO_FRAME_SIZE_CLASS_ID -+ * (see JitRuntime::generateBailoutHandler). -+ */ -+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, uint32_t frameClass, 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.setupAlignedABICall(); -+ masm.passABIArg(r3); -+ masm.passABIArg(r4); -+ masm.callWithABI(MoveOp::GENERAL, -+ CheckUnsafeCallWithABI::DontCheckOther); -+ -+ // Get BailoutInfo pointer. -+ masm.loadPtr(Address(StackPointer, 0), r5); -+ -+ // Stack is: -+ // [frame] -+ // snapshotOffset -+ // frameSize -+ // [bailoutFrame] -+ // [bailoutInfo] -+ // -+ // Remove both the bailout frame and the topmost Ion frame's stack. -+ // First, load frameSize from stack. -+ masm.loadPtr(Address(StackPointer, -+ sizeOfBailoutInfo + BailoutStack::offsetOfFrameSize()), r4); -+ // Remove complete BailoutStack class and data after it. -+ masm.addPtr(Imm32(sizeof(BailoutStack) + sizeOfBailoutInfo), StackPointer); -+ // Finally, remove frame size from stack. -+ masm.addPtr(r4, StackPointer); -+ -+ // 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); -+} -+ -+JitRuntime::BailoutTable -+JitRuntime::generateBailoutTable(MacroAssembler& masm, Label* bailoutTail, uint32_t frameClass) -+{ -+ MOZ_CRASH("PPC64 does not use bailout tables"); -+} -+ -+void -+JitRuntime::generateBailoutHandler(MacroAssembler& masm, Label* bailoutTail) -+{ -+ ADBlock("generateBailoutHandler"); -+ bailoutHandlerOffset_ = startTrampolineCode(masm); -+ -+ GenerateBailoutThunk(masm, NO_FRAME_SIZE_CLASS_ID, bailoutTail); -+} -+ -+bool -+JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm, -+ const VMFunctionData& f, DynFn nativeFun, -+ uint32_t* wrapperOffset) -+{ -+ ADBlock("generateVMWrapper"); -+ *wrapperOffset = startTrampolineCode(masm); -+ -+ 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; a0 is the first argument register. -+ Register cxreg = r3; -+ regs.take(cxreg); -+ -+ // If it isn't a tail call, then the return address needs to be saved. -+ // Even though the callee should do this for us, we may change the return address. -+ // This completes any exit frame on top of the stack (see JitFrames.h). -+ if (f.expectTailCall == NonTailCall) -+ masm.pushReturnAddress(); -+ -+ // We're aligned to an exit frame, so link it up. -+ masm.loadJSContext(cxreg); -+ masm.enterExitFrame(cxreg, regs.getAny(), &f); -+ -+ // Save the base of the argument set stored on the stack. -+ Register argsBase = InvalidReg; -+ if (f.explicitArgs) { -+ argsBase = ThirdScratchReg; // It can't be r0, r1, r2, r12 or an argsreg, so ... -+ masm.as_addi(argsBase, StackPointer, ExitFrameLayout::SizeWithFooter()); -+ } -+ -+ // Reserve space for the outparameter. -+ Register outReg = InvalidReg; -+ switch (f.outParam) { -+ case Type_Value: -+ outReg = regs.takeAny(); -+ masm.reserveStack(sizeof(Value)); -+ masm.movePtr(StackPointer, outReg); -+ break; -+ -+ case Type_Handle: -+ outReg = regs.takeAny(); -+ masm.PushEmptyRooted(f.outParamRootType); -+ masm.movePtr(StackPointer, outReg); -+ break; -+ -+ case Type_Bool: -+ case Type_Int32: -+ outReg = regs.takeAny(); -+ // Reserve 4-byte space to make stack aligned to 8-byte. -+ masm.reserveStack(2 * sizeof(int32_t)); -+ masm.movePtr(StackPointer, outReg); -+ break; -+ -+ case Type_Pointer: -+ outReg = regs.takeAny(); -+ masm.reserveStack(sizeof(uintptr_t)); -+ masm.movePtr(StackPointer, outReg); -+ break; -+ -+ case Type_Double: -+ outReg = regs.takeAny(); -+ masm.reserveStack(sizeof(double)); -+ masm.movePtr(StackPointer, outReg); -+ break; -+ -+ default: -+ MOZ_ASSERT(f.outParam == Type_Void); -+ break; -+ } -+ -+ if (!generateTLEnterVM(masm, f)) -+ return false; -+ -+ masm.setupUnalignedABICall(regs.getAny()); -+ masm.passABIArg(cxreg); -+ -+ size_t argDisp = 0; -+ -+ // 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(argsBase, argDisp), MoveOp::DOUBLE); -+ else -+ masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); -+ argDisp += sizeof(void*); -+ break; -+ case VMFunctionData::WordByRef: -+ masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), -+ MoveOp::GENERAL); -+ argDisp += sizeof(void*); -+ break; -+ case VMFunctionData::DoubleByValue: -+ case VMFunctionData::DoubleByRef: -+ MOZ_CRASH("NYI: PPC64 callVM no support for 128-bit values"); -+ break; -+ } -+ } -+ -+ // Copy the implicit outparam, if any. -+ if (InvalidReg != outReg) -+ masm.passABIArg(outReg); -+ -+ masm.callWithABI(nativeFun, MoveOp::GENERAL, -+ CheckUnsafeCallWithABI::DontCheckHasExitFrame); -+ -+ -+ if (!generateTLExitVM(masm, f)) -+ return false; -+ -+ // 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 and free any allocated stack. -+ switch (f.outParam) { -+ case Type_Handle: -+ masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand); -+ break; -+ -+ case Type_Value: -+ masm.loadValue(Address(StackPointer, 0), JSReturnOperand); -+ masm.freeStack(sizeof(Value)); -+ break; -+ -+ case Type_Int32: -+ masm.load32(Address(StackPointer, 0), ReturnReg); -+ masm.freeStack(2 * sizeof(int32_t)); -+ break; -+ -+ case Type_Pointer: -+ masm.loadPtr(Address(StackPointer, 0), ReturnReg); -+ masm.freeStack(sizeof(uintptr_t)); -+ break; -+ -+ case Type_Bool: -+ // Boolean on Power ISA is not necessarily a byte, but we should -+ // treat it like one. -+ masm.loadPtr(Address(StackPointer, 0), ReturnReg); -+ masm.as_andi_rc(ReturnReg, ReturnReg, 255); -+ masm.freeStack(2 * sizeof(int32_t)); -+ break; -+ -+ case Type_Double: -+ masm.loadDouble(Address(StackPointer, 0), ReturnDoubleReg); -+ masm.freeStack(sizeof(double)); -+ break; -+ -+ default: -+ MOZ_ASSERT(f.outParam == Type_Void); -+ break; -+ } -+ -+ masm.leaveExitFrame(); -+ masm.retn(Imm32(sizeof(ExitFrameLayout) + -+ f.explicitStackSlots() * sizeof(void*) + -+ f.extraValuesToPop * sizeof(Value))); -+ -+ return true; -+} -+ -+uint32_t -+JitRuntime::generatePreBarrier(JSContext* cx, MacroAssembler& masm, MIRType type) -+{ -+ ADBlock("generatePreBarrier"); -+ uint32_t offset = startTrampolineCode(masm); -+ -+ MOZ_ASSERT(PreBarrierReg == r4); -+ Register temp1 = r3; -+ Register temp2 = r5; -+ Register temp3 = r6; -+ 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; -+} -+ -+typedef bool (*HandleDebugTrapFn)(JSContext*, BaselineFrame*, uint8_t*, bool*); -+ -+void -+JitRuntime::generateExceptionTailStub(MacroAssembler& masm, Label* profilerExitTail) -+{ -+ ADBlock("generateExceptionTailStub"); -+ exceptionTailOffset_ = startTrampolineCode(masm); -+ -+ masm.bind(masm.failureLabel()); -+ masm.handleFailureWithHandlerTail(profilerExitTail); -+} -+ -+void -+JitRuntime::generateBailoutTailStub(MacroAssembler& masm, Label* bailoutTail) -+{ -+ ADBlock("generateBailoutTailStub"); -+ bailoutTailOffset_ = startTrampolineCode(masm); -+ masm.bind(bailoutTail); -+ -+ masm.generateBailoutTail(r4, r5); -+} -+ -+void -+JitRuntime::generateProfilerExitFrameTailStub(MacroAssembler& masm, Label* profilerExitTail) -+{ -+ ADBlock("generateProfilerExitFrameTailStub"); -+ profilerExitFrameTailOffset_ = startTrampolineCode(masm); -+ masm.bind(profilerExitTail); -+ -+ Register scratch1 = r7; // XXX? -+ Register scratch2 = r8; -+ Register scratch3 = r9; -+ Register scratch4 = r10; -+ -+ // -+ // The code generated below expects that the current stack pointer points -+ // to an Ion or Baseline frame, at the state it would be immediately -+ // before a ret(). Thus, after this stub's business is done, it executes -+ // a ret() and returns directly to the caller script, on behalf of the -+ // callee script that jumped to this code. -+ // -+ // Thus the expected stack is: -+ // -+ // StackPointer ----+ -+ // v -+ // ..., ActualArgc, CalleeToken, Descriptor, ReturnAddr -+ // MEM-HI MEM-LOW -+ // -+ // -+ // The generated jitcode is responsible for overwriting the -+ // jitActivation->lastProfilingFrame field with a pointer to the previous -+ // Ion or Baseline jit-frame that was pushed before this one. It is also -+ // responsible for overwriting jitActivation->lastProfilingCallSite with -+ // the return address into that frame. The frame could either be an -+ // immediate "caller" frame, or it could be a frame in a previous -+ // JitActivation (if the current frame was entered from C++, and the C++ -+ // was entered by some caller jit-frame further down the stack). -+ // -+ // So this jitcode is responsible for "walking up" the jit stack, finding -+ // the previous Ion or Baseline JS frame, and storing its address and the -+ // return address into the appropriate fields on the current jitActivation. -+ // -+ // There are a fixed number of different path types that can lead to the -+ // current frame, which is either a baseline or ion frame: -+ // -+ // -+ // ^ -+ // | -+ // ^--- Ion -+ // | -+ // ^--- Baseline Stub <---- Baseline -+ // | -+ // ^--- Argument Rectifier -+ // | ^ -+ // | | -+ // | ^--- Ion -+ // | | -+ // | ^--- Baseline Stub <---- Baseline -+ // | -+ // ^--- Entry Frame (From C++) -+ // -+ Register actReg = scratch4; -+ masm.loadJSContext(actReg); -+ masm.loadPtr(Address(actReg, offsetof(JSContext, profilingActivation_)), actReg); -+ -+ Address lastProfilingFrame(actReg, JitActivation::offsetOfLastProfilingFrame()); -+ Address lastProfilingCallSite(actReg, JitActivation::offsetOfLastProfilingCallSite()); -+ -+#ifdef DEBUG -+ // Ensure that frame we are exiting is current lastProfilingFrame -+ { -+ masm.loadPtr(lastProfilingFrame, scratch1); -+ Label checkOk; -+ masm.branchPtr(Assembler::Equal, scratch1, ImmWord(0), &checkOk); -+ masm.branchPtr(Assembler::Equal, StackPointer, scratch1, &checkOk); -+ masm.assumeUnreachable( -+ "Mismatch between stored lastProfilingFrame and current stack pointer."); -+ masm.bind(&checkOk); -+ } -+#endif -+ -+ // Load the frame descriptor into |scratch1|, figure out what to do depending on its type. -+ masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfDescriptor()), scratch1); -+ -+ // Going into the conditionals, we will have: -+ // FrameDescriptor.size in scratch1 -+ // FrameDescriptor.type in scratch2 -+ masm.ma_and(scratch2, scratch1, Imm32((1 << FRAMETYPE_BITS) - 1)); -+ masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch1); -+ -+ // Handling of each case is dependent on FrameDescriptor.type -+ Label handle_IonJS; -+ Label handle_BaselineStub; -+ Label handle_Rectifier; -+ Label handle_IonICCall; -+ Label handle_Entry; -+ Label end; -+ -+ masm.branch32(Assembler::Equal, scratch2, Imm32(FrameType::IonJS), -+ &handle_IonJS); -+ masm.branch32(Assembler::Equal, scratch2, Imm32(FrameType::BaselineJS), -+ &handle_IonJS); -+ masm.branch32(Assembler::Equal, scratch2, Imm32(FrameType::BaselineStub), -+ &handle_BaselineStub); -+ masm.branch32(Assembler::Equal, scratch2, Imm32(FrameType::Rectifier), -+ &handle_Rectifier); -+ masm.branch32(Assembler::Equal, scratch2, Imm32(FrameType::IonICCall), -+ &handle_IonICCall); -+ masm.branch32(Assembler::Equal, scratch2, Imm32(FrameType::CppToJSJit), -+ &handle_Entry); -+ -+ // The WasmToJSJit is just another kind of entry. -+ masm.branch32(Assembler::Equal, scratch2, Imm32(FrameType::WasmToJSJit), -+ &handle_Entry); -+ -+ masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame."); -+ -+ // -+ // FrameType::IonJS -+ // -+ // Stack layout: -+ // ... -+ // Ion-Descriptor -+ // Prev-FP ---> Ion-ReturnAddr -+ // ... previous frame data ... |- Descriptor.Size -+ // ... arguments ... | -+ // ActualArgc | -+ // CalleeToken |- JitFrameLayout::Size() -+ // Descriptor | -+ // FP -----> ReturnAddr | -+ // -+ masm.bind(&handle_IonJS); -+ { -+ // |scratch1| contains Descriptor.size -+ -+ // returning directly to an IonJS frame. Store return addr to frame -+ // in lastProfilingCallSite. -+ masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfReturnAddress()), scratch2); -+ masm.storePtr(scratch2, lastProfilingCallSite); -+ -+ // Store return frame in lastProfilingFrame. -+ // scratch2 := StackPointer + Descriptor.size*1 + JitFrameLayout::Size(); -+ masm.as_add(scratch2, StackPointer, scratch1); -+ masm.as_addi(scratch2, scratch2, JitFrameLayout::Size()); -+ masm.storePtr(scratch2, lastProfilingFrame); -+ masm.ret(); -+ } -+ -+ // -+ // FrameType::BaselineStub -+ // -+ // Look past the stub and store the frame pointer to -+ // the baselineJS frame prior to it. -+ // -+ // Stack layout: -+ // ... -+ // BL-Descriptor -+ // Prev-FP ---> BL-ReturnAddr -+ // +-----> BL-PrevFramePointer -+ // | ... BL-FrameData ... -+ // | BLStub-Descriptor -+ // | BLStub-ReturnAddr -+ // | BLStub-StubPointer | -+ // +------ BLStub-SavedFramePointer |- Descriptor.Size -+ // ... arguments ... | -+ // ActualArgc | -+ // CalleeToken |- JitFrameLayout::Size() -+ // Descriptor | -+ // FP -----> ReturnAddr | -+ // -+ // We take advantage of the fact that the stub frame saves the frame -+ // pointer pointing to the baseline frame, so a bunch of calculation can -+ // be avoided. -+ // -+ masm.bind(&handle_BaselineStub); -+ { -+ masm.as_add(scratch3, StackPointer, scratch1); -+ Address stubFrameReturnAddr(scratch3, -+ JitFrameLayout::Size() + -+ BaselineStubFrameLayout::offsetOfReturnAddress()); -+ masm.loadPtr(stubFrameReturnAddr, scratch2); -+ masm.storePtr(scratch2, lastProfilingCallSite); -+ -+ Address stubFrameSavedFramePtr(scratch3, -+ JitFrameLayout::Size() - (2 * sizeof(void*))); -+ masm.loadPtr(stubFrameSavedFramePtr, scratch2); -+ masm.addPtr(Imm32(sizeof(void*)), scratch2); // Skip past BL-PrevFramePtr -+ masm.storePtr(scratch2, lastProfilingFrame); -+ masm.ret(); -+ } -+ -+ -+ // -+ // FrameType::Rectifier -+ // -+ // The rectifier frame can be preceded by either an IonJS, a BaselineStub, -+ // or a CppToJSJit/WasmToJSJit frame. -+ // -+ // Stack layout if caller of rectifier was Ion or CppToJSJit/WasmToJSJit: -+ // -+ // Ion-Descriptor -+ // Ion-ReturnAddr -+ // ... ion frame data ... |- Rect-Descriptor.Size -+ // < COMMON LAYOUT > -+ // -+ // Stack layout if caller of rectifier was Baseline: -+ // -+ // BL-Descriptor -+ // Prev-FP ---> BL-ReturnAddr -+ // +-----> BL-SavedFramePointer -+ // | ... baseline frame data ... -+ // | BLStub-Descriptor -+ // | BLStub-ReturnAddr -+ // | BLStub-StubPointer | -+ // +------ BLStub-SavedFramePointer |- Rect-Descriptor.Size -+ // ... args to rectifier ... | -+ // < COMMON LAYOUT > -+ // -+ // Common stack layout: -+ // -+ // ActualArgc | -+ // CalleeToken |- IonRectitiferFrameLayout::Size() -+ // Rect-Descriptor | -+ // Rect-ReturnAddr | -+ // ... rectifier data & args ... |- Descriptor.Size -+ // ActualArgc | -+ // CalleeToken |- JitFrameLayout::Size() -+ // Descriptor | -+ // FP -----> ReturnAddr | -+ // -+ masm.bind(&handle_Rectifier); -+ { -+ // scratch2 := StackPointer + Descriptor.size*1 + JitFrameLayout::Size(); -+ masm.as_add(scratch2, StackPointer, scratch1); -+ masm.addPtr(Imm32(JitFrameLayout::Size()), scratch2); -+ masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfDescriptor()), scratch3); -+ masm.x_srwi(scratch1, scratch3, FRAMESIZE_SHIFT); -+ masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch3); -+ -+ // Now |scratch1| contains Rect-Descriptor.Size -+ // and |scratch2| points to Rectifier frame -+ // and |scratch3| contains Rect-Descriptor.Type -+ -+ masm.assertRectifierFrameParentType(scratch3); -+ -+ // Check for either Ion or BaselineStub frame. -+ Label notIonFrame; -+ masm.branch32(Assembler::NotEqual, scratch3, Imm32(FrameType::IonJS), ¬IonFrame); -+ -+ // Handle Rectifier <- IonJS -+ // scratch3 := RectFrame[ReturnAddr] -+ masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfReturnAddress()), scratch3); -+ masm.storePtr(scratch3, lastProfilingCallSite); -+ -+ // scratch3 := RectFrame + Rect-Descriptor.Size + RectifierFrameLayout::Size() -+ masm.as_add(scratch3, scratch2, scratch1); -+ masm.addPtr(Imm32(RectifierFrameLayout::Size()), scratch3); -+ masm.storePtr(scratch3, lastProfilingFrame); -+ masm.ret(); -+ -+ masm.bind(¬IonFrame); -+ -+ // Check for either BaselineStub or a CppToJSJit/WasmToJSJit entry -+ // frame. -+ masm.branch32(Assembler::NotEqual, scratch3, Imm32(FrameType::BaselineStub), &handle_Entry); -+ -+ // Handle Rectifier <- BaselineStub <- BaselineJS -+ masm.as_add(scratch3, scratch2, scratch1); -+ Address stubFrameReturnAddr(scratch3, RectifierFrameLayout::Size() + -+ BaselineStubFrameLayout::offsetOfReturnAddress()); -+ masm.loadPtr(stubFrameReturnAddr, scratch2); -+ masm.storePtr(scratch2, lastProfilingCallSite); -+ -+ Address stubFrameSavedFramePtr(scratch3, -+ RectifierFrameLayout::Size() - (2 * sizeof(void*))); -+ masm.loadPtr(stubFrameSavedFramePtr, scratch2); -+ masm.addPtr(Imm32(sizeof(void*)), scratch2); -+ masm.storePtr(scratch2, lastProfilingFrame); -+ masm.ret(); -+ } -+ -+ // FrameType::IonICCall -+ // -+ // The caller is always an IonJS frame. -+ // -+ // Ion-Descriptor -+ // Ion-ReturnAddr -+ // ... ion frame data ... |- CallFrame-Descriptor.Size -+ // StubCode | -+ // ICCallFrame-Descriptor |- IonICCallFrameLayout::Size() -+ // ICCallFrame-ReturnAddr | -+ // ... call frame data & args ... |- Descriptor.Size -+ // ActualArgc | -+ // CalleeToken |- JitFrameLayout::Size() -+ // Descriptor | -+ // FP -----> ReturnAddr | -+ masm.bind(&handle_IonICCall); -+ { -+ // scratch2 := StackPointer + Descriptor.size + JitFrameLayout::Size() -+ masm.as_add(scratch2, StackPointer, scratch1); -+ masm.addPtr(Imm32(JitFrameLayout::Size()), scratch2); -+ -+ // scratch3 := ICCallFrame-Descriptor.Size -+ masm.loadPtr(Address(scratch2, IonICCallFrameLayout::offsetOfDescriptor()), scratch3); -+#ifdef DEBUG -+ // Assert previous frame is an IonJS frame. -+ masm.movePtr(scratch3, scratch1); -+ masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch1); -+ { -+ Label checkOk; -+ masm.branch32(Assembler::Equal, scratch1, Imm32(FrameType::IonJS), &checkOk); -+ masm.assumeUnreachable("IonICCall frame must be preceded by IonJS frame"); -+ masm.bind(&checkOk); -+ } -+#endif -+ masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch3); -+ -+ // lastProfilingCallSite := ICCallFrame-ReturnAddr -+ masm.loadPtr(Address(scratch2, IonICCallFrameLayout::offsetOfReturnAddress()), scratch1); -+ masm.storePtr(scratch1, lastProfilingCallSite); -+ -+ // lastProfilingFrame := ICCallFrame + ICCallFrame-Descriptor.Size + -+ // IonICCallFrameLayout::Size() -+ masm.as_add(scratch1, scratch2, scratch3); -+ masm.addPtr(Imm32(IonICCallFrameLayout::Size()), scratch1); -+ masm.storePtr(scratch1, lastProfilingFrame); -+ masm.ret(); -+ } -+ -+ // -+ // FrameType::CppToJSJit / FrameType::WasmToJSJit -+ // -+ // If at an entry frame, store null into both fields. -+ // A fast-path wasm->jit transition frame is an entry frame from the point -+ // of view of the JIT. -+ // -+ masm.bind(&handle_Entry); -+ { -+ masm.movePtr(ImmPtr(nullptr), scratch1); -+ masm.storePtr(scratch1, lastProfilingCallSite); -+ masm.storePtr(scratch1, lastProfilingFrame); -+ masm.ret(); -+ } -+} -+ -+// 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{}; -+} -+ -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/shared/Assembler-shared.h ---- a/js/src/jit/shared/Assembler-shared.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/shared/Assembler-shared.h Thu Sep 07 19:51:49 2023 -0700 -@@ -22,24 +22,24 @@ - #include "js/ScalarType.h" // js::Scalar::Type - #include "vm/HelperThreads.h" - #include "wasm/WasmCodegenTypes.h" - #include "wasm/WasmConstants.h" - - #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ - defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ - defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_WASM32) || \ -- defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_RISCV64) || defined(JS_CODEGEN_PPC64) - // Push return addresses callee-side. - # define JS_USE_LINK_REGISTER - #endif - - #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ - defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_LOONG64) || \ -- defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_RISCV64) || defined(JS_CODEGEN_PPC64) - // JS_CODELABEL_LINKMODE gives labels additional metadata - // describing how Bind() should patch them. - # define JS_CODELABEL_LINKMODE - #endif - - namespace js { - namespace jit { - -diff -r cd548157ebca -r 99b51ba09f3f js/src/jit/shared/AtomicOperations-shared-jit.cpp ---- a/js/src/jit/shared/AtomicOperations-shared-jit.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jit/shared/AtomicOperations-shared-jit.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -54,16 +54,19 @@ - # endif - # if defined(__x86_64__) || defined(__i386__) - return true; - # elif defined(__arm__) - return !HasAlignmentFault(); - # elif defined(__aarch64__) - // This is not necessarily true but it's the best guess right now. - return true; -+#elif defined(JS_CODEGEN_PPC64) -+ // We'd sure like to avoid it, even though it works. -+ return false; - # else - # error "Unsupported platform" - # endif - } - - # ifndef JS_64BIT - void AtomicCompilerFence() { - std::atomic_signal_fence(std::memory_order_acq_rel); -diff -r cd548157ebca -r 99b51ba09f3f js/src/jsapi-tests/testJitABIcalls.cpp ---- a/js/src/jsapi-tests/testJitABIcalls.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jsapi-tests/testJitABIcalls.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -664,16 +664,19 @@ - Register base = t1; - regs.take(base); - #elif defined(JS_CODEGEN_LOONG64) - Register base = t0; - regs.take(base); - #elif defined(JS_CODEGEN_RISCV64) - Register base = t0; - regs.take(base); -+#elif defined(JS_CODEGEN_PPC64) -+ Register base = r0; -+ regs.take(base); - #else - # error "Unknown architecture!" - #endif - - Register setup = regs.takeAny(); - - this->generateCalls(masm, base, setup); - -diff -r cd548157ebca -r 99b51ba09f3f js/src/jsapi-tests/testsJit.cpp ---- a/js/src/jsapi-tests/testsJit.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/jsapi-tests/testsJit.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -21,16 +21,21 @@ - AllocatableRegisterSet regs(RegisterSet::All()); - LiveRegisterSet save(regs.asLiveSet()); - #if defined(JS_CODEGEN_ARM) - save.add(js::jit::d15); - #endif - #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ - defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) - save.add(js::jit::ra); -+#elif defined(JS_CODEGEN_PPC64) -+ // XXX -+ // Push the link register separately, since it's not a GPR. -+ masm.xs_mflr(ScratchRegister); -+ masm.as_stdu(ScratchRegister, StackPointer, -8); - #elif defined(JS_USE_LINK_REGISTER) - save.add(js::jit::lr); - #endif - masm.PushRegsInMask(save); - } - - // Generate the exit path of the JIT code, which restores every register. Then, - // make it executable and run it. -@@ -39,26 +44,35 @@ - AllocatableRegisterSet regs(RegisterSet::All()); - LiveRegisterSet save(regs.asLiveSet()); - #if defined(JS_CODEGEN_ARM) - save.add(js::jit::d15); - #endif - #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ - defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) - save.add(js::jit::ra); -+#elif defined(JS_CODEGEN_PPC64) -+ // We pop after loading the regs. - #elif defined(JS_USE_LINK_REGISTER) - save.add(js::jit::lr); - #endif - masm.PopRegsInMask(save); - #if defined(JS_CODEGEN_ARM64) - // Return using the value popped into x30. - masm.abiret(); - - // Reset stack pointer. - masm.SetStackPointer64(PseudoStackPointer64); -+#elif defined(JS_CODEGEN_PPC64) -+ // XXX -+ // Pop LR and exit. -+ masm.as_ld(ScratchRegister, StackPointer, 0); -+ masm.xs_mtlr(ScratchRegister); -+ masm.as_addi(StackPointer, StackPointer, 8); -+ masm.as_blr(); - #else - // Exit the JIT-ed code using the ABI return style. - masm.abiret(); - #endif - - if (masm.oom()) { - return false; - } -diff -r cd548157ebca -r 99b51ba09f3f js/src/util/Poison.h ---- a/js/src/util/Poison.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/util/Poison.h Thu Sep 07 19:51:49 2023 -0700 -@@ -86,16 +86,18 @@ - # define JS_SWEPT_CODE_PATTERN 0xA3 // undefined instruction - #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) - # define JS_SWEPT_CODE_PATTERN 0x01 // undefined instruction - #elif defined(JS_CODEGEN_LOONG64) - # define JS_SWEPT_CODE_PATTERN 0x01 // undefined instruction - #elif defined(JS_CODEGEN_RISCV64) - # define JS_SWEPT_CODE_PATTERN \ - 0x29 // illegal sb instruction, crashes in user mode. -+#elif defined(JS_CODEGEN_PPC64) -+# define JS_SWEPT_CODE_PATTERN 0x00 // architecturally defined as illegal - #else - # error "JS_SWEPT_CODE_PATTERN not defined for this platform" - #endif - - enum class MemCheckKind : uint8_t { - // Marks a region as poisoned. Memory sanitizers like ASan will crash when - // accessing it (both reads and writes). - MakeNoAccess, -diff -r cd548157ebca -r 99b51ba09f3f js/src/wasm/WasmBCDefs.h ---- a/js/src/wasm/WasmBCDefs.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/wasm/WasmBCDefs.h Thu Sep 07 19:51:49 2023 -0700 -@@ -48,16 +48,19 @@ - # include "jit/mips64/Assembler-mips64.h" - #endif - #if defined(JS_CODEGEN_LOONG64) - # include "jit/loong64/Assembler-loong64.h" - #endif - #if defined(JS_CODEGEN_RISCV64) - # include "jit/riscv64/Assembler-riscv64.h" - #endif -+#if defined(JS_CODEGEN_PPC64) -+# include "jit/ppc64/Assembler-ppc64.h" -+#endif - #include "js/ScalarType.h" - #include "util/Memory.h" - #include "wasm/WasmCodegenTypes.h" - #include "wasm/WasmDebugFrame.h" - #include "wasm/WasmGC.h" - #include "wasm/WasmGcObject.h" - #include "wasm/WasmGenerator.h" - #include "wasm/WasmInstance.h" -@@ -168,16 +171,20 @@ - #endif - - #ifdef JS_CODEGEN_ARM - # define RABALDR_INT_DIV_I64_CALLOUT - # define RABALDR_I64_TO_FLOAT_CALLOUT - # define RABALDR_FLOAT_TO_I64_CALLOUT - #endif - -+#ifdef JS_CODEGEN_PPC64 -+# define RABALDR_HAS_HEAPREG -+#endif -+ - #ifdef JS_CODEGEN_MIPS64 - # define RABALDR_PIN_INSTANCE - #endif - - #ifdef JS_CODEGEN_LOONG64 - # define RABALDR_PIN_INSTANCE - #endif - -diff -r cd548157ebca -r 99b51ba09f3f js/src/wasm/WasmBCMemory.cpp ---- a/js/src/wasm/WasmBCMemory.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/wasm/WasmBCMemory.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -498,17 +498,18 @@ - if (dest.tag == AnyReg::I64) { - MOZ_ASSERT(dest.i64() == specific_.abiReturnRegI64); - masm.wasmLoadI64(*access, srcAddr, dest.i64()); - } else { - // For 8 bit loads, this will generate movsbl or movzbl, so - // there's no constraint on what the output register may be. - masm.wasmLoad(*access, srcAddr, dest.any()); - } --#elif defined(JS_CODEGEN_MIPS64) -+#elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_PPC64) -+// XXX: we don't really need this anymore - if (IsUnaligned(*access)) { - switch (dest.tag) { - case AnyReg::I64: - masm.wasmUnalignedLoadI64(*access, HeapReg, ptr, ptr, dest.i64(), temp); - break; - case AnyReg::F32: - masm.wasmUnalignedLoadFP(*access, HeapReg, ptr, ptr, dest.f32(), temp); - break; -@@ -575,17 +576,17 @@ - return executeLoad(access, check, instance, RegI32(ptr.low), dest, - maybeFromI64(temp)); - # elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64) - // On x64 and arm64 the 32-bit code simply assumes that the high bits of the - // 64-bit pointer register are zero and performs a 64-bit add. Thus the code - // generated is the same for the 64-bit and the 32-bit case. - return executeLoad(access, check, instance, RegI32(ptr.reg), dest, - maybeFromI64(temp)); --# elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) -+# elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_PPC64) - // On mips64 and loongarch64, the 'prepareMemoryAccess' function will make - // sure that ptr holds a valid 64-bit index value. Thus the code generated in - // 'executeLoad' is the same for the 64-bit and the 32-bit case. - return executeLoad(access, check, instance, RegI32(ptr.reg), dest, - maybeFromI64(temp)); - # else - MOZ_CRASH("Missing platform hook"); - # endif -@@ -631,17 +632,18 @@ - MOZ_ASSERT(temp.isInvalid()); - if (access->type() == Scalar::Int64) { - masm.wasmStoreI64(*access, src.i64(), HeapReg, ptr, ptr); - } else if (src.tag == AnyReg::I64) { - masm.wasmStore(*access, AnyRegister(src.i64().low), HeapReg, ptr, ptr); - } else { - masm.wasmStore(*access, src.any(), HeapReg, ptr, ptr); - } --#elif defined(JS_CODEGEN_MIPS64) -+#elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_PPC64) -+// XXX: we don't really need this anymore - if (IsUnaligned(*access)) { - switch (src.tag) { - case AnyReg::I64: - masm.wasmUnalignedStoreI64(*access, src.i64(), HeapReg, ptr, ptr, temp); - break; - case AnyReg::F32: - masm.wasmUnalignedStoreFP(*access, src.f32(), HeapReg, ptr, ptr, temp); - break; -@@ -692,17 +694,18 @@ - void BaseCompiler::store(MemoryAccessDesc* access, AccessCheck* check, - RegPtr instance, RegI64 ptr, AnyReg src, RegI64 temp) { - prepareMemoryAccess(access, check, instance, ptr); - // See comments in load() - # if !defined(JS_64BIT) - return executeStore(access, check, instance, RegI32(ptr.low), src, - maybeFromI64(temp)); - # elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64) || \ -- defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) -+ defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || \ -+ defined(JS_CODEGEN_PPC64) - return executeStore(access, check, instance, RegI32(ptr.reg), src, - maybeFromI64(temp)); - # else - MOZ_CRASH("Missing platform hook"); - # endif - } - #endif - -@@ -1180,17 +1183,17 @@ - bc->masm.wasmAtomicFetchOp(access, op, rv, srcAddr, temps.t0, rd); - } - - static void Deallocate(BaseCompiler* bc, RegI32 rv, const Temps& temps) { - bc->freeI32(rv); - bc->freeI32(temps.t0); - } - --#elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) -+#elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_PPC64) - - struct Temps { - RegI32 t0, t1, t2; - }; - - static void PopAndAllocate(BaseCompiler* bc, ValType type, - Scalar::Type viewType, AtomicOp op, RegI32* rd, - RegI32* rv, Temps* temps) { -@@ -1371,17 +1374,17 @@ - } - - static void Deallocate(BaseCompiler* bc, AtomicOp op, RegI64 rv, RegI64 temp) { - bc->freeI64(rv); - bc->freeI64(temp); - } - - #elif defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS64) || \ -- defined(JS_CODEGEN_LOONG64) -+ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_PPC64) - - static void PopAndAllocate(BaseCompiler* bc, AtomicOp op, RegI64* rd, - RegI64* rv, RegI64* temp) { - *rv = bc->popI64(); - *temp = bc->needI64(); - *rd = bc->needI64(); - } - -@@ -1548,17 +1551,17 @@ - BaseIndex srcAddr, RegI32 rv, RegI32 rd, const Temps&) { - bc->masm.wasmAtomicExchange(access, srcAddr, rv, rd); - } - - static void Deallocate(BaseCompiler* bc, RegI32 rv, const Temps&) { - bc->freeI32(rv); - } - --#elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) -+#elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_PPC64) - - struct Temps { - RegI32 t0, t1, t2; - }; - - static void PopAndAllocate(BaseCompiler* bc, ValType type, - Scalar::Type viewType, RegI32* rd, RegI32* rv, - Temps* temps) { -@@ -1696,17 +1699,17 @@ - - static void Deallocate(BaseCompiler* bc, RegI64 rd, RegI64 rv) { - MOZ_ASSERT(rd == bc->specific_.edx_eax || rd == RegI64::Invalid()); - bc->maybeFree(rd); - bc->freeI32(bc->specific_.ecx); - } - - #elif defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS64) || \ -- defined(JS_CODEGEN_LOONG64) -+ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_PPC64) - - static void PopAndAllocate(BaseCompiler* bc, RegI64* rd, RegI64* rv) { - *rv = bc->popI64(); - *rd = bc->needI64(); - } - - static void Deallocate(BaseCompiler* bc, RegI64 rd, RegI64 rv) { - bc->freeI64(rv); -@@ -1875,17 +1878,17 @@ - } - - static void Deallocate(BaseCompiler* bc, RegI32 rexpect, RegI32 rnew, - const Temps&) { - bc->freeI32(rnew); - bc->freeI32(rexpect); - } - --#elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) -+#elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_PPC64) - - struct Temps { - RegI32 t0, t1, t2; - }; - - static void PopAndAllocate(BaseCompiler* bc, ValType type, - Scalar::Type viewType, RegI32* rexpect, RegI32* rnew, - RegI32* rd, Temps* temps) { -@@ -2123,17 +2126,17 @@ - - template - static void Deallocate(BaseCompiler* bc, RegI64 rexpect, RegI64 rnew) { - bc->freeI64(rexpect); - bc->freeI64(rnew); - } - - #elif defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS64) || \ -- defined(JS_CODEGEN_LOONG64) -+ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_PPC64) - - template - static void PopAndAllocate(BaseCompiler* bc, RegI64* rexpect, RegI64* rnew, - RegI64* rd) { - *rnew = bc->popI64(); - *rexpect = bc->popI64(); - *rd = bc->needI64(); - } -diff -r cd548157ebca -r 99b51ba09f3f js/src/wasm/WasmBCRegDefs.h ---- a/js/src/wasm/WasmBCRegDefs.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/wasm/WasmBCRegDefs.h Thu Sep 07 19:51:49 2023 -0700 -@@ -110,16 +110,23 @@ - // the regular scratch register(s) pretty liberally. We could - // work around that in several cases but the mess does not seem - // worth it yet. CallTempReg2 seems safe. - - # define RABALDR_SCRATCH_I32 - static constexpr Register RabaldrScratchI32 = CallTempReg2; - #endif - -+#ifdef JS_CODEGEN_PPC64 -+# define RABALDR_SCRATCH_I32 -+// We can use all the argregs up, and we don't want the JIT using our own -+// private scratch registers, so this is the best option of what's left. -+static constexpr Register RabaldrScratchI32 = r19; -+#endif -+ - #ifdef RABALDR_SCRATCH_F32_ALIASES_F64 - # if !defined(RABALDR_SCRATCH_F32) || !defined(RABALDR_SCRATCH_F64) - # error "Bad configuration" - # endif - #endif - - ////////////////////////////////////////////////////////////////////////////// - // -@@ -384,16 +391,21 @@ - SpecificRegs() : abiReturnRegI64(ReturnReg64) {} - }; - #elif defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS64) || \ - defined(JS_CODEGEN_LOONG64) - struct SpecificRegs { - // Required by gcc. - SpecificRegs() {} - }; -+#elif defined(JS_CODEGEN_PPC64) -+struct SpecificRegs { -+ // Because gcc. -+ SpecificRegs() {} -+}; - #else - struct SpecificRegs { - # ifndef JS_64BIT - RegI64 abiReturnRegI64; - # endif - - SpecificRegs() { MOZ_CRASH("BaseCompiler porting interface: SpecificRegs"); } - }; -diff -r cd548157ebca -r 99b51ba09f3f js/src/wasm/WasmBaselineCompile.cpp ---- a/js/src/wasm/WasmBaselineCompile.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/wasm/WasmBaselineCompile.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -270,17 +270,17 @@ - // Compute the absolute table base pointer into `scratch`, offset by 8 - // to account for the fact that ma_mov read PC+8. - masm.ma_sub(Imm32(offset + 8), scratch, arm_scratch); - - // Jump indirect via table element. - masm.ma_ldr(DTRAddr(scratch, DtrRegImmShift(switchValue, LSL, 2)), pc, Offset, - Assembler::Always); - #elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || \ -- defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_RISCV64) || defined(JS_CODEGEN_PPC64) - ScratchI32 scratch(*this); - CodeLabel tableCl; - - masm.ma_li(scratch, &tableCl); - - tableCl.target()->bind(theTable->offset()); - masm.addCodeLabel(tableCl); - -@@ -697,16 +697,27 @@ - Label L; - masm.loadPtr(Address(InstanceReg, Instance::offsetOfDebugTrapHandler()), - scratch); - masm.branchPtr(Assembler::Equal, scratch, ImmWord(0), &L); - masm.call(&debugTrapStub_); - masm.append(CallSiteDesc(iter_.lastOpcodeOffset(), kind), - CodeOffset(masm.currentOffset())); - masm.bind(&L); -+#elif defined(JS_CODEGEN_PPC64) -+ Label L; -+ masm.loadPtr(Address(InstanceReg, Instance::offsetOfDebugTrapHandler()), -+ ScratchRegister); -+ // TODO: Ideally this should be a bcl, but we have to note the call site. -+ masm.ma_bc(ScratchRegister, ScratchRegister, &L, -+ Assembler::Zero, Assembler::ShortJump); -+ masm.ma_bl(&debugTrapStub_, Assembler::ShortJump); -+ masm.append(CallSiteDesc(iter_.lastOpcodeOffset(), kind), -+ CodeOffset(masm.currentOffset())); -+ masm.bind(&L); - #else - MOZ_CRASH("BaseCompiler platform hook: insertBreakablePoint"); - #endif - } - - void BaseCompiler::insertBreakpointStub() { - // The debug trap stub performs out-of-line filtering before jumping to the - // debug trap handler if necessary. The trap handler returns directly to -@@ -763,17 +774,17 @@ - masm.ma_ldr( - DTRAddr(InstanceReg, DtrOffImm(Instance::offsetOfDebugFilter())), tmp1); - masm.ma_mov(Imm32(func_.index / 32), tmp2); - masm.ma_ldr(DTRAddr(tmp1, DtrRegImmShift(tmp2, LSL, 0)), tmp2); - masm.ma_tst(tmp2, Imm32(1 << func_.index % 32), tmp1, Assembler::Always); - masm.ma_bx(lr, Assembler::Zero); - } - #elif defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_MIPS64) || \ -- defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_RISCV64) || defined(JS_CODEGEN_PPC64) - { - ScratchPtr scratch(*this); - - // Logic same as ARM64. - masm.loadPtr(Address(InstanceReg, Instance::offsetOfDebugFilter()), - scratch); - masm.branchTest32(Assembler::NonZero, Address(scratch, func_.index / 32), - Imm32(1 << (func_.index % 32)), &L); -@@ -1407,16 +1418,26 @@ - ABIArg argLoc = call->abi.next(MIRType::Int32); - if (argLoc.kind() == ABIArg::Stack) { - ScratchI32 scratch(*this); - loadI32(arg, scratch); - masm.store32(scratch, Address(masm.getStackPointer(), - argLoc.offsetFromArgBase())); - } else { - loadI32(arg, RegI32(argLoc.gpr())); -+#if JS_CODEGEN_PPC64 -+ // XXX: THIS SHOULD NOT BE NECESSARY! -+ // If this is a call to compiled C++, we must ensure that the -+ // upper 32 bits are clear: addi can sign-extend, which yields -+ // difficult-to-diagnose bugs when the function expects a uint32_t -+ // but the register it gets has a residual 64-bit value. -+ if (call->usesSystemAbi) { -+ masm.as_rldicl(argLoc.gpr(), argLoc.gpr(), 0, 32); -+ } -+#endif - } - break; - } - case ValType::I64: { - ABIArg argLoc = call->abi.next(MIRType::Int64); - if (argLoc.kind() == ABIArg::Stack) { - ScratchI32 scratch(*this); - #ifdef JS_PUNBOX64 -@@ -1755,17 +1776,18 @@ - // popXForY where X says something about types and Y something about the - // operation being targeted. - - RegI32 BaseCompiler::needRotate64Temp() { - #if defined(JS_CODEGEN_X86) - return needI32(); - #elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || \ - defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS64) || \ -- defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) || \ -+ defined(JS_CODEGEN_PPC64) - return RegI32::Invalid(); - #else - MOZ_CRASH("BaseCompiler platform hook: needRotate64Temp"); - #endif - } - - void BaseCompiler::popAndAllocateForDivAndRemI32(RegI32* r0, RegI32* r1, - RegI32* reserved) { -@@ -1812,16 +1834,18 @@ - pop2xI64(r0, r1); - #elif defined(JS_CODEGEN_ARM) - pop2xI64(r0, r1); - *temp = needI32(); - #elif defined(JS_CODEGEN_ARM64) - pop2xI64(r0, r1); - #elif defined(JS_CODEGEN_LOONG64) - pop2xI64(r0, r1); -+#elif defined(JS_CODEGEN_PPC64) -+ pop2xI64(r0, r1); - #else - MOZ_CRASH("BaseCompiler porting interface: popAndAllocateForMulI64"); - #endif - } - - #ifndef RABALDR_INT_DIV_I64_CALLOUT - - void BaseCompiler::popAndAllocateForDivAndRemI64(RegI64* r0, RegI64* r1, -@@ -1833,16 +1857,22 @@ - *r1 = popI64(); - *r0 = popI64ToSpecific(specific_.rax); - *reserved = specific_.rdx; - # elif defined(JS_CODEGEN_ARM64) - pop2xI64(r0, r1); - if (isRemainder) { - *reserved = needI64(); - } -+# elif defined(JS_CODEGEN_PPC64) -+ pop2xI64(r0, r1); -+ if (isRemainder && !js::jit::HasPPCISA3()) { -+ // Need temp register for CPUs that don't have mod* instructions. -+ *reserved = needI64(); -+ } - # else - pop2xI64(r0, r1); - # endif - } - - static void QuotientI64(MacroAssembler& masm, RegI64 rhs, RegI64 srcDest, - RegI64 reserved, IsUnsigned isUnsigned) { - # if defined(JS_CODEGEN_X64) -@@ -1874,16 +1904,23 @@ - masm.Sdiv(sd, sd, r); - } - # elif defined(JS_CODEGEN_LOONG64) - if (isUnsigned) { - masm.as_div_du(srcDest.reg, srcDest.reg, rhs.reg); - } else { - masm.as_div_d(srcDest.reg, srcDest.reg, rhs.reg); - } -+# elif defined(JS_CODEGEN_PPC64) -+ MOZ_ASSERT(reserved.isInvalid()); -+ if (isUnsigned) { -+ masm.as_divdu(srcDest.reg, srcDest.reg, rhs.reg); -+ } else { -+ masm.as_divd(srcDest.reg, srcDest.reg, rhs.reg); -+ } - # else - MOZ_CRASH("BaseCompiler platform hook: quotientI64"); - # endif - } - - static void RemainderI64(MacroAssembler& masm, RegI64 rhs, RegI64 srcDest, - RegI64 reserved, IsUnsigned isUnsigned) { - # if defined(JS_CODEGEN_X64) -@@ -1919,16 +1956,34 @@ - masm.Mul(t, t, r); - masm.Sub(sd, sd, t); - # elif defined(JS_CODEGEN_LOONG64) - if (isUnsigned) { - masm.as_mod_du(srcDest.reg, srcDest.reg, rhs.reg); - } else { - masm.as_mod_d(srcDest.reg, srcDest.reg, rhs.reg); - } -+# elif defined(JS_CODEGEN_PPC64) -+ if (js::jit::HasPPCISA3()) { -+ MOZ_ASSERT(reserved.isInvalid()); -+ if (isUnsigned) { -+ masm.as_modud(srcDest.reg, srcDest.reg, rhs.reg); -+ } else { -+ masm.as_modsd(srcDest.reg, srcDest.reg, rhs.reg); -+ } -+ } else { -+ MOZ_ASSERT(!reserved.isInvalid()); -+ if (isUnsigned) { -+ masm.as_divdu(reserved.reg, srcDest.reg, rhs.reg); -+ } else { -+ masm.as_divd(reserved.reg, srcDest.reg, rhs.reg); -+ } -+ masm.as_mulld(reserved.reg, reserved.reg, rhs.reg); -+ masm.as_subf(srcDest.reg, reserved.reg, srcDest.reg); // T = B - A -+ } - # else - MOZ_CRASH("BaseCompiler platform hook: remainderI64"); - # endif - } - - #endif // RABALDR_INT_DIV_I64_CALLOUT - - RegI32 BaseCompiler::popI32RhsForShift() { -@@ -2286,16 +2341,18 @@ - - // Currently common to PopcntI32 and PopcntI64 - static RegI32 PopcntTemp(BaseCompiler& bc) { - #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) - return AssemblerX86Shared::HasPOPCNT() ? RegI32::Invalid() : bc.needI32(); - #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ - defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) - return bc.needI32(); -+#elif defined(JS_CODEGEN_PPC64) -+ return RegI32::Invalid(); - #else - MOZ_CRASH("BaseCompiler platform hook: PopcntTemp"); - #endif - } - - static void PopcntI32(BaseCompiler& bc, RegI32 rsd, RegI32 temp) { - bc.masm.popcnt32(rsd, rsd, temp); - } -@@ -10923,17 +10980,19 @@ - // they are definitely implemented on the Cortex-A7 and Cortex-A15 - // and on all ARMv8 systems. - if (!HasIDIV()) { - return false; - } - #endif - #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || \ - defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ -- defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) -+ defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || \ -+ defined(JS_CODEGEN_PPC64) -+ // PPC64 gates on other prerequisites internal to its code generator. - return true; - #else - return false; - #endif - } - - bool js::wasm::BaselineCompileFunctions(const ModuleEnvironment& moduleEnv, - const CompilerEnvironment& compilerEnv, -diff -r cd548157ebca -r 99b51ba09f3f js/src/wasm/WasmBuiltins.cpp ---- a/js/src/wasm/WasmBuiltins.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/wasm/WasmBuiltins.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -600,16 +600,20 @@ - - MOZ_ASSERT(iter.instance() == iter.instance()); - iter.instance()->setPendingException(ref); - - rfe->kind = ExceptionResumeKind::WasmCatch; - rfe->framePointer = (uint8_t*)iter.frame(); - rfe->instance = iter.instance(); - -+#if defined(JS_CODEGEN_PPC64) -+ // Our Frame must also account for the linkage area. -+ offsetAdjustment += 4 * sizeof(uintptr_t); -+#endif - rfe->stackPointer = - (uint8_t*)(rfe->framePointer - tryNote->landingPadFramePushed()); - rfe->target = - iter.instance()->codeBase(tier) + tryNote->landingPadEntryPoint(); - - // Make sure to clear trapping state if we got here due to a trap. - if (activation->isWasmTrapping()) { - activation->finishWasmTrap(); -diff -r cd548157ebca -r 99b51ba09f3f js/src/wasm/WasmCompile.cpp ---- a/js/src/wasm/WasmCompile.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/wasm/WasmCompile.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -47,16 +47,17 @@ - X86 = 0x1, - X64 = 0x2, - ARM = 0x3, - MIPS = 0x4, - MIPS64 = 0x5, - ARM64 = 0x6, - LOONG64 = 0x7, - RISCV64 = 0x8, -+ PPC64 = 0x9, - ARCH_BITS = 3 - }; - - #if defined(JS_CODEGEN_X86) - MOZ_ASSERT(uint32_t(jit::CPUInfo::GetFingerprint()) <= - (UINT32_MAX >> ARCH_BITS)); - return X86 | (uint32_t(jit::CPUInfo::GetFingerprint()) << ARCH_BITS); - #elif defined(JS_CODEGEN_X64) -@@ -73,16 +74,19 @@ - MOZ_ASSERT(jit::GetMIPSFlags() <= (UINT32_MAX >> ARCH_BITS)); - return MIPS64 | (jit::GetMIPSFlags() << ARCH_BITS); - #elif defined(JS_CODEGEN_LOONG64) - MOZ_ASSERT(jit::GetLOONG64Flags() <= (UINT32_MAX >> ARCH_BITS)); - return LOONG64 | (jit::GetLOONG64Flags() << ARCH_BITS); - #elif defined(JS_CODEGEN_RISCV64) - MOZ_ASSERT(jit::GetRISCV64Flags() <= (UINT32_MAX >> ARCH_BITS)); - return RISCV64 | (jit::GetRISCV64Flags() << ARCH_BITS); -+#elif defined(JS_CODEGEN_PPC64) -+ MOZ_ASSERT(jit::GetPPC64Flags() <= (UINT32_MAX >> ARCH_BITS)); -+ return PPC64 | (jit::GetPPC64Flags() << ARCH_BITS); - #elif defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_WASM32) - return 0; - #else - # error "unknown architecture" - #endif - } - - FeatureArgs FeatureArgs::build(JSContext* cx, const FeatureOptions& options) { -diff -r cd548157ebca -r 99b51ba09f3f js/src/wasm/WasmDebugFrame.cpp ---- a/js/src/wasm/WasmDebugFrame.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/wasm/WasmDebugFrame.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -33,16 +33,20 @@ - using namespace js::wasm; - - /* static */ - DebugFrame* DebugFrame::from(Frame* fp) { - MOZ_ASSERT(GetNearestEffectiveInstance(fp)->code().metadata().debugEnabled); - auto* df = - reinterpret_cast((uint8_t*)fp - DebugFrame::offsetOfFrame()); - MOZ_ASSERT(GetNearestEffectiveInstance(fp) == df->instance()); -+#if defined(JS_CODEGEN_PPC64) -+ // Our Frame has a linkage area in it which must be accounted for. -+ offsetAdjustment += 4 * sizeof(uintptr_t); -+#endif - return df; - } - - void DebugFrame::alignmentStaticAsserts() { - // VS2017 doesn't consider offsetOfFrame() to be a constexpr, so we have - // to use offsetof directly. These asserts can't be at class-level - // because the type is incomplete. - -diff -r cd548157ebca -r 99b51ba09f3f js/src/wasm/WasmFrame.h ---- a/js/src/wasm/WasmFrame.h Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/wasm/WasmFrame.h Thu Sep 07 19:51:49 2023 -0700 -@@ -283,16 +283,25 @@ - // before the function has made its stack reservation, the stack alignment is - // sizeof(Frame) % WasmStackAlignment. - // - // During MacroAssembler code generation, the bytes pushed after the wasm::Frame - // are counted by masm.framePushed. Thus, the stack alignment at any point in - // time is (sizeof(wasm::Frame) + masm.framePushed) % WasmStackAlignment. - - class Frame { -+#if defined(JS_CODEGEN_PPC64) -+ // Since Wasm can call directly to ABI-compliant routines, the Frame must -+ // have an ABI-compliant linkage area. We allocate four doublewords, the -+ // minimum size. -+ void *_ppc_sp_; -+ void *_ppc_cr_; -+ void *_ppc_lr_; -+ void *_ppc_toc_; -+#endif - // See GenerateCallableEpilogue for why this must be - // the first field of wasm::Frame (in a downward-growing stack). - // It's either the caller's Frame*, for wasm callers, or the JIT caller frame - // plus a tag otherwise. - uint8_t* callerFP_; - - // The return address pushed by the call (in the case of ARM/MIPS the return - // address is pushed by the first instruction of the prologue). -@@ -338,18 +347,21 @@ - static uint8_t* addExitFPTag(const Frame* fp) { - MOZ_ASSERT(!isExitFP(fp)); - return reinterpret_cast(reinterpret_cast(fp) | - ExitFPTag); - } - }; - - static_assert(!std::is_polymorphic_v, "Frame doesn't need a vtable."); -+#if !defined(JS_CODEGEN_PPC64) -+// PowerPC requires a linkage area, so this assert doesn't hold on that arch. - static_assert(sizeof(Frame) == 2 * sizeof(void*), - "Frame is a two pointer structure"); -+#endif - - // Note that sizeof(FrameWithInstances) does not account for ShadowStackSpace. - // Use FrameWithInstances::sizeOf() if you are not incorporating - // ShadowStackSpace through other means (eg the ABIArgIter). - - class FrameWithInstances : public Frame { - // `ShadowStackSpace` bytes will be allocated here on Win64, at higher - // addresses than Frame and at lower addresses than the instance fields. -diff -r cd548157ebca -r 99b51ba09f3f js/src/wasm/WasmFrameIter.cpp ---- a/js/src/wasm/WasmFrameIter.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/wasm/WasmFrameIter.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -405,16 +405,22 @@ - static const unsigned PoppedFP = 4; - static const unsigned PoppedFPJitEntry = 0; - #elif defined(JS_CODEGEN_RISCV64) - static const unsigned PushedRetAddr = 8; - static const unsigned PushedFP = 16; - static const unsigned SetFP = 20; - static const unsigned PoppedFP = 4; - static const unsigned PoppedFPJitEntry = 0; -+#elif defined(JS_CODEGEN_PPC64) -+static const unsigned PushedRetAddr = 12; -+static const unsigned PushedFP = 16; -+static const unsigned SetFP = 20; -+static const unsigned PoppedFP = 8; -+static const unsigned PoppedFPJitEntry = 0; - #elif defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_WASM32) - // Synthetic values to satisfy asserts and avoid compiler warnings. - static const unsigned PushedRetAddr = 0; - static const unsigned PushedFP = 1; - static const unsigned SetFP = 2; - static const unsigned PoppedFP = 3; - static const unsigned PoppedFPJitEntry = 4; - #else -@@ -517,16 +523,38 @@ - MemOperand(sp, Frame::callerFPOffset())); - MOZ_ASSERT_IF(!masm.oom(), PushedFP == masm.currentOffset() - *entry); - masm.Mov(ARMRegister(FramePointer, 64), sp); - MOZ_ASSERT_IF(!masm.oom(), SetFP == masm.currentOffset() - *entry); - - // And restore the SP-reg setting, per comment above. - masm.SetStackPointer64(stashedSPreg); - } -+#elif defined(JS_CODEGEN_PPC64) -+ { -+ *entry = masm.currentOffset(); -+ -+ // These must be in this precise order. Fortunately we can subsume the -+ // SPR load into the initial "verse" since it is treated atomically. -+ // The linkage area required for ABI compliance is baked into the Frame. -+ masm.xs_mflr(ScratchRegister); -+ masm.as_addi(StackPointer, StackPointer, -(sizeof(Frame))); -+ masm.as_std(ScratchRegister, StackPointer, Frame::returnAddressOffset()); -+ MOZ_ASSERT_IF(!masm.oom(), PushedRetAddr == masm.currentOffset() - *entry); -+ masm.as_std(FramePointer, StackPointer, Frame::callerFPOffset()); -+ MOZ_ASSERT_IF(!masm.oom(), PushedFP == masm.currentOffset() - *entry); -+ masm.xs_mr(FramePointer, StackPointer); -+ MOZ_ASSERT_IF(!masm.oom(), SetFP == masm.currentOffset() - *entry); -+ -+ // Burn nops because we have to make this a multiple of 16 and the mfspr -+ // just screwed us. -+ masm.as_nop(); // 24 -+ masm.as_nop(); // 28 -+ masm.as_nop(); // 32 // trap point -+ } - #else - { - # if defined(JS_CODEGEN_ARM) - AutoForbidPoolsAndNops afp(&masm, - /* number of instructions in scope = */ 3); - - *entry = masm.currentOffset(); - -@@ -615,16 +643,28 @@ - // use it. Hence we have to do it "by hand". - masm.Mov(PseudoStackPointer64, vixl::sp); - - masm.Ret(ARMRegister(lr, 64)); - - // See comment at equivalent place in |GenerateCallablePrologue| above. - masm.SetStackPointer64(stashedSPreg); - -+#elif defined(JS_CODEGEN_PPC64) -+ -+ masm.as_ld(FramePointer, StackPointer, Frame::callerFPOffset()); -+ poppedFP = masm.currentOffset(); -+ // This is suboptimal since we get serialized, but has to be in this order. -+ masm.as_ld(ScratchRegister, StackPointer, Frame::returnAddressOffset()); -+ masm.xs_mtlr(ScratchRegister); -+ *ret = masm.currentOffset(); -+ -+ masm.as_addi(StackPointer, StackPointer, sizeof(Frame)); -+ masm.as_blr(); -+ - #else - // Forbid pools for the same reason as described in GenerateCallablePrologue. - # if defined(JS_CODEGEN_ARM) - AutoForbidPoolsAndNops afp(&masm, /* number of instructions in scope = */ 6); - # endif - - // There is an important ordering constraint here: fp must be repointed to - // the caller's frame before any field of the frame currently pointed to by -@@ -863,16 +903,23 @@ - AutoForbidPoolsAndNops afp(&masm, - /* number of instructions in scope = */ 4); - offsets->begin = masm.currentOffset(); - static_assert(BeforePushRetAddr == 0); - // Subtract from SP first as SP must be aligned before offsetting. - masm.Sub(sp, sp, 16); - static_assert(JitFrameLayout::offsetOfReturnAddress() == 8); - masm.Str(ARMRegister(lr, 64), MemOperand(sp, 8)); -+#elif defined(JS_CODEGEN_PPC64) -+ offsets->begin = masm.currentOffset(); -+ -+ // We have to burn a nop here to match the other prologue length. -+ masm.xs_mflr(ScratchRegister); -+ masm.as_nop(); // might as well explicitly wait for the mfspr to complete -+ masm.as_stdu(ScratchRegister, StackPointer, -8); - #else - // The x86/x64 call instruction pushes the return address. - offsets->begin = masm.currentOffset(); - #endif - MOZ_ASSERT_IF(!masm.oom(), - PushedRetAddr == masm.currentOffset() - offsets->begin); - // Save jit frame pointer, so unwinding from wasm to jit frames is trivial. - #if defined(JS_CODEGEN_ARM64) -diff -r cd548157ebca -r 99b51ba09f3f js/src/wasm/WasmGC.cpp ---- a/js/src/wasm/WasmGC.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/wasm/WasmGC.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -302,13 +302,29 @@ - # elif defined(JS_CODEGEN_LOONG64) - // TODO(loong64): Implement IsValidStackMapKey. - return true; - # elif defined(JS_CODEGEN_RISCV64) - const uint32_t* insn = (const uint32_t*)nextPC; - return (((uintptr_t(insn) & 3) == 0) && - (insn[-1] == 0x00006037 && insn[-2] == 0x00100073) || // break; - ((insn[-1] & kBaseOpcodeMask) == JALR)); -+# elif defined(JS_CODEGEN_PPC64) -+ js::jit::Instruction* inst = (js::jit::Instruction*)nextPC; -+ //fprintf(stderr, "IsValidStackMapKey: 0x%lx 0x%08x 0x%08x\n", (uint64_t)nextPC, inst[0].encode(), inst[0].extractOpcode()); -+ return (((uintptr_t(nextPC) & 3) == 0) && ( -+ inst[0].extractOpcode() == 0xf0000000 || // VSX (xxlxor etc) -+ inst[0].extractOpcode() == js::jit::PPC_addi || // stack allocate -+ inst[0].extractOpcode() == js::jit::PPC_addis || // load immediate -+ inst[0].extractOpcode() == js::jit::PPC_cmpwi || // test after bl -+ inst[0].extractOpcode() == js::jit::PPC_cmpw || // (extsw, same) -+ inst[0].extractOpcode() == js::jit::PPC_lfd || // load FPR -+ inst[0].extractOpcode() == js::jit::PPC_lfs || // load FPR -+ inst[0].extractOpcode() == js::jit::PPC_lwz || // load GPR -+ inst[0].extractOpcode() == js::jit::PPC_ld || // load GPR -+ inst[0].extractOpcode() == js::jit::PPC_b || // branch -+ inst[0].encode() == js::jit::PPC_nop || // GET BACK TO WORK -+ inst[0].encode() == js::jit::PPC_stop)); // designated throw - # else - MOZ_CRASH("IsValidStackMapKey: requires implementation on this platform"); - # endif - } - #endif -diff -r cd548157ebca -r 99b51ba09f3f js/src/wasm/WasmSignalHandlers.cpp ---- a/js/src/wasm/WasmSignalHandlers.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/wasm/WasmSignalHandlers.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -104,17 +104,19 @@ - # endif - # if defined(__mips__) - # define EPC_sig(p) ((p)->sc_pc) - # define RFP_sig(p) ((p)->sc_regs[30]) - # endif - # if defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \ - defined(__PPC64LE__) - # define R01_sig(p) ((p)->sc_frame.fixreg[1]) -+# define R31_sig(p) ((p)->sc_frame.fixreg[31]) - # define R32_sig(p) ((p)->sc_frame.srr0) -+# define R36_sig(p) ((p)->sc_frame.lr) - # endif - # elif defined(__linux__) || defined(__sun) - # if defined(__linux__) - # define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_EIP]) - # define EBP_sig(p) ((p)->uc_mcontext.gregs[REG_EBP]) - # define ESP_sig(p) ((p)->uc_mcontext.gregs[REG_ESP]) - # else - # define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_PC]) -@@ -150,17 +152,19 @@ - # if defined(__linux__) && (defined(__sparc__) && defined(__arch64__)) - # define PC_sig(p) ((p)->uc_mcontext.mc_gregs[MC_PC]) - # define FP_sig(p) ((p)->uc_mcontext.mc_fp) - # define SP_sig(p) ((p)->uc_mcontext.mc_i7) - # endif - # if defined(__linux__) && (defined(__ppc64__) || defined(__PPC64__) || \ - defined(__ppc64le__) || defined(__PPC64LE__)) - # define R01_sig(p) ((p)->uc_mcontext.gp_regs[1]) -+# define R31_sig(p) ((p)->uc_mcontext.gp_regs[31]) - # define R32_sig(p) ((p)->uc_mcontext.gp_regs[32]) -+# define R36_sig(p) ((p)->uc_mcontext.gp_regs[36]) - # endif - # if defined(__linux__) && defined(__loongarch__) - # define EPC_sig(p) ((p)->uc_mcontext.__pc) - # define RRA_sig(p) ((p)->uc_mcontext.__gregs[1]) - # define R03_sig(p) ((p)->uc_mcontext.__gregs[3]) - # define RFP_sig(p) ((p)->uc_mcontext.__gregs[22]) - # endif - # if defined(__linux__) && defined(__riscv) -@@ -193,17 +197,19 @@ - # endif - # if defined(__mips__) - # define EPC_sig(p) ((p)->uc_mcontext.__gregs[_REG_EPC]) - # define RFP_sig(p) ((p)->uc_mcontext.__gregs[_REG_S8]) - # endif - # if defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \ - defined(__PPC64LE__) - # define R01_sig(p) ((p)->uc_mcontext.__gregs[_REG_R1]) -+# define R31_sig(p) ((p)->uc_mcontext.__gregs[_REG_R31]) - # define R32_sig(p) ((p)->uc_mcontext.__gregs[_REG_PC]) -+# define R36_sig(p) ((p)->uc_mcontext.__gregs[_REG_LR]) - # endif - # elif defined(__DragonFly__) || defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) - # define EIP_sig(p) ((p)->uc_mcontext.mc_eip) - # define EBP_sig(p) ((p)->uc_mcontext.mc_ebp) - # define ESP_sig(p) ((p)->uc_mcontext.mc_esp) - # define RIP_sig(p) ((p)->uc_mcontext.mc_rip) - # define RSP_sig(p) ((p)->uc_mcontext.mc_rsp) -@@ -227,17 +233,19 @@ - # endif - # if defined(__FreeBSD__) && defined(__mips__) - # define EPC_sig(p) ((p)->uc_mcontext.mc_pc) - # define RFP_sig(p) ((p)->uc_mcontext.mc_regs[30]) - # endif - # if defined(__FreeBSD__) && (defined(__ppc64__) || defined(__PPC64__) || \ - defined(__ppc64le__) || defined(__PPC64LE__)) - # define R01_sig(p) ((p)->uc_mcontext.mc_gpr[1]) -+# define R31_sig(p) ((p)->uc_mcontext.mc_gpr[31]) - # define R32_sig(p) ((p)->uc_mcontext.mc_srr0) -+# define R36_sig(p) ((p)->uc_mcontext.mc_lr) - # endif - # elif defined(XP_DARWIN) - # define EIP_sig(p) ((p)->thread.uts.ts32.__eip) - # define EBP_sig(p) ((p)->thread.uts.ts32.__ebp) - # define ESP_sig(p) ((p)->thread.uts.ts32.__esp) - # define RIP_sig(p) ((p)->thread.__rip) - # define RBP_sig(p) ((p)->thread.__rbp) - # define RSP_sig(p) ((p)->thread.__rsp) -@@ -405,17 +413,19 @@ - # define PC_sig(p) EPC_sig(p) - # define FP_sig(p) RFP_sig(p) - # define SP_sig(p) RSP_sig(p) - # define LR_sig(p) R31_sig(p) - # elif defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \ - defined(__PPC64LE__) - # define PC_sig(p) R32_sig(p) - # define SP_sig(p) R01_sig(p) --# define FP_sig(p) R01_sig(p) -+ // There is no official frame pointer in the ABI, so we use r31. -+# define FP_sig(p) R31_sig(p) -+# define LR_sig(p) R36_sig(p) - # elif defined(__loongarch__) - # define PC_sig(p) EPC_sig(p) - # define FP_sig(p) RFP_sig(p) - # define SP_sig(p) R03_sig(p) - # define LR_sig(p) RRA_sig(p) - # elif defined(__riscv) - # define PC_sig(p) RPC_sig(p) - # define FP_sig(p) RFP_sig(p) -@@ -451,34 +461,36 @@ - # ifdef SP_sig - return reinterpret_cast(SP_sig(context)); - # else - MOZ_CRASH(); - # endif - } - - # if defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ -- defined(__loongarch__) || defined(__riscv) -+ defined(__loongarch__) || defined(__riscv) || defined(__ppc64__) || \ -+ defined(__PPC64__) - static uint8_t* ContextToLR(CONTEXT* context) { - # ifdef LR_sig - return reinterpret_cast(LR_sig(context)); - # else - MOZ_CRASH(); - # endif - } - # endif - - static JS::ProfilingFrameIterator::RegisterState ToRegisterState( - CONTEXT* context) { - JS::ProfilingFrameIterator::RegisterState state; - state.fp = ContextToFP(context); - state.pc = ContextToPC(context); - state.sp = ContextToSP(context); - # if defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ -- defined(__loongarch__) || defined(__riscv) -+ defined(__loongarch__) || defined(__riscv) || defined(__ppc64__) || \ -+ defined(__PPC64__) - state.lr = ContextToLR(context); - # else - state.lr = (void*)UINTPTR_MAX; - # endif - return state; - } - - // ============================================================================= -diff -r cd548157ebca -r 99b51ba09f3f js/src/wasm/WasmStubs.cpp ---- a/js/src/wasm/WasmStubs.cpp Thu Aug 10 15:36:00 2023 +0000 -+++ b/js/src/wasm/WasmStubs.cpp Thu Sep 07 19:51:49 2023 -0700 -@@ -725,17 +725,18 @@ - AssertExpectedSP(masm); - masm.haltingAlign(CodeAlignment); - - offsets->begin = masm.currentOffset(); - - // Save the return address if it wasn't already saved by the call insn. - #ifdef JS_USE_LINK_REGISTER - # if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS64) || \ -- defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) || \ -+ defined(JS_CODEGEN_PPC64) - masm.pushReturnAddress(); - # elif defined(JS_CODEGEN_ARM64) - // WasmPush updates framePushed() unlike pushReturnAddress(), but that's - // cancelled by the setFramePushed() below. - WasmPush(masm, lr); - # else - MOZ_CRASH("Implement this"); - # endif -@@ -2133,17 +2134,26 @@ - masm.storePtr(scratch, - Address(masm.getStackPointer(), i->offsetFromArgBase())); - } - i++; - MOZ_ASSERT(i.done()); - - // Make the call, test whether it succeeded, and extract the return value. - AssertStackAlignment(masm, ABIStackAlignment); -+#ifdef JS_CODEGEN_PPC64 -+ // Because this is calling an ABI-compliant function, we have to pull down -+ // a dummy linkage area or the values on the stack will be stomped on. The -+ // minimum size is sufficient. -+ masm.as_addi(masm.getStackPointer(), masm.getStackPointer(), -32); -+#endif - masm.call(SymbolicAddress::CallImport_General); -+#ifdef JS_CODEGEN_PPC64 -+ masm.as_addi(masm.getStackPointer(), masm.getStackPointer(), 32); -+#endif - masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); - - ResultType resultType = ResultType::Vector(funcType.results()); - ValType registerResultType; - for (ABIResultIter iter(resultType); !iter.done(); iter.next()) { - if (iter.cur().inRegister()) { - MOZ_ASSERT(!registerResultType.isValid()); - registerResultType = iter.cur().type(); -@@ -2197,17 +2207,18 @@ - - GenPrintf(DebugChannel::Import, masm, "\n"); - - // The native ABI preserves the instance, heap and global registers since they - // are non-volatile. - MOZ_ASSERT(NonVolatileRegs.has(InstanceReg)); - #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || \ - defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS64) || \ -- defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) || \ -+ defined(JS_CODEGEN_PPC64) - MOZ_ASSERT(NonVolatileRegs.has(HeapReg)); - #endif - - GenerateExitEpilogue(masm, framePushed, ExitReason::Fixed::ImportInterp, - offsets); - - return FinishOffsets(masm, offsets); - } -@@ -2667,16 +2678,21 @@ - #elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) - // It's correct to use FloatRegisters::AllMask even when SIMD is not enabled; - // PushRegsInMask strips out the high lanes of the XMM registers in this case, - // while the singles will be stripped as they are aliased by the larger doubles. - static const LiveRegisterSet RegsToPreserve( - GeneralRegisterSet(Registers::AllMask & - ~(Registers::SetType(1) << Registers::StackPointer)), - FloatRegisterSet(FloatRegisters::AllMask)); -+#elif defined(JS_CODEGEN_PPC64) -+// Note that this includes no SPRs, since the JIT is unaware of them. -+static const LiveRegisterSet RegsToPreserve( -+ GeneralRegisterSet(Registers::AllMask), -+ FloatRegisterSet(FloatRegisters::AllMask)); - #else - static const LiveRegisterSet RegsToPreserve( - GeneralRegisterSet(0), FloatRegisterSet(FloatRegisters::AllDoubleMask)); - # ifdef ENABLE_WASM_SIMD - # error "no SIMD support" - # endif - #endif - -diff -r cd548157ebca -r 99b51ba09f3f modules/libpref/init/StaticPrefList.yaml ---- a/modules/libpref/init/StaticPrefList.yaml Thu Aug 10 15:36:00 2023 +0000 -+++ b/modules/libpref/init/StaticPrefList.yaml Thu Sep 07 19:51:49 2023 -0700 -@@ -7447,17 +7447,17 @@ - - name: javascript.options.baselinejit - type: bool - value: true - mirror: always # LoadStartupJSPrefs - do_not_use_directly: true - - - name: javascript.options.ion - type: bool -- value: true -+ value: false - mirror: always # LoadStartupJSPrefs - do_not_use_directly: true - - # The irregexp JIT for regex evaluation. - - name: javascript.options.native_regexp - type: bool - value: true - mirror: always # LoadStartupJSPrefs -@@ -7711,17 +7711,17 @@ - value: 6 * 1024 * 1024 - #else - value: 2 * 1024 * 1024 - #endif - mirror: always - - - name: javascript.options.wasm_optimizingjit - type: bool -- value: true -+ value: false - mirror: always - - #if defined(ENABLE_WASM_RELAXED_SIMD) - - name: javascript.options.wasm_relaxed_simd - type: bool - value: @IS_NIGHTLY_BUILD@ - mirror: always - #endif // defined(ENABLE_WASM_RELAXED_SIMD) diff --git a/firefox-esr/power9-jit-744147.diff b/firefox-esr/power9-jit-744147.diff deleted file mode 100644 index abda363726..0000000000 --- a/firefox-esr/power9-jit-744147.diff +++ /dev/null @@ -1,2681 +0,0 @@ -# HG changeset patch -# User Cameron Kaiser -# Date 1694539672 25200 -# Tue Sep 12 10:27:52 2023 -0700 -# Node ID 671b771fd1de061e02f382e0cb20237d0e3a84a8 -# Parent 99b51ba09f3fb402a7c05948f5cb847f7ad21689 -all js109 patches down - -diff -r 99b51ba09f3f -r 671b771fd1de config/check_macroassembler_style.py ---- a/config/check_macroassembler_style.py Thu Sep 07 19:51:49 2023 -0700 -+++ b/config/check_macroassembler_style.py Tue Sep 12 10:27:52 2023 -0700 -@@ -23,20 +23,20 @@ - import difflib - import os - import re - import sys - - architecture_independent = set(["generic"]) - all_unsupported_architectures_names = set(["mips32", "mips64", "mips_shared"]) - all_architecture_names = set( -- ["x86", "x64", "arm", "arm64", "loong64", "riscv64", "wasm32"] -+ ["x86", "x64", "arm", "arm64", "loong64", "ppc64", "riscv64", "wasm32"] - ) - all_shared_architecture_names = set( -- ["x86_shared", "arm", "arm64", "loong64", "riscv64", "wasm32"] -+ ["x86_shared", "arm", "arm64", "loong64", "ppc64", "riscv64", "wasm32"] - ) - - reBeforeArg = "(?<=[(,\s])" - reArgType = "(?P[\w\s:*&<>]+)" - reArgName = "(?P\s\w+)" - reArgDefault = "(?P(?:\s=(?:(?:\s[\w:]+\(\))|[^,)]+))?)" - reAfterArg = "(?=[,)])" - reMatchArg = re.compile(reBeforeArg + reArgType + reArgName + reArgDefault + reAfterArg) -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/FlushICache.h ---- a/js/src/jit/FlushICache.h Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/FlushICache.h Tue Sep 12 10:27:52 2023 -0700 -@@ -38,17 +38,18 @@ - inline void FlushICache(void* code, size_t size) { MOZ_CRASH(); } - - #else - # error "Unknown architecture!" - #endif - - #if (defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)) || \ - (defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)) || \ -- defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) || \ -+ defined(JS_CODEGEN_PPC64) - - inline void FlushExecutionContext() { - // No-op. Execution context is coherent with instruction cache. - } - inline bool CanFlushExecutionContextForAllThreads() { return true; } - inline void FlushExecutionContextForAllThreads() { - // No-op. Execution context is coherent with instruction cache. - } -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/moz.build ---- a/js/src/jit/moz.build Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/moz.build Tue Sep 12 10:27:52 2023 -0700 -@@ -251,23 +251,21 @@ - "riscv64/Trampoline-riscv64.cpp", - ] - if CONFIG["JS_SIMULATOR_RISCV64"]: - UNIFIED_SOURCES += ["riscv64/Simulator-riscv64.cpp"] - elif CONFIG["JS_CODEGEN_PPC64"]: - UNIFIED_SOURCES += [ - "ppc64/Architecture-ppc64.cpp", - "ppc64/Assembler-ppc64.cpp", -- "ppc64/Bailouts-ppc64.cpp", - "ppc64/CodeGenerator-ppc64.cpp", - "ppc64/Lowering-ppc64.cpp", - "ppc64/MacroAssembler-ppc64.cpp", - "ppc64/MoveEmitter-ppc64.cpp", - "ppc64/Trampoline-ppc64.cpp", -- "shared/AtomicOperations-shared-jit.cpp", - ] - elif CONFIG["JS_CODEGEN_WASM32"]: - UNIFIED_SOURCES += [ - "wasm32/CodeGenerator-wasm32.cpp", - "wasm32/MacroAssembler-wasm32.cpp", - "wasm32/Trampoline-wasm32.cpp", - ] - -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/ppc64/Architecture-ppc64.cpp ---- a/js/src/jit/ppc64/Architecture-ppc64.cpp Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/ppc64/Architecture-ppc64.cpp Tue Sep 12 10:27:52 2023 -0700 -@@ -54,20 +54,24 @@ - // no CPU features will be detected. - #endif - - return result; - } - #endif - - void --FlushICache(void* code, size_t size, bool codeIsThreadLocal) { -- intptr_t end = reinterpret_cast(code) + size; -- __builtin___clear_cache(reinterpret_cast(code), -- reinterpret_cast(end)); -+FlushICache(void* code, size_t size) { -+#if defined(__GNUC__) -+ intptr_t end = reinterpret_cast(code) + size; -+ __builtin___clear_cache(reinterpret_cast(code), -+ reinterpret_cast(end)); -+#else -+ _flush_cache(reinterpret_cast(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) - { -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/ppc64/Architecture-ppc64.h ---- a/js/src/jit/ppc64/Architecture-ppc64.h Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/ppc64/Architecture-ppc64.h Tue Sep 12 10:27:52 2023 -0700 -@@ -12,18 +12,19 @@ - - #include "jit/shared/Architecture-shared.h" - - #include "js/Utility.h" - - namespace js { - namespace jit { - --// Not used on PPC. --static const uint32_t ShadowStackSpace = 0; -+// Used to protect the stack from linkage area clobbers. Minimum size -+// is 4 doublewords for SP, LR, CR and TOC. -+static const uint32_t ShadowStackSpace = 32; - // The return address is in LR, not in memory/stack. - static const uint32_t SizeOfReturnAddressAfterCall = 0u; - - // Size of each bailout table entry. - // For PowerPC 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. -@@ -98,17 +99,17 @@ - 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 = 24; -+ 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) | -@@ -153,21 +154,26 @@ - (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); -+ (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); - -@@ -193,22 +199,22 @@ - (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::r28) | - (1 << Registers::r29) | - (1 << Registers::r30) | -- (1 << Registers::r31) -+ //(1 << Registers::r31) - // Watch out for sign extension! -- ) & AllMask; -+ 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); -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/ppc64/Assembler-ppc64.h ---- a/js/src/jit/ppc64/Assembler-ppc64.h Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/ppc64/Assembler-ppc64.h Tue Sep 12 10:27:52 2023 -0700 -@@ -155,17 +155,17 @@ - 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; // wasm -+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; -@@ -188,25 +188,30 @@ - // Registers used in RegExpTester instruction (do not use ReturnReg). - static constexpr Register RegExpTesterRegExpReg = CallTempReg0; - static constexpr Register RegExpTesterStringReg = CallTempReg1; - static constexpr Register RegExpTesterLastIndexReg = 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 WasmTlsReg = r18; -+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 - -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/ppc64/Bailouts-ppc64.cpp ---- a/js/src/jit/ppc64/Bailouts-ppc64.cpp Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/ppc64/Bailouts-ppc64.cpp Tue Sep 12 10:27:52 2023 -0700 -@@ -1,8 +1,9 @@ -+#error don't use - /* -*- 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/Bailouts-ppc64.h" - #include "jit/Bailouts.h" -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/ppc64/Bailouts-ppc64.h ---- a/js/src/jit/ppc64/Bailouts-ppc64.h Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/ppc64/Bailouts-ppc64.h Tue Sep 12 10:27:52 2023 -0700 -@@ -1,8 +1,9 @@ -+#error don't use - /* -*- 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_Bailouts_ppc64_h - #define jit_ppc64_Bailouts_ppc64_h -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/ppc64/CodeGenerator-ppc64.cpp ---- a/js/src/jit/ppc64/CodeGenerator-ppc64.cpp Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/ppc64/CodeGenerator-ppc64.cpp Tue Sep 12 10:27:52 2023 -0700 -@@ -18,17 +18,16 @@ - #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 "vm/TraceLogging.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; - -@@ -746,21 +745,23 @@ - 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(masm.getStackPointer(), ToStackOffset(&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); - } - -@@ -772,17 +773,16 @@ - - #ifdef JS_PUNBOX64 - Operand - CodeGeneratorPPC64::ToOperandOrRegister64(const LInt64Allocation input) - { - return ToOperand(input.value()); - } - #else --#error does this actually get compiled? - Register64 - CodeGeneratorPPC64::ToOperandOrRegister64(const LInt64Allocation input) - { - return ToRegister64(input); - } - #endif - - void -@@ -792,34 +792,16 @@ - // 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); - } - --FrameSizeClass --FrameSizeClass::FromDepth(uint32_t frameDepth) --{ -- return FrameSizeClass::None(); --} -- --FrameSizeClass --FrameSizeClass::ClassLimit() --{ -- return FrameSizeClass(0); --} -- --uint32_t --FrameSizeClass::frameSize() const --{ -- MOZ_CRASH("PPC64 does not use frame size classes"); --} -- - void - CodeGenerator::visitTestIAndBranch(LTestIAndBranch* test) - { - ADBlock(); - const LAllocation* opd = test->getOperand(0); - MBasicBlock* ifTrue = test->ifTrue(); - MBasicBlock* ifFalse = test->ifFalse(); - -@@ -932,23 +914,16 @@ - { - ADBlock(); - - MOZ_ASSERT_IF(!masm.oom(), label->used()); - MOZ_ASSERT_IF(!masm.oom(), !label->bound()); - - encode(snapshot); - -- // Though the assembler doesn't track all frame pushes, at least make sure -- // the known value makes sense. We can't use bailout tables if the stack -- // isn't properly aligned to the static frame size. -- MOZ_ASSERT_IF(frameClass_ != FrameSizeClass::None(), -- frameClass_.frameSize() == masm.framePushed()); -- -- // We don't use table bailouts because retargeting is easier this way. - 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 -@@ -1897,20 +1872,21 @@ - MoveOperand - CodeGeneratorPPC64::toMoveOperand(LAllocation a) const - { - if (a.isGeneralReg()) - return MoveOperand(ToRegister(a)); - if (a.isFloatReg()) { - return MoveOperand(ToFloatRegister(a)); - } -- int32_t offset = ToStackOffset(a); -- MOZ_ASSERT((offset & 3) == 0); -- -- return MoveOperand(StackPointer, offset); -+ MoveOperand::Kind kind = -+ a.isStackArea() ? MoveOperand::EFFECTIVE_ADDRESS : MoveOperand::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)); -@@ -2982,17 +2958,18 @@ - CodeGenerator::visitWasmBuiltinTruncateFToInt32(LWasmBuiltinTruncateFToInt32 *lir) - { - MOZ_CRASH("NYI"); - } - - void - CodeGenerator::visitWasmHeapBase(LWasmHeapBase *lir) - { -- MOZ_CRASH("NYI"); -+ MOZ_ASSERT(lir->instance()->isBogus()); -+ masm.movePtr(HeapReg, ToRegister(lir->output())); - } - - void - CodeGenerator::visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop* lir) - { - ADBlock(); - MOZ_ASSERT(lir->mir()->hasUses()); - -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/ppc64/MacroAssembler-ppc64-inl.h ---- a/js/src/jit/ppc64/MacroAssembler-ppc64-inl.h Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/ppc64/MacroAssembler-ppc64-inl.h Tue Sep 12 10:27:52 2023 -0700 -@@ -1671,16 +1671,47 @@ - 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: -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/ppc64/MacroAssembler-ppc64.cpp ---- a/js/src/jit/ppc64/MacroAssembler-ppc64.cpp Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/ppc64/MacroAssembler-ppc64.cpp Tue Sep 12 10:27:52 2023 -0700 -@@ -1171,23 +1171,20 @@ - as_stfsu(f, StackPointer, (int32_t)-sizeof(double)); - else - as_stfdu(f, StackPointer, (int32_t)-sizeof(double)); - } - - bool - MacroAssemblerPPC64Compat::buildOOLFakeExitFrame(void* fakeReturnAddr) - { -- uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), FrameType::IonJS, -- ExitFrameLayout::Size()); -- -- asMasm().Push(Imm32(descriptor)); // descriptor_ -- asMasm().Push(ImmPtr(fakeReturnAddr)); -- -- return true; -+ 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); -@@ -2095,123 +2092,170 @@ - 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) -+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); -- // Call the handler. - asMasm().setupUnalignedABICall(r4); - asMasm().passABIArg(r3); - asMasm().callWithABI(MoveOp::GENERAL, - CheckUnsafeCallWithABI::DontCheckHasExitFrame); - - Label entryFrame; - Label catch_; - Label finally; -- Label return_; -+ Label returnBaseline; -+ Label returnIon; - Label bailout; - Label wasm; -+ Label wasmCatch; - - // Already clobbered r3, so use it... -- load32(Address(StackPointer, offsetof(ResumeFromException, kind)), r3); -- asMasm().branch32(Assembler::Equal, r3, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), -- &entryFrame); -- asMasm().branch32(Assembler::Equal, r3, Imm32(ResumeFromException::RESUME_CATCH), &catch_); -- asMasm().branch32(Assembler::Equal, r3, Imm32(ResumeFromException::RESUME_FINALLY), &finally); -- asMasm().branch32(Assembler::Equal, r3, Imm32(ResumeFromException::RESUME_FORCED_RETURN), -- &return_); -- asMasm().branch32(Assembler::Equal, r3, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout); -- asMasm().branch32(Assembler::Equal, r3, Imm32(ResumeFromException::RESUME_WASM), &wasm); -+ 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, load the new stack pointer -+ // 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, offsetof(ResumeFromException, stackPointer)), StackPointer); -+ 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_); -- loadPtr(Address(StackPointer, offsetof(ResumeFromException, target)), r3); -- loadPtr(Address(StackPointer, offsetof(ResumeFromException, framePointer)), BaselineFrameReg); -- loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)), StackPointer); -- jump(r3); -+ // 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 JSOP_RETSUB: BooleanValue(true) and the -- // exception. -+ // two values expected by the finally block: the exception and -+ // BooleanValue(true). - bind(&finally); - ValueOperand exception = ValueOperand(r4); -- loadValue(Address(sp, offsetof(ResumeFromException, exception)), exception); -- -- loadPtr(Address(sp, offsetof(ResumeFromException, target)), r3); -- loadPtr(Address(sp, offsetof(ResumeFromException, framePointer)), BaselineFrameReg); -- loadPtr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp); -- -- pushValue(BooleanValue(true)); -+ loadValue(Address(sp, ResumeFromException::offsetOfException()), exception); -+ -+ loadPtr(Address(sp, ResumeFromException::offsetOfTarget()), r12); -+ xs_mtctr(r12); -+ loadPtr(Address(sp, ResumeFromException::offsetOfFramePointer()), FramePointer); -+ loadPtr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp); -+ - pushValue(exception); -- jump(r3); -- -- // Only used in debug mode. Return BaselineFrame->returnValue() to the -- // caller. -- bind(&return_); -- loadPtr(Address(StackPointer, offsetof(ResumeFromException, framePointer)), BaselineFrameReg); -- loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)), StackPointer); -- loadValue(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfReturnValue()), -- JSReturnOperand); -- ma_move(StackPointer, BaselineFrameReg); -- pop(BaselineFrameReg); -- -- // If profiling is enabled, then update the lastProfilingFrame to refer to caller -- // frame before returning. -- { -- Label skipProfilingInstrumentation; -- // Test if profiler enabled. -- AbsoluteAddress addressOfEnabled(GetJitContext()->runtime->geckoProfiler().addressOfEnabled()); -- asMasm().branch32(Assembler::Equal, addressOfEnabled, Imm32(0), -- &skipProfilingInstrumentation); -- jump(profilerExitTail); -- bind(&skipProfilingInstrumentation); -- } -- -- ret(); -+ 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. -+ // the bailout tail stub. Load 1 (true) in ReturnReg to indicate -+ // success. - bind(&bailout); -- loadPtr(Address(sp, offsetof(ResumeFromException, bailoutInfo)), r5); -+ loadPtr(Address(sp, ResumeFromException::offsetOfBailoutInfo()), r5); -+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), -+ StackPointer); - ma_li(ReturnReg, Imm32(1)); -- loadPtr(Address(sp, offsetof(ResumeFromException, target)), r4); -- jump(r4); -+ 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, offsetof(ResumeFromException, framePointer)), FramePointer); -- loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)), StackPointer); -+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()), FramePointer); -+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), StackPointer); - ret(); -+ -+ // Found a wasm catch handler, restore state and jump to it. -+ bind(&wasmCatch); -+ loadPtr(Address(sp, ResumeFromException::offsetOfTarget()), r12); -+ xs_mtctr(r12); -+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()), -+ FramePointer); -+ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), -+ StackPointer); -+ as_bctr(); - } - - // 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) - { -@@ -2572,55 +2616,39 @@ - MOZ_ASSERT(lhs.valueReg() != scratch); - moveValue(rhs, ValueOperand(scratch)); - ma_bc(lhs.valueReg(), scratch, label, cond); - } - - // ======================================================================== - // Memory access primitives. - template --void --MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, -- const T& dest, MIRType slotType) --{ -- if (valueType == MIRType::Double) { -- storeDouble(value.reg().typedReg().fpu(), dest); -- return; -- } -- -- // For known integers and booleans, we can just store the unboxed value if -- // the slot has the same type. -- if ((valueType == MIRType::Int32 || valueType == MIRType::Boolean) && slotType == valueType) { -- if (value.constant()) { -- Value val = value.value(); -- if (valueType == MIRType::Int32) -- store32(Imm32(val.toInt32()), dest); -- else -- store32(Imm32(val.toBoolean() ? 1 : 0), dest); -- } else { -- store32(value.reg().typedReg().gpr(), 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, MIRType slotType); --template void --MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, -- const BaseIndex& dest, MIRType slotType); --template void --MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, -- const BaseObjectElementIndex& dest, MIRType slotType); -+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)); -@@ -3172,16 +3200,27 @@ - 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) -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/ppc64/MacroAssembler-ppc64.h ---- a/js/src/jit/ppc64/MacroAssembler-ppc64.h Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/ppc64/MacroAssembler-ppc64.h Tue Sep 12 10:27:52 2023 -0700 -@@ -836,17 +836,17 @@ - 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 handleFailureWithHandlerTail(Label* profilerExitTail); -+ 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); -@@ -1126,22 +1126,24 @@ - 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; - -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/ppc64/SharedICHelpers-ppc64-inl.h ---- a/js/src/jit/ppc64/SharedICHelpers-ppc64-inl.h Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/ppc64/SharedICHelpers-ppc64-inl.h Tue Sep 12 10:27:52 2023 -0700 -@@ -9,120 +9,74 @@ - - #include "jit/SharedICHelpers.h" - - #include "jit/MacroAssembler-inl.h" - - namespace js { - namespace jit { - --inline void --EmitBaselineTailCallVM(TrampolinePtr target, MacroAssembler& masm, uint32_t argSize) --{ -- Register scratch = R2.scratchReg(); -+inline void EmitBaselineTailCallVM(TrampolinePtr target, MacroAssembler& masm, -+ uint32_t argSize) { -+#ifdef DEBUG -+ Register scratch = R2.scratchReg(); - -- // Compute frame size. -- masm.movePtr(BaselineFrameReg, scratch); -- masm.addPtr(Imm32(BaselineFrame::FramePointerOffset), scratch); -- masm.subPtr(BaselineStackReg, scratch); -+ // Compute frame size. -+ masm.movePtr(FramePointer, scratch); -+ masm.subPtr(StackPointer, scratch); - --#ifdef DEBUG -- // Store frame size without VMFunction arguments for debug assertions. -- masm.subPtr(Imm32(argSize), scratch); -- Address frameSizeAddr(BaselineFrameReg, -- BaselineFrame::reverseOffsetOfDebugFrameSize()); -- masm.store32(scratch, frameSizeAddr); -- masm.addPtr(Imm32(argSize), 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. -- // ICTailCallReg (LR) already contains the return address (as we -- // keep it there through the stub calls), but the VMWrapper code being -- // called expects the return address to also be pushed on the stack. -- masm.makeFrameDescriptor(scratch, FrameType::BaselineJS, ExitFrameLayout::Size()); -- masm.subPtr(Imm32(sizeof(CommonFrameLayout)), StackPointer); -- // Keep the tail call register current (i.e., don't just use r0). -- masm.xs_mflr(ICTailCallReg); -- masm.storePtr(scratch, Address(StackPointer, CommonFrameLayout::offsetOfDescriptor())); -- masm.storePtr(ICTailCallReg, Address(StackPointer, CommonFrameLayout::offsetOfReturnAddress())); -- -- masm.jump(target); -+ // Push frame descriptor and perform the tail call. -+ // ICTailCallReg will contain the return address after we transfer it -+ // from LR, but the VMWrapper code being -+ // called expects the return address to also be pushed on the stack. -+ masm.xs_mflr(ICTailCallReg); -+ masm.pushFrameDescriptor(FrameType::BaselineJS); -+ masm.push(ICTailCallReg); -+ masm.jump(target); - } - --/* --inline void --EmitIonTailCallVM(TrampolinePtr target, MacroAssembler& masm, uint32_t stackSize) --{ -- Register scratch = R2.scratchReg(); -- -- masm.loadPtr(Address(sp, stackSize), scratch); -- masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch); -- masm.addPtr(Imm32(stackSize + JitStubFrameLayout::Size() - sizeof(intptr_t)), scratch); -- -- // Push frame descriptor and return address, perform the tail call. -- masm.makeFrameDescriptor(scratch, FrameType::IonJS, ExitFrameLayout::Size()); -- masm.xs_mflr(ScratchRegister); -- masm.push(scratch); -- masm.push(ScratchRegister); -- masm.jump(target); --} --*/ -- --inline void --EmitBaselineCreateStubFrameDescriptor(MacroAssembler& masm, Register reg, uint32_t headerSize) --{ -- // Compute stub frame size. We have to add two pointers: the stub reg and -- // previous frame pointer pushed by EmitEnterStubFrame. -- masm.as_addi(reg, BaselineFrameReg, sizeof(intptr_t)*2); -- masm.subPtr(BaselineStackReg, reg); -- -- masm.makeFrameDescriptor(reg, FrameType::BaselineStub, headerSize); -+inline void EmitBaselineCallVM(TrampolinePtr target, MacroAssembler& masm) { -+ masm.pushFrameDescriptor(FrameType::BaselineStub); -+ masm.call(target); - } - --inline void --EmitBaselineCallVM(TrampolinePtr target, MacroAssembler& masm) --{ -- Register scratch = R2.scratchReg(); -- EmitBaselineCreateStubFrameDescriptor(masm, scratch, ExitFrameLayout::Size()); -- masm.push(scratch); -- masm.call(target); --} -- --inline void --EmitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch) --{ -- // Compute frame size. -- masm.as_addi(scratch, BaselineFrameReg, BaselineFrame::FramePointerOffset); -- masm.subPtr(BaselineStackReg, scratch); -+inline void EmitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch) { -+ MOZ_ASSERT(scratch != ICTailCallReg); - - #ifdef DEBUG -- Address frameSizeAddr(BaselineFrameReg, -- BaselineFrame::reverseOffsetOfDebugFrameSize()); -- masm.store32(scratch, frameSizeAddr); -+ // 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. -+ // 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); - -- // Push frame descriptor and return address. -- masm.makeFrameDescriptor(scratch, FrameType::BaselineJS, BaselineStubFrameLayout::Size()); -- // Keep the tail call register current (i.e., don't just use r0). -- masm.xs_mflr(ICTailCallReg); -- masm.subPtr(Imm32(STUB_FRAME_SIZE), StackPointer); -- masm.storePtr(scratch, Address(StackPointer, offsetof(BaselineStubFrame, descriptor))); -- masm.storePtr(ICTailCallReg, Address(StackPointer, -- offsetof(BaselineStubFrame, returnAddress))); -+ // Save old frame pointer, stack pointer and stub reg. -+ masm.Push(FramePointer); -+ masm.movePtr(StackPointer, FramePointer); -+ masm.Push(ICStubReg); - -- // Save old frame pointer, stack pointer and stub reg. -- masm.storePtr(ICStubReg, Address(StackPointer, -- offsetof(BaselineStubFrame, savedStub))); -- masm.storePtr(BaselineFrameReg, Address(StackPointer, -- offsetof(BaselineStubFrame, savedFrame))); -- masm.movePtr(BaselineStackReg, BaselineFrameReg); -- -- // Stack should remain aligned. -- masm.assertStackAlignment(sizeof(Value), 0); -+ // Stack should remain aligned. -+ masm.assertStackAlignment(sizeof(Value), 0); - } - - } // namespace jit - } // namespace js - - #endif /* jit_ppc64le_SharedICHelpers_ppc64le_inl_h */ -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/ppc64/SharedICHelpers-ppc64.h ---- a/js/src/jit/ppc64/SharedICHelpers-ppc64.h Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/ppc64/SharedICHelpers-ppc64.h Tue Sep 12 10:27:52 2023 -0700 -@@ -21,20 +21,16 @@ - - struct BaselineStubFrame { - uintptr_t savedFrame; - uintptr_t savedStub; - uintptr_t returnAddress; - uintptr_t descriptor; - }; - --// Size of values pushed by EmitBaselineEnterStubFrame. --static const uint32_t STUB_FRAME_SIZE = sizeof(BaselineStubFrame); --static const uint32_t STUB_FRAME_SAVED_STUB_OFFSET = offsetof(BaselineStubFrame, savedStub); -- - inline void - EmitRestoreTailCallReg(MacroAssembler& masm) - { - // No-op; LR is always the return address. - } - - inline void - EmitRepushTailCallReg(MacroAssembler& masm) -@@ -57,52 +53,34 @@ - - inline void - EmitReturnFromIC(MacroAssembler& masm) - { - masm.as_blr(); - } - - inline void --EmitChangeICReturnAddress(MacroAssembler& masm, Register reg) --{ -- masm.xs_mtlr(reg); --} -- --inline void --EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false) -+EmitBaselineLeaveStubFrame(MacroAssembler& masm) - { -- // Ion frames do not save and restore the frame pointer. If we called -- // into Ion, we have to restore the stack pointer from the frame descriptor. -- // If we performed a VM call, the descriptor has been popped already so -- // in that case we use the frame pointer. -- if (calledIntoIon) { -- masm.pop(ScratchRegister); -- masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), ScratchRegister); -- masm.addPtr(ScratchRegister, BaselineStackReg); -- } else { -- masm.movePtr(BaselineFrameReg, BaselineStackReg); -- } -+ masm.loadPtr( -+ Address(FramePointer, BaselineStubFrameLayout::ICStubOffsetFromFP), -+ ICStubReg); -+ masm.movePtr(FramePointer, StackPointer); -+ masm.Pop(FramePointer); - -- masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, savedFrame)), -- BaselineFrameReg); -- masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, savedStub)), -- ICStubReg); -+ // Load the return address into our GPR "mirror." -+ masm.Pop(ICTailCallReg); -+ // Move to LR for branching. -+ masm.xs_mtlr(ICTailCallReg); - -- // Load the return address. -- // This is different on PowerPC because LR is not a GPR. However, we -- // still need to have it in a GPR in case Ion or Baseline relies on it. -- masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, returnAddress)), -- ICTailCallReg); -- masm.xs_mtlr(ICTailCallReg); -- -- // Discard the frame descriptor and the rest of the frame. -- //masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, descriptor)), ScratchRegister); -- masm.addPtr(Imm32(STUB_FRAME_SIZE), StackPointer); -- masm.checkStackAlignment(); -+ // Discard the frame descriptor. -+ { -+ SecondScratchRegisterScope scratch2(masm); -+ masm.Pop(scratch2); -+ } - } - - template - 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); -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/ppc64/SharedICRegisters-ppc64.h ---- a/js/src/jit/ppc64/SharedICRegisters-ppc64.h Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/ppc64/SharedICRegisters-ppc64.h Tue Sep 12 10:27:52 2023 -0700 -@@ -7,18 +7,16 @@ - #ifndef jit_ppc64le_SharedICRegisters_ppc64le_h - #define jit_ppc64le_SharedICRegisters_ppc64le_h - - #include "jit/MacroAssembler.h" - - namespace js { - namespace jit { - --// The frame register should be allocatable but non-volatile. --static constexpr Register BaselineFrameReg = r20; - // 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 -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/ppc64/Trampoline-ppc64.cpp ---- a/js/src/jit/ppc64/Trampoline-ppc64.cpp Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/ppc64/Trampoline-ppc64.cpp Tue Sep 12 10:27:52 2023 -0700 -@@ -6,21 +6,18 @@ - - #include "mozilla/DebugOnly.h" - - #include "jit/Bailouts.h" - #include "jit/JitFrames.h" - #include "jit/JitRealm.h" - #include "jit/JitSpewer.h" - #include "jit/Linker.h" --#include "jit/ppc64/Bailouts-ppc64.h" -+#include "jit/PerfSpewer.h" - #include "jit/ppc64/SharedICHelpers-ppc64.h" --#ifdef JS_ION_PERF --# include "jit/PerfSpewer.h" --#endif - #include "jit/VMFunctions.h" - #include "vm/Realm.h" - - #include "jit/MacroAssembler-inl.h" - #include "jit/SharedICHelpers-inl.h" - - #if DEBUG - -@@ -170,23 +167,25 @@ - // 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 Register reg_frame = IntArgReg3; // r6 -+ const mozilla::DebugOnly 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. -@@ -237,31 +236,27 @@ - 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)); - -- // Hold stack pointer in a random clobberable register for computing -- // the frame descriptor later. Arbitrarily, let's choose r31. -- const Register frameDescSP = r31; -- masm.movePtr(StackPointer, frameDescSP); -- - // Save stack pointer as baseline frame. -- masm.movePtr(StackPointer, BaselineFrameReg); -+ 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. -+ // 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. -@@ -282,131 +277,127 @@ - - 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); - -- masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer); -- // Load the number of actual arguments. -- // This is a 32-bit quantity. -- // Then store it and the callee token on the stack. -+ // 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.storePtr(reg_token, Address(StackPointer, 0)); // callee token -- masm.storePtr(ScratchRegister, Address(StackPointer, sizeof(uintptr_t))); // actual arguments -- -- // Push frame descriptor. -- masm.subPtr(StackPointer, frameDescSP); -- masm.makeFrameDescriptor(frameDescSP, FrameType::CppToJSJit, JitFrameLayout::Size()); -- masm.push(frameDescSP); -+ masm.pushFrameDescriptorForJitCall(FrameType::CppToJSJit, -+ ScratchRegister, ScratchRegister); - - CodeLabel returnLabel; -- CodeLabel oomReturnLabel; -+ Label oomReturnLabel; - { - // Handle Interpreter -> Baseline OSR. - AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); -+ MOZ_ASSERT(!regs.has(FramePointer)); - regs.take(OsrFrameReg); -- regs.take(BaselineFrameReg); - regs.take(reg_code); -+ // 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. - MOZ_ASSERT(reg_code == ReturnReg); // regs.take(ReturnReg); -- regs.take(JSReturnOperand); -+ regs.take(JSReturnOperand); // ??? - - 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)), StackPointer); -+ masm.subPtr(Imm32(sizeof(uintptr_t) * 2), StackPointer); - masm.ma_li(scratch, &returnLabel); -- masm.storePtr(scratch, Address(StackPointer, 0)); -- -- // Push previous frame pointer. -- masm.subPtr(Imm32(sizeof(uintptr_t)), StackPointer); -- masm.storePtr(BaselineFrameReg, Address(StackPointer, 0)); -+ masm.storePtr(scratch, Address(StackPointer, sizeof(uintptr_t))); -+ // Push previous frame pointer. Recovered at frame epilogue. -+ masm.storePtr(FramePointer, Address(StackPointer, 0)); - - // Reserve frame. -- Register framePtr = BaselineFrameReg; -+ masm.movePtr(StackPointer, FramePointer); - masm.subPtr(Imm32(BaselineFrame::Size()), StackPointer); -- masm.movePtr(StackPointer, framePtr); -+ -+ 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.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch); -- masm.makeFrameDescriptor(scratch, FrameType::BaselineJS, ExitFrameLayout::Size()); -- -- // Push frame descriptor and fake return address. -- masm.reserveStack(2 * sizeof(uintptr_t)); -- masm.storePtr(scratch, Address(StackPointer, sizeof(uintptr_t))); // Frame descriptor -- masm.storePtr(scratch, Address(StackPointer, 0)); // fake return address -+ 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(framePtr, Address(StackPointer, sizeof(uintptr_t))); // BaselineFrameReg -+ masm.storePtr(FramePointer, -+ Address(StackPointer, sizeof(uintptr_t))); // BaselineFrame - masm.storePtr(reg_code, Address(StackPointer, 0)); // jitcode - -- // Initialize the frame, including filling in the slots. - using Fn = bool (*)(BaselineFrame * frame, InterpreterFrame * interpFrame, - uint32_t numStackValues); - masm.setupUnalignedABICall(scratch); -- masm.passABIArg(BaselineFrameReg); // BaselineFrame -+ masm.passABIArg(framePtrScratch); // BaselineFrame - masm.passABIArg(OsrFrameReg); // InterpreterFrame - masm.passABIArg(numStackValues); - masm.callWithABI( - MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame); - - regs.add(OsrFrameReg); - Register jitcode = regs.takeAny(); - masm.loadPtr(Address(StackPointer, 0), jitcode); -- masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), framePtr); -+ masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), FramePointer); - masm.freeStack(2 * sizeof(uintptr_t)); - - Label error; - masm.freeStack(ExitFrameLayout::SizeWithFooter()); -- masm.addPtr(Imm32(BaselineFrame::Size()), framePtr); - masm.branchIfFalseBool(ReturnReg, &error); - - // If OSR-ing, then emit instrumentation for setting lastProfilerFrame - // if profiler instrumentation is enabled. - { - Label skipProfilingInstrumentation; -- Register realFramePtr = numStackValues; - AbsoluteAddress addressOfEnabled(cx->runtime()->geckoProfiler().addressOfEnabled()); - masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), - &skipProfilingInstrumentation); -- masm.as_addi(realFramePtr, framePtr, sizeof(void*)); -- masm.profilerEnterFrame(realFramePtr, scratch); -+ masm.profilerEnterFrame(FramePointer, scratch); - masm.bind(&skipProfilingInstrumentation); - } - - //masm.xs_trap_tagged(Assembler::DebugTag0); - masm.jump(jitcode); - -- // OOM: load error value, discard return address and previous frame -- // pointer and return. -+ // OOM: frame epilogue, load error value, discard return address -+ // and return. - masm.bind(&error); -- masm.movePtr(framePtr, StackPointer); -+ 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.ma_li(scratch, &oomReturnLabel); -- masm.jump(scratch); -+ 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 -@@ -419,24 +410,21 @@ - //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); -- masm.addCodeLabel(oomReturnLabel); - } - -- // Pop arguments off the stack. -- // scratch <- 8*argc (size of all arguments we pushed on the stack) -- masm.pop(ScratchRegister); -- masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), ScratchRegister); -- masm.addPtr(ScratchRegister, StackPointer); -+ // 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. -@@ -487,156 +475,169 @@ - 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 alligned here. If not, we will have to fix it. -+ // 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 return value and BailoutInfo pointer -+ // Reserve place for BailoutInfo pointer. Two words to ensure alignment for -+ // setupAlignedABICall. - masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer); -- // Pass pointer to return value. -- masm.as_addi(r4, StackPointer, (uint16_t)sizeof(uintptr_t)); - // Pass pointer to BailoutInfo -- masm.movePtr(StackPointer, r5); -+ masm.movePtr(StackPointer, r4); - -- using Fn = bool (*)(InvalidationBailoutStack * sp, size_t * frameSizeOut, -+ using Fn = bool (*)(InvalidationBailoutStack * sp, - BaselineBailoutInfo * *info); - masm.setupAlignedABICall(); - masm.passABIArg(r3); - masm.passABIArg(r4); -- masm.passABIArg(r5); - masm.callWithABI( - MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther); - -- masm.loadPtr(Address(StackPointer, 0), r5); -- masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), r4); -- // Remove the return address, the IonScript, the register state -- // (InvaliationBailoutStack) and the space that was allocated for the -- // return value. -- masm.addPtr(Imm32(sizeof(InvalidationBailoutStack) + 2 * sizeof(uintptr_t)), StackPointer); -- // Remove the space that this frame was using before the bailout -- // (computed by InvalidationBailout). -- masm.addPtr(r4, StackPointer); -+ masm.pop(r5); - -- // Jump to shared bailout tail. The BailoutInfo pointer remains in 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"); -- // MIPS uses a5-a7, t0-t3 and s3, with s3 being the only callee-save register. -+ -+ // 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; - -- // Do not erase the frame pointer in this function. -+ 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(); - -- // Caller: -- // [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- sp -- -- // Get the |nargs| from the RectifierFrame. -- masm.loadPtr(Address(StackPointer, RectifierFrameLayout::offsetOfNumActualArgs()), nvRectReg); -- // Add one for |this|. -- masm.addPtr(Imm32(1), nvRectReg); -+ // 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); - -- const Register numActArgsReg = r5; -- const Register calleeTokenReg = r6; -- const Register tempValue = r7; -- const Register numArgsReg = r4; -- const Register numToPush = r8; -- const Register tempCalleeTokenReg = r9; -- const Register tempNumArgsReg = r10; -- -+ // Load argc. -+ masm.loadNumActualArgs(FramePointer, nvRectReg); - // Load |nformals| into numArgsReg. -- masm.loadPtr(Address(StackPointer, RectifierFrameLayout::offsetOfCalleeToken()), -+ masm.loadPtr(Address(FramePointer, -+ RectifierFrameLayout::offsetOfCalleeToken()), - calleeTokenReg); - masm.mov(calleeTokenReg, numArgsReg); - masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), numArgsReg); -- masm.load32(Address(numArgsReg, JSFunction::offsetOfFlagsAndArgCount()), -- numArgsReg); -- masm.rshift32(Imm32(JSFunction::ArgCountShift), 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); -+ 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, -+ // 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, -+ 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); -+ 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 <- r9 -- // '--- nvRectReg ---' -+ // [arg2] [arg1] [this] [ [argc] [callee] [descr] [raddr] ] <- sp -+ // '-nvRectReg-' - // - // Rectifier frame: -- // [undef] [undef] [undef] [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] -- // '-------- r8 ---------' '---- nvRectReg ---' -+ // [fp'][undef] [undef] [undef] [arg2] [arg1] [this] [ [argc] [callee] -+ // [descr] [raddr] ] -+ // '-------- r8 ---------' '-nvRectReg-' - -- // Copy number of actual arguments into numActArgsReg -- masm.loadPtr(Address(StackPointer, RectifierFrameLayout::offsetOfNumActualArgs()), -- numActArgsReg); -- -+ // Copy number of actual arguments into numActArgsReg. -+ masm.mov(nvRectReg, numActArgsReg); - - masm.moveValue(UndefinedValue(), ValueOperand(tempValue)); - -- masm.movePtr(StackPointer, tempCalleeTokenReg); // Save stack pointer. We can clobber it. -- - // Push undefined (including the padding). - { - #if(0) - Label undefLoopTop; - - masm.bind(&undefLoopTop); - masm.sub32(Imm32(1), numToPush); - masm.subPtr(Imm32(sizeof(Value)), StackPointer); -@@ -647,27 +648,25 @@ - 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"); -- -- // | - sizeof(Value)| is used such that we can read the last -- // argument, and not the value which is after. - 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, r9, r7); // r8 <- t9(saved sp) + nargs * 8 -- masm.addPtr(Imm32(sizeof(RectifierFrameLayout) - sizeof(Value)), numToPush); -+ masm.as_add(numToPush, FramePointer, r7); // r8 <- fp + nargs * 8 -+ masm.addPtr(Imm32(sizeof(RectifierFrameLayout)), numToPush); - -- // Copy and push arguments |nargs| + 1 times (to include |this|). -+ // 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)); -@@ -689,186 +688,153 @@ - Label notConstructing; - - masm.branchTest32(Assembler::Zero, calleeTokenReg, Imm32(CalleeToken_FunctionConstructing), - ¬Constructing); - - // thisFrame[numFormals] = prevFrame[argc] - ValueOperand newTarget(tempValue); - -- // +1 for |this|. We want vp[argc], so don't subtract 1. -- BaseIndex newTargetSrc(r9, numActArgsReg, TimesEight, sizeof(RectifierFrameLayout) + sizeof(Value)); -+ // 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]] <- r9 -+ // [arg2] [arg1] [this] [ [argc] [callee] [descr] [raddr] ] - // - // - // Rectifier frame: -- // [undef] [undef] [undef] [arg2] [arg1] [this] <- sp [[argc] [callee] [descr] [raddr]] -- -- MOZ_ASSERT(numToPush == r8); // can clobber -- MOZ_ASSERT(tempCalleeTokenReg == r9); // can clobber -- -- // Construct sizeDescriptor. -- masm.subPtr(StackPointer, r9); -- masm.makeFrameDescriptor(r9, FrameType::Rectifier, JitFrameLayout::Size()); -+ // [fp'] <- fp [undef] [undef] [undef] [arg2] [arg1] [this] <- sp [ [argc] -+ // [callee] [descr] [raddr] ] - - // Construct JitFrameLayout. -- masm.subPtr(Imm32(3 * sizeof(uintptr_t)), StackPointer); -- // Push actual arguments. -- masm.storePtr(numActArgsReg, Address(StackPointer, 2 * sizeof(uintptr_t))); -- // Push callee token. -- masm.storePtr(calleeTokenReg, Address(StackPointer, sizeof(uintptr_t))); -- // Push frame descriptor. -- masm.storePtr(r9, Address(StackPointer, 0)); -+ 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); -+ 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; - } - -- // Remove the rectifier frame. -- masm.loadPtr(Address(StackPointer, 0), r9); -- masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), r9); -- -- // Discard descriptor, calleeToken and number of actual arguments. -- masm.addPtr(Imm32(3 * sizeof(uintptr_t)), StackPointer); -- -- // Discard pushed arguments. -- masm.addPtr(r9, StackPointer); -- -+ 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(). Field -- * frameClassId_ is forced to be NO_FRAME_SIZE_CLASS_ID -- * (see JitRuntime::generateBailoutHandler). -+ * 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, uint32_t frameClass, Label* bailoutTail) -+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.setupAlignedABICall(); - masm.passABIArg(r3); - masm.passABIArg(r4); -- masm.callWithABI(MoveOp::GENERAL, -- CheckUnsafeCallWithABI::DontCheckOther); -+ masm.callWithABI(MoveOp::GENERAL, -+ CheckUnsafeCallWithABI::DontCheckOther); - - // Get BailoutInfo pointer. - masm.loadPtr(Address(StackPointer, 0), r5); - -- // Stack is: -- // [frame] -- // snapshotOffset -- // frameSize -- // [bailoutFrame] -- // [bailoutInfo] -- // - // Remove both the bailout frame and the topmost Ion frame's stack. -- // First, load frameSize from stack. -- masm.loadPtr(Address(StackPointer, -- sizeOfBailoutInfo + BailoutStack::offsetOfFrameSize()), r4); -- // Remove complete BailoutStack class and data after it. -- masm.addPtr(Imm32(sizeof(BailoutStack) + sizeOfBailoutInfo), StackPointer); -- // Finally, remove frame size from stack. -- masm.addPtr(r4, StackPointer); -+ 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); - } - --JitRuntime::BailoutTable --JitRuntime::generateBailoutTable(MacroAssembler& masm, Label* bailoutTail, uint32_t frameClass) --{ -- MOZ_CRASH("PPC64 does not use bailout tables"); --} -- - void - JitRuntime::generateBailoutHandler(MacroAssembler& masm, Label* bailoutTail) - { -+ AutoCreatedBy acb(masm, "JitRuntime::generateBailoutHandler"); - ADBlock("generateBailoutHandler"); -+ - bailoutHandlerOffset_ = startTrampolineCode(masm); - -- GenerateBailoutThunk(masm, NO_FRAME_SIZE_CLASS_ID, bailoutTail); -+ GenerateBailoutThunk(masm, bailoutTail); - } - - bool - JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm, - 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; a0 is the first argument register. -+ // The context is the first argument; r3 is the first argument register. - Register cxreg = r3; - regs.take(cxreg); - - // If it isn't a tail call, then the return address needs to be saved. -- // Even though the callee should do this for us, we may change the return address. -- // This completes any exit frame on top of the stack (see JitFrames.h). - if (f.expectTailCall == NonTailCall) - masm.pushReturnAddress(); - -- // We're aligned to an exit frame, so link it up. -+ // Push the frame pointer to finish the exit frame, then link it up. -+ masm.Push(FramePointer); - masm.loadJSContext(cxreg); - masm.enterExitFrame(cxreg, regs.getAny(), &f); - - // Save the base of the argument set stored on the stack. - Register argsBase = InvalidReg; - if (f.explicitArgs) { - argsBase = ThirdScratchReg; // It can't be r0, r1, r2, r12 or an argsreg, so ... - masm.as_addi(argsBase, StackPointer, ExitFrameLayout::SizeWithFooter()); -@@ -909,19 +875,16 @@ - masm.movePtr(StackPointer, outReg); - break; - - default: - MOZ_ASSERT(f.outParam == Type_Void); - break; - } - -- if (!generateTLEnterVM(masm, f)) -- return false; -- - masm.setupUnalignedABICall(regs.getAny()); - masm.passABIArg(cxreg); - - size_t argDisp = 0; - - // Copy any arguments. - for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) { - switch (f.argProperties(explicitArg)) { -@@ -946,20 +909,16 @@ - - // Copy the implicit outparam, if any. - if (InvalidReg != outReg) - masm.passABIArg(outReg); - - masm.callWithABI(nativeFun, MoveOp::GENERAL, - CheckUnsafeCallWithABI::DontCheckHasExitFrame); - -- -- if (!generateTLExitVM(masm, f)) -- return false; -- - // 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()); -@@ -1004,28 +963,33 @@ - masm.freeStack(sizeof(double)); - break; - - default: - MOZ_ASSERT(f.outParam == Type_Void); - break; - } - -- masm.leaveExitFrame(); -- masm.retn(Imm32(sizeof(ExitFrameLayout) + -+ // Pop ExitFooterFrame and the frame pointer. -+ masm.leaveExitFrame(sizeof(void*)); -+ -+ // 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; - masm.push(temp1); - masm.push(temp2); -@@ -1062,382 +1026,18 @@ - masm.pop(temp3); - masm.pop(temp2); - masm.pop(temp1); - masm.abiret(); - - return offset; - } - --typedef bool (*HandleDebugTrapFn)(JSContext*, BaselineFrame*, uint8_t*, bool*); -- --void --JitRuntime::generateExceptionTailStub(MacroAssembler& masm, Label* profilerExitTail) --{ -- ADBlock("generateExceptionTailStub"); -- exceptionTailOffset_ = startTrampolineCode(masm); -- -- masm.bind(masm.failureLabel()); -- masm.handleFailureWithHandlerTail(profilerExitTail); --} -- - void - JitRuntime::generateBailoutTailStub(MacroAssembler& masm, Label* bailoutTail) - { -+ AutoCreatedBy acb(masm, "JitRuntime::generateBailoutTailStub"); - ADBlock("generateBailoutTailStub"); -- bailoutTailOffset_ = startTrampolineCode(masm); -+ - masm.bind(bailoutTail); -- - masm.generateBailoutTail(r4, r5); - } - --void --JitRuntime::generateProfilerExitFrameTailStub(MacroAssembler& masm, Label* profilerExitTail) --{ -- ADBlock("generateProfilerExitFrameTailStub"); -- profilerExitFrameTailOffset_ = startTrampolineCode(masm); -- masm.bind(profilerExitTail); -- -- Register scratch1 = r7; // XXX? -- Register scratch2 = r8; -- Register scratch3 = r9; -- Register scratch4 = r10; -- -- // -- // The code generated below expects that the current stack pointer points -- // to an Ion or Baseline frame, at the state it would be immediately -- // before a ret(). Thus, after this stub's business is done, it executes -- // a ret() and returns directly to the caller script, on behalf of the -- // callee script that jumped to this code. -- // -- // Thus the expected stack is: -- // -- // StackPointer ----+ -- // v -- // ..., ActualArgc, CalleeToken, Descriptor, ReturnAddr -- // MEM-HI MEM-LOW -- // -- // -- // The generated jitcode is responsible for overwriting the -- // jitActivation->lastProfilingFrame field with a pointer to the previous -- // Ion or Baseline jit-frame that was pushed before this one. It is also -- // responsible for overwriting jitActivation->lastProfilingCallSite with -- // the return address into that frame. The frame could either be an -- // immediate "caller" frame, or it could be a frame in a previous -- // JitActivation (if the current frame was entered from C++, and the C++ -- // was entered by some caller jit-frame further down the stack). -- // -- // So this jitcode is responsible for "walking up" the jit stack, finding -- // the previous Ion or Baseline JS frame, and storing its address and the -- // return address into the appropriate fields on the current jitActivation. -- // -- // There are a fixed number of different path types that can lead to the -- // current frame, which is either a baseline or ion frame: -- // -- // -- // ^ -- // | -- // ^--- Ion -- // | -- // ^--- Baseline Stub <---- Baseline -- // | -- // ^--- Argument Rectifier -- // | ^ -- // | | -- // | ^--- Ion -- // | | -- // | ^--- Baseline Stub <---- Baseline -- // | -- // ^--- Entry Frame (From C++) -- // -- Register actReg = scratch4; -- masm.loadJSContext(actReg); -- masm.loadPtr(Address(actReg, offsetof(JSContext, profilingActivation_)), actReg); -- -- Address lastProfilingFrame(actReg, JitActivation::offsetOfLastProfilingFrame()); -- Address lastProfilingCallSite(actReg, JitActivation::offsetOfLastProfilingCallSite()); -- --#ifdef DEBUG -- // Ensure that frame we are exiting is current lastProfilingFrame -- { -- masm.loadPtr(lastProfilingFrame, scratch1); -- Label checkOk; -- masm.branchPtr(Assembler::Equal, scratch1, ImmWord(0), &checkOk); -- masm.branchPtr(Assembler::Equal, StackPointer, scratch1, &checkOk); -- masm.assumeUnreachable( -- "Mismatch between stored lastProfilingFrame and current stack pointer."); -- masm.bind(&checkOk); -- } --#endif -- -- // Load the frame descriptor into |scratch1|, figure out what to do depending on its type. -- masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfDescriptor()), scratch1); -- -- // Going into the conditionals, we will have: -- // FrameDescriptor.size in scratch1 -- // FrameDescriptor.type in scratch2 -- masm.ma_and(scratch2, scratch1, Imm32((1 << FRAMETYPE_BITS) - 1)); -- masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch1); -- -- // Handling of each case is dependent on FrameDescriptor.type -- Label handle_IonJS; -- Label handle_BaselineStub; -- Label handle_Rectifier; -- Label handle_IonICCall; -- Label handle_Entry; -- Label end; -- -- masm.branch32(Assembler::Equal, scratch2, Imm32(FrameType::IonJS), -- &handle_IonJS); -- masm.branch32(Assembler::Equal, scratch2, Imm32(FrameType::BaselineJS), -- &handle_IonJS); -- masm.branch32(Assembler::Equal, scratch2, Imm32(FrameType::BaselineStub), -- &handle_BaselineStub); -- masm.branch32(Assembler::Equal, scratch2, Imm32(FrameType::Rectifier), -- &handle_Rectifier); -- masm.branch32(Assembler::Equal, scratch2, Imm32(FrameType::IonICCall), -- &handle_IonICCall); -- masm.branch32(Assembler::Equal, scratch2, Imm32(FrameType::CppToJSJit), -- &handle_Entry); -- -- // The WasmToJSJit is just another kind of entry. -- masm.branch32(Assembler::Equal, scratch2, Imm32(FrameType::WasmToJSJit), -- &handle_Entry); -- -- masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame."); -- -- // -- // FrameType::IonJS -- // -- // Stack layout: -- // ... -- // Ion-Descriptor -- // Prev-FP ---> Ion-ReturnAddr -- // ... previous frame data ... |- Descriptor.Size -- // ... arguments ... | -- // ActualArgc | -- // CalleeToken |- JitFrameLayout::Size() -- // Descriptor | -- // FP -----> ReturnAddr | -- // -- masm.bind(&handle_IonJS); -- { -- // |scratch1| contains Descriptor.size -- -- // returning directly to an IonJS frame. Store return addr to frame -- // in lastProfilingCallSite. -- masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfReturnAddress()), scratch2); -- masm.storePtr(scratch2, lastProfilingCallSite); -- -- // Store return frame in lastProfilingFrame. -- // scratch2 := StackPointer + Descriptor.size*1 + JitFrameLayout::Size(); -- masm.as_add(scratch2, StackPointer, scratch1); -- masm.as_addi(scratch2, scratch2, JitFrameLayout::Size()); -- masm.storePtr(scratch2, lastProfilingFrame); -- masm.ret(); -- } -- -- // -- // FrameType::BaselineStub -- // -- // Look past the stub and store the frame pointer to -- // the baselineJS frame prior to it. -- // -- // Stack layout: -- // ... -- // BL-Descriptor -- // Prev-FP ---> BL-ReturnAddr -- // +-----> BL-PrevFramePointer -- // | ... BL-FrameData ... -- // | BLStub-Descriptor -- // | BLStub-ReturnAddr -- // | BLStub-StubPointer | -- // +------ BLStub-SavedFramePointer |- Descriptor.Size -- // ... arguments ... | -- // ActualArgc | -- // CalleeToken |- JitFrameLayout::Size() -- // Descriptor | -- // FP -----> ReturnAddr | -- // -- // We take advantage of the fact that the stub frame saves the frame -- // pointer pointing to the baseline frame, so a bunch of calculation can -- // be avoided. -- // -- masm.bind(&handle_BaselineStub); -- { -- masm.as_add(scratch3, StackPointer, scratch1); -- Address stubFrameReturnAddr(scratch3, -- JitFrameLayout::Size() + -- BaselineStubFrameLayout::offsetOfReturnAddress()); -- masm.loadPtr(stubFrameReturnAddr, scratch2); -- masm.storePtr(scratch2, lastProfilingCallSite); -- -- Address stubFrameSavedFramePtr(scratch3, -- JitFrameLayout::Size() - (2 * sizeof(void*))); -- masm.loadPtr(stubFrameSavedFramePtr, scratch2); -- masm.addPtr(Imm32(sizeof(void*)), scratch2); // Skip past BL-PrevFramePtr -- masm.storePtr(scratch2, lastProfilingFrame); -- masm.ret(); -- } -- -- -- // -- // FrameType::Rectifier -- // -- // The rectifier frame can be preceded by either an IonJS, a BaselineStub, -- // or a CppToJSJit/WasmToJSJit frame. -- // -- // Stack layout if caller of rectifier was Ion or CppToJSJit/WasmToJSJit: -- // -- // Ion-Descriptor -- // Ion-ReturnAddr -- // ... ion frame data ... |- Rect-Descriptor.Size -- // < COMMON LAYOUT > -- // -- // Stack layout if caller of rectifier was Baseline: -- // -- // BL-Descriptor -- // Prev-FP ---> BL-ReturnAddr -- // +-----> BL-SavedFramePointer -- // | ... baseline frame data ... -- // | BLStub-Descriptor -- // | BLStub-ReturnAddr -- // | BLStub-StubPointer | -- // +------ BLStub-SavedFramePointer |- Rect-Descriptor.Size -- // ... args to rectifier ... | -- // < COMMON LAYOUT > -- // -- // Common stack layout: -- // -- // ActualArgc | -- // CalleeToken |- IonRectitiferFrameLayout::Size() -- // Rect-Descriptor | -- // Rect-ReturnAddr | -- // ... rectifier data & args ... |- Descriptor.Size -- // ActualArgc | -- // CalleeToken |- JitFrameLayout::Size() -- // Descriptor | -- // FP -----> ReturnAddr | -- // -- masm.bind(&handle_Rectifier); -- { -- // scratch2 := StackPointer + Descriptor.size*1 + JitFrameLayout::Size(); -- masm.as_add(scratch2, StackPointer, scratch1); -- masm.addPtr(Imm32(JitFrameLayout::Size()), scratch2); -- masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfDescriptor()), scratch3); -- masm.x_srwi(scratch1, scratch3, FRAMESIZE_SHIFT); -- masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch3); -- -- // Now |scratch1| contains Rect-Descriptor.Size -- // and |scratch2| points to Rectifier frame -- // and |scratch3| contains Rect-Descriptor.Type -- -- masm.assertRectifierFrameParentType(scratch3); -- -- // Check for either Ion or BaselineStub frame. -- Label notIonFrame; -- masm.branch32(Assembler::NotEqual, scratch3, Imm32(FrameType::IonJS), ¬IonFrame); -- -- // Handle Rectifier <- IonJS -- // scratch3 := RectFrame[ReturnAddr] -- masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfReturnAddress()), scratch3); -- masm.storePtr(scratch3, lastProfilingCallSite); -- -- // scratch3 := RectFrame + Rect-Descriptor.Size + RectifierFrameLayout::Size() -- masm.as_add(scratch3, scratch2, scratch1); -- masm.addPtr(Imm32(RectifierFrameLayout::Size()), scratch3); -- masm.storePtr(scratch3, lastProfilingFrame); -- masm.ret(); -- -- masm.bind(¬IonFrame); -- -- // Check for either BaselineStub or a CppToJSJit/WasmToJSJit entry -- // frame. -- masm.branch32(Assembler::NotEqual, scratch3, Imm32(FrameType::BaselineStub), &handle_Entry); -- -- // Handle Rectifier <- BaselineStub <- BaselineJS -- masm.as_add(scratch3, scratch2, scratch1); -- Address stubFrameReturnAddr(scratch3, RectifierFrameLayout::Size() + -- BaselineStubFrameLayout::offsetOfReturnAddress()); -- masm.loadPtr(stubFrameReturnAddr, scratch2); -- masm.storePtr(scratch2, lastProfilingCallSite); -- -- Address stubFrameSavedFramePtr(scratch3, -- RectifierFrameLayout::Size() - (2 * sizeof(void*))); -- masm.loadPtr(stubFrameSavedFramePtr, scratch2); -- masm.addPtr(Imm32(sizeof(void*)), scratch2); -- masm.storePtr(scratch2, lastProfilingFrame); -- masm.ret(); -- } -- -- // FrameType::IonICCall -- // -- // The caller is always an IonJS frame. -- // -- // Ion-Descriptor -- // Ion-ReturnAddr -- // ... ion frame data ... |- CallFrame-Descriptor.Size -- // StubCode | -- // ICCallFrame-Descriptor |- IonICCallFrameLayout::Size() -- // ICCallFrame-ReturnAddr | -- // ... call frame data & args ... |- Descriptor.Size -- // ActualArgc | -- // CalleeToken |- JitFrameLayout::Size() -- // Descriptor | -- // FP -----> ReturnAddr | -- masm.bind(&handle_IonICCall); -- { -- // scratch2 := StackPointer + Descriptor.size + JitFrameLayout::Size() -- masm.as_add(scratch2, StackPointer, scratch1); -- masm.addPtr(Imm32(JitFrameLayout::Size()), scratch2); -- -- // scratch3 := ICCallFrame-Descriptor.Size -- masm.loadPtr(Address(scratch2, IonICCallFrameLayout::offsetOfDescriptor()), scratch3); --#ifdef DEBUG -- // Assert previous frame is an IonJS frame. -- masm.movePtr(scratch3, scratch1); -- masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch1); -- { -- Label checkOk; -- masm.branch32(Assembler::Equal, scratch1, Imm32(FrameType::IonJS), &checkOk); -- masm.assumeUnreachable("IonICCall frame must be preceded by IonJS frame"); -- masm.bind(&checkOk); -- } --#endif -- masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch3); -- -- // lastProfilingCallSite := ICCallFrame-ReturnAddr -- masm.loadPtr(Address(scratch2, IonICCallFrameLayout::offsetOfReturnAddress()), scratch1); -- masm.storePtr(scratch1, lastProfilingCallSite); -- -- // lastProfilingFrame := ICCallFrame + ICCallFrame-Descriptor.Size + -- // IonICCallFrameLayout::Size() -- masm.as_add(scratch1, scratch2, scratch3); -- masm.addPtr(Imm32(IonICCallFrameLayout::Size()), scratch1); -- masm.storePtr(scratch1, lastProfilingFrame); -- masm.ret(); -- } -- -- // -- // FrameType::CppToJSJit / FrameType::WasmToJSJit -- // -- // If at an entry frame, store null into both fields. -- // A fast-path wasm->jit transition frame is an entry frame from the point -- // of view of the JIT. -- // -- masm.bind(&handle_Entry); -- { -- masm.movePtr(ImmPtr(nullptr), scratch1); -- masm.storePtr(scratch1, lastProfilingCallSite); -- masm.storePtr(scratch1, lastProfilingFrame); -- masm.ret(); -- } --} -- --// 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{}; --} -- -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/shared/AtomicOperations-shared-jit.cpp ---- a/js/src/jit/shared/AtomicOperations-shared-jit.cpp Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/shared/AtomicOperations-shared-jit.cpp Tue Sep 12 10:27:52 2023 -0700 -@@ -54,19 +54,19 @@ - # endif - # if defined(__x86_64__) || defined(__i386__) - return true; - # elif defined(__arm__) - return !HasAlignmentFault(); - # elif defined(__aarch64__) - // This is not necessarily true but it's the best guess right now. - return true; --#elif defined(JS_CODEGEN_PPC64) -- // We'd sure like to avoid it, even though it works. -- return false; -+# elif defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) -+ // Unaligned accesses are supported in hardware (just suboptimal). -+ return true; - # else - # error "Unsupported platform" - # endif - } - - # ifndef JS_64BIT - void AtomicCompilerFence() { - std::atomic_signal_fence(std::memory_order_acq_rel); -diff -r 99b51ba09f3f -r 671b771fd1de js/src/jit/shared/Lowering-shared-inl.h ---- a/js/src/jit/shared/Lowering-shared-inl.h Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/jit/shared/Lowering-shared-inl.h Tue Sep 12 10:27:52 2023 -0700 -@@ -518,17 +518,17 @@ - mir->type() != MIRType::Float32) { - return LAllocation(mir->toConstant()); - } - return useRegister(mir); - } - - #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ - defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_MIPS64) || \ -- defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_RISCV64) || defined(JS_CODEGEN_PPC64) - LAllocation LIRGeneratorShared::useAnyOrConstant(MDefinition* mir) { - return useRegisterOrConstant(mir); - } - LAllocation LIRGeneratorShared::useStorable(MDefinition* mir) { - return useRegister(mir); - } - LAllocation LIRGeneratorShared::useStorableAtStart(MDefinition* mir) { - return useRegisterAtStart(mir); -diff -r 99b51ba09f3f -r 671b771fd1de js/src/wasm/WasmBaselineCompile.cpp ---- a/js/src/wasm/WasmBaselineCompile.cpp Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/wasm/WasmBaselineCompile.cpp Tue Sep 12 10:27:52 2023 -0700 -@@ -687,27 +687,28 @@ - ScratchPtr scratch(*this); - masm.loadPtr(Address(InstanceReg, Instance::offsetOfDebugTrapHandler()), - scratch); - masm.ma_orr(scratch, scratch, SetCC); - masm.ma_bl(&debugTrapStub_, Assembler::NonZero); - masm.append(CallSiteDesc(iter_.lastOpcodeOffset(), kind), - CodeOffset(masm.currentOffset())); - #elif defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_MIPS64) || \ -- defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_RISCV64) || defined(JS_CODEGEN_PPC64) -+// TODO: THIS IS SUBOPTIMAL FOR PPC64 - ScratchPtr scratch(*this); - Label L; - masm.loadPtr(Address(InstanceReg, Instance::offsetOfDebugTrapHandler()), - scratch); - masm.branchPtr(Assembler::Equal, scratch, ImmWord(0), &L); - masm.call(&debugTrapStub_); - masm.append(CallSiteDesc(iter_.lastOpcodeOffset(), kind), - CodeOffset(masm.currentOffset())); - masm.bind(&L); --#elif defined(JS_CODEGEN_PPC64) -+#elif defined(XXX_JS_CODEGEN_PPC64) - Label L; - masm.loadPtr(Address(InstanceReg, Instance::offsetOfDebugTrapHandler()), - ScratchRegister); - // TODO: Ideally this should be a bcl, but we have to note the call site. - masm.ma_bc(ScratchRegister, ScratchRegister, &L, - Assembler::Zero, Assembler::ShortJump); - masm.ma_bl(&debugTrapStub_, Assembler::ShortJump); - masm.append(CallSiteDesc(iter_.lastOpcodeOffset(), kind), -diff -r 99b51ba09f3f -r 671b771fd1de js/src/wasm/WasmBuiltins.cpp ---- a/js/src/wasm/WasmBuiltins.cpp Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/wasm/WasmBuiltins.cpp Tue Sep 12 10:27:52 2023 -0700 -@@ -600,20 +600,16 @@ - - MOZ_ASSERT(iter.instance() == iter.instance()); - iter.instance()->setPendingException(ref); - - rfe->kind = ExceptionResumeKind::WasmCatch; - rfe->framePointer = (uint8_t*)iter.frame(); - rfe->instance = iter.instance(); - --#if defined(JS_CODEGEN_PPC64) -- // Our Frame must also account for the linkage area. -- offsetAdjustment += 4 * sizeof(uintptr_t); --#endif - rfe->stackPointer = - (uint8_t*)(rfe->framePointer - tryNote->landingPadFramePushed()); - rfe->target = - iter.instance()->codeBase(tier) + tryNote->landingPadEntryPoint(); - - // Make sure to clear trapping state if we got here due to a trap. - if (activation->isWasmTrapping()) { - activation->finishWasmTrap(); -diff -r 99b51ba09f3f -r 671b771fd1de js/src/wasm/WasmDebugFrame.cpp ---- a/js/src/wasm/WasmDebugFrame.cpp Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/wasm/WasmDebugFrame.cpp Tue Sep 12 10:27:52 2023 -0700 -@@ -33,20 +33,16 @@ - using namespace js::wasm; - - /* static */ - DebugFrame* DebugFrame::from(Frame* fp) { - MOZ_ASSERT(GetNearestEffectiveInstance(fp)->code().metadata().debugEnabled); - auto* df = - reinterpret_cast((uint8_t*)fp - DebugFrame::offsetOfFrame()); - MOZ_ASSERT(GetNearestEffectiveInstance(fp) == df->instance()); --#if defined(JS_CODEGEN_PPC64) -- // Our Frame has a linkage area in it which must be accounted for. -- offsetAdjustment += 4 * sizeof(uintptr_t); --#endif - return df; - } - - void DebugFrame::alignmentStaticAsserts() { - // VS2017 doesn't consider offsetOfFrame() to be a constexpr, so we have - // to use offsetof directly. These asserts can't be at class-level - // because the type is incomplete. - -diff -r 99b51ba09f3f -r 671b771fd1de js/src/wasm/WasmFrame.h ---- a/js/src/wasm/WasmFrame.h Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/wasm/WasmFrame.h Tue Sep 12 10:27:52 2023 -0700 -@@ -283,25 +283,16 @@ - // before the function has made its stack reservation, the stack alignment is - // sizeof(Frame) % WasmStackAlignment. - // - // During MacroAssembler code generation, the bytes pushed after the wasm::Frame - // are counted by masm.framePushed. Thus, the stack alignment at any point in - // time is (sizeof(wasm::Frame) + masm.framePushed) % WasmStackAlignment. - - class Frame { --#if defined(JS_CODEGEN_PPC64) -- // Since Wasm can call directly to ABI-compliant routines, the Frame must -- // have an ABI-compliant linkage area. We allocate four doublewords, the -- // minimum size. -- void *_ppc_sp_; -- void *_ppc_cr_; -- void *_ppc_lr_; -- void *_ppc_toc_; --#endif - // See GenerateCallableEpilogue for why this must be - // the first field of wasm::Frame (in a downward-growing stack). - // It's either the caller's Frame*, for wasm callers, or the JIT caller frame - // plus a tag otherwise. - uint8_t* callerFP_; - - // The return address pushed by the call (in the case of ARM/MIPS the return - // address is pushed by the first instruction of the prologue). -@@ -347,21 +338,18 @@ - static uint8_t* addExitFPTag(const Frame* fp) { - MOZ_ASSERT(!isExitFP(fp)); - return reinterpret_cast(reinterpret_cast(fp) | - ExitFPTag); - } - }; - - static_assert(!std::is_polymorphic_v, "Frame doesn't need a vtable."); --#if !defined(JS_CODEGEN_PPC64) --// PowerPC requires a linkage area, so this assert doesn't hold on that arch. - static_assert(sizeof(Frame) == 2 * sizeof(void*), - "Frame is a two pointer structure"); --#endif - - // Note that sizeof(FrameWithInstances) does not account for ShadowStackSpace. - // Use FrameWithInstances::sizeOf() if you are not incorporating - // ShadowStackSpace through other means (eg the ABIArgIter). - - class FrameWithInstances : public Frame { - // `ShadowStackSpace` bytes will be allocated here on Win64, at higher - // addresses than Frame and at lower addresses than the instance fields. -diff -r 99b51ba09f3f -r 671b771fd1de js/src/wasm/WasmFrameIter.cpp ---- a/js/src/wasm/WasmFrameIter.cpp Thu Sep 07 19:51:49 2023 -0700 -+++ b/js/src/wasm/WasmFrameIter.cpp Tue Sep 12 10:27:52 2023 -0700 -@@ -529,17 +529,18 @@ - masm.SetStackPointer64(stashedSPreg); - } - #elif defined(JS_CODEGEN_PPC64) - { - *entry = masm.currentOffset(); - - // These must be in this precise order. Fortunately we can subsume the - // SPR load into the initial "verse" since it is treated atomically. -- // The linkage area required for ABI compliance is baked into the Frame. -+ // The linkage area required for ABI compliance is baked into the Frame, -+ // so we can't just |push| anything or the offsets will be wrong. - masm.xs_mflr(ScratchRegister); - masm.as_addi(StackPointer, StackPointer, -(sizeof(Frame))); - masm.as_std(ScratchRegister, StackPointer, Frame::returnAddressOffset()); - MOZ_ASSERT_IF(!masm.oom(), PushedRetAddr == masm.currentOffset() - *entry); - masm.as_std(FramePointer, StackPointer, Frame::callerFPOffset()); - MOZ_ASSERT_IF(!masm.oom(), PushedFP == masm.currentOffset() - *entry); - masm.xs_mr(FramePointer, StackPointer); - MOZ_ASSERT_IF(!masm.oom(), SetFP == masm.currentOffset() - *entry); -@@ -1284,16 +1285,18 @@ - } else - #elif defined(JS_CODEGEN_ARM) - if (offsetFromEntry == BeforePushRetAddr || codeRange->isThunk()) { - // The return address is still in lr and fp holds the caller's fp. - fixedPC = (uint8_t*)registers.lr; - fixedFP = fp; - AssertMatchesCallSite(fixedPC, fixedFP); - } else -+#elif defined(JS_CODEGEN_PPC64) -+ MOZ_ASSERT(0); - #endif - if (offsetFromEntry == PushedRetAddr || codeRange->isThunk()) { - // The return address has been pushed on the stack but fp still - // points to the caller's fp. - fixedPC = sp[0]; - fixedFP = fp; - AssertMatchesCallSite(fixedPC, fixedFP); - } else if (offsetFromEntry == PushedFP) { -@@ -1336,16 +1339,18 @@ - #elif defined(JS_CODEGEN_ARM64) - // The stack pointer does not move until all values have - // been restored so several cases can be coalesced here. - } else if (offsetInCode >= codeRange->ret() - PoppedFP && - offsetInCode <= codeRange->ret()) { - fixedPC = Frame::fromUntaggedWasmExitFP(sp)->returnAddress(); - fixedFP = fp; - AssertMatchesCallSite(fixedPC, fixedFP); -+#elif defined(JS_CODEGEN_PPC64) -+ MOZ_ASSERT(0); - #else - } else if (offsetInCode >= codeRange->ret() - PoppedFP && - offsetInCode < codeRange->ret()) { - // The fixedFP field of the Frame has been popped into fp. - fixedPC = sp[1]; - fixedFP = fp; - AssertMatchesCallSite(fixedPC, fixedFP); - } else if (offsetInCode == codeRange->ret()) { diff --git a/firefox-esr/power9-jit-744148.diff b/firefox-esr/power9-jit-744148.diff deleted file mode 100644 index cff15e142e..0000000000 --- a/firefox-esr/power9-jit-744148.diff +++ /dev/null @@ -1,488 +0,0 @@ -# HG changeset patch -# User Cameron Kaiser -# Date 1694573058 25200 -# Tue Sep 12 19:44:18 2023 -0700 -# Node ID e3eda281a1dc739c862eb38c795833595724cefc -# Parent 671b771fd1de061e02f382e0cb20237d0e3a84a8 -builds links - -diff -r 671b771fd1de -r e3eda281a1dc config/check_macroassembler_style.py ---- a/config/check_macroassembler_style.py Tue Sep 12 10:27:52 2023 -0700 -+++ b/config/check_macroassembler_style.py Tue Sep 12 19:44:18 2023 -0700 -@@ -21,22 +21,22 @@ - # ---------------------------------------------------------------------------- - - import difflib - import os - import re - import sys - - architecture_independent = set(["generic"]) --all_unsupported_architectures_names = set(["mips32", "mips64", "mips_shared"]) -+all_unsupported_architectures_names = set(["mips32", "mips64", "mips_shared", "ppc64"]) - all_architecture_names = set( -- ["x86", "x64", "arm", "arm64", "loong64", "ppc64", "riscv64", "wasm32"] -+ ["x86", "x64", "arm", "arm64", "loong64", "riscv64", "wasm32"] - ) - all_shared_architecture_names = set( -- ["x86_shared", "arm", "arm64", "loong64", "ppc64", "riscv64", "wasm32"] -+ ["x86_shared", "arm", "arm64", "loong64", "riscv64", "wasm32"] - ) - - reBeforeArg = "(?<=[(,\s])" - reArgType = "(?P[\w\s:*&<>]+)" - reArgName = "(?P\s\w+)" - reArgDefault = "(?P(?:\s=(?:(?:\s[\w:]+\(\))|[^,)]+))?)" - reAfterArg = "(?=[,)])" - reMatchArg = re.compile(reBeforeArg + reArgType + reArgName + reArgDefault + reAfterArg) -diff -r 671b771fd1de -r e3eda281a1dc js/moz.configure ---- a/js/moz.configure Tue Sep 12 10:27:52 2023 -0700 -+++ b/js/moz.configure Tue Sep 12 19:44:18 2023 -0700 -@@ -258,16 +258,18 @@ - if target.cpu == "aarch64": - return namespace(arm64=True) - elif target.cpu == "x86_64": - return namespace(x64=True) - elif target.cpu == "loongarch64": - return namespace(loong64=True) - elif target.cpu == "riscv64": - return namespace(riscv64=True) -+ elif target.cpu == "ppc64": -+ return namespace(ppc64=True) - - return namespace(**{str(target.cpu): True}) - - - set_config("JS_CODEGEN_NONE", jit_codegen.none) - set_config("JS_CODEGEN_ARM", jit_codegen.arm) - set_config("JS_CODEGEN_ARM64", jit_codegen.arm64) - set_config("JS_CODEGEN_MIPS32", jit_codegen.mips32) -@@ -281,17 +283,17 @@ - - set_define("JS_CODEGEN_NONE", jit_codegen.none) - set_define("JS_CODEGEN_ARM", jit_codegen.arm) - set_define("JS_CODEGEN_ARM64", jit_codegen.arm64) - set_define("JS_CODEGEN_MIPS32", jit_codegen.mips32) - set_define("JS_CODEGEN_MIPS64", jit_codegen.mips64) - set_define("JS_CODEGEN_LOONG64", jit_codegen.loong64) - set_define("JS_CODEGEN_RISCV64", jit_codegen.riscv64) --set_config("JS_CODEGEN_PPC64", jit_codegen.ppc64) -+set_define("JS_CODEGEN_PPC64", jit_codegen.ppc64) - set_define("JS_CODEGEN_X86", jit_codegen.x86) - set_define("JS_CODEGEN_X64", jit_codegen.x64) - set_define("JS_CODEGEN_WASM32", jit_codegen.wasm32) - - - # Profiling - # ======================================================= - option( -diff -r 671b771fd1de -r e3eda281a1dc js/src/jit/CodeGenerator.cpp ---- a/js/src/jit/CodeGenerator.cpp Tue Sep 12 10:27:52 2023 -0700 -+++ b/js/src/jit/CodeGenerator.cpp Tue Sep 12 19:44:18 2023 -0700 -@@ -12513,17 +12513,18 @@ - - // We're out-of-bounds. We only handle the index == initlength case. - // If index > initializedLength, bail out. Note that this relies on the - // condition flags sticking from the incoming branch. - // Also note: this branch does not need Spectre mitigations, doing that for - // the capacity check below is sufficient. - Label allocElement, addNewElement; - #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ -- defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) -+ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) || \ -+ defined(JS_CODEGEN_PPC64) - // Had to reimplement for MIPS because there are no flags. - bailoutCmp32(Assembler::NotEqual, initLength, index, ins->snapshot()); - #else - bailoutIf(Assembler::NotEqual, ins->snapshot()); - #endif - - // If index < capacity, we can add a dense element inline. If not, we need - // to allocate more elements first. -diff -r 671b771fd1de -r e3eda281a1dc js/src/jit/MacroAssembler.cpp ---- a/js/src/jit/MacroAssembler.cpp Tue Sep 12 10:27:52 2023 -0700 -+++ b/js/src/jit/MacroAssembler.cpp Tue Sep 12 19:44:18 2023 -0700 -@@ -5183,17 +5183,17 @@ - ma_sll(temp1, temp1, temp3); - #elif JS_CODEGEN_MIPS64 - ma_dsll(temp1, temp1, temp3); - #elif JS_CODEGEN_LOONG64 - as_sll_d(temp1, temp1, temp3); - #elif JS_CODEGEN_RISCV64 - sll(temp1, temp1, temp3); - #elif JS_CODEGEN_PPC64 -- as_sld(temp1, temp1, temp3) -+ as_sld(temp1, temp1, temp3); - #elif JS_CODEGEN_WASM32 - MOZ_CRASH(); - #elif JS_CODEGEN_NONE - MOZ_CRASH(); - #else - # error "Unknown architecture" - #endif - -diff -r 671b771fd1de -r e3eda281a1dc js/src/jit/ppc64/Assembler-ppc64.h ---- a/js/src/jit/ppc64/Assembler-ppc64.h Tue Sep 12 10:27:52 2023 -0700 -+++ b/js/src/jit/ppc64/Assembler-ppc64.h Tue Sep 12 19:44:18 2023 -0700 -@@ -175,25 +175,30 @@ - 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 in RegExpMatcher instruction (do not use JSReturnOperand). -+// 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 in RegExpTester instruction (do not use ReturnReg). --static constexpr Register RegExpTesterRegExpReg = CallTempReg0; --static constexpr Register RegExpTesterStringReg = CallTempReg1; --static constexpr Register RegExpTesterLastIndexReg = 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. -diff -r 671b771fd1de -r e3eda281a1dc js/src/jit/ppc64/CodeGenerator-ppc64.cpp ---- a/js/src/jit/ppc64/CodeGenerator-ppc64.cpp Tue Sep 12 10:27:52 2023 -0700 -+++ b/js/src/jit/ppc64/CodeGenerator-ppc64.cpp Tue Sep 12 19:44:18 2023 -0700 -@@ -1873,17 +1873,17 @@ - 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::EFFECTIVE_ADDRESS : MoveOperand::MEMORY; -+ 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) - { -diff -r 671b771fd1de -r e3eda281a1dc js/src/jit/ppc64/MacroAssembler-ppc64-inl.h ---- a/js/src/jit/ppc64/MacroAssembler-ppc64-inl.h Tue Sep 12 10:27:52 2023 -0700 -+++ b/js/src/jit/ppc64/MacroAssembler-ppc64-inl.h Tue Sep 12 19:44:18 2023 -0700 -@@ -369,16 +369,23 @@ - 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); - } - -+void MacroAssembler::mulHighUnsigned32(Imm32 imm, Register src, Register dest) { -+ MOZ_ASSERT(src != ScratchRegister); -+ move32(imm, ScratchRegister); -+ as_mulhw(dest, ScratchRegister, src); -+ x_sldi(dest, dest, 32); -+} -+ - 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); - } -diff -r 671b771fd1de -r e3eda281a1dc js/src/jit/ppc64/MacroAssembler-ppc64.cpp ---- a/js/src/jit/ppc64/MacroAssembler-ppc64.cpp Tue Sep 12 10:27:52 2023 -0700 -+++ b/js/src/jit/ppc64/MacroAssembler-ppc64.cpp Tue Sep 12 19:44:18 2023 -0700 -@@ -5,27 +5,28 @@ - * 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 -- -+#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 "jit/MacroAssembler-inl.h" - - using namespace js; - using namespace jit; - - using mozilla::Abs; - using mozilla::CheckedInt; - -@@ -1225,18 +1226,17 @@ - } - 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) --{ -+CodeOffset MacroAssembler::moveNearAddressWithPatch(Register dest) { - return movWithPatch(ImmPtr(nullptr), dest); - } - - void - MacroAssembler::patchNearAddressMove(CodeLocationLabel loc, - CodeLocationLabel target) - { - PatchDataWithValueCheck(loc, ImmPtr(target.raw()), ImmPtr(nullptr)); -@@ -2579,35 +2579,23 @@ - Label* label) - { - ADBlock(); - MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); - Label done; - branchTestGCThing(Assembler::NotEqual, value, - cond == Assembler::Equal ? &done : label); - -- if (temp != InvalidReg) { -- unboxGCThingForGCBarrier(value, temp); -- orPtr(Imm32(gc::ChunkMask), temp); -- loadPtr(Address(temp, gc::ChunkStoreBufferOffsetFromLastByte), temp); -- branchPtr(InvertCondition(cond), temp, ImmWord(0), label); -- } else { -- // Honey, Ion stole the temp register again. Get out the baseball -- // bat, would you? -- // -- // Both constants are too large to be immediates. -- unboxGCThingForGCBarrier(value, ScratchRegister); -- ma_li(SecondScratchReg, gc::ChunkMask); -- as_or(SecondScratchReg, ScratchRegister, SecondScratchReg); -- ma_li(ScratchRegister, gc::ChunkStoreBufferOffsetFromLastByte); -- as_add(SecondScratchReg, SecondScratchReg, ScratchRegister); -- as_ld(ScratchRegister, SecondScratchReg, 0); -- as_cmpdi(ScratchRegister, 0); -- ma_bc(InvertCondition(cond), label); -- } -+ // getGCThingValueChunk uses r0 and may use r12. -+ ScratchRegisterScope scratch2(*this); -+ -+ getGCThingValueChunk(value, scratch2); -+ loadPtr(Address(scratch2, gc::ChunkStoreBufferOffset), scratch2); -+ branchPtr(InvertCondition(cond), scratch2, ImmWord(0), label); -+ - bind(&done); - } - - void - MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, - const Value& rhs, Label* label) - { - ADBlock(); -@@ -4772,52 +4760,34 @@ - - addCodeLabel(cl); - return retAddr; - } - - void - MacroAssembler::loadStoreBuffer(Register ptr, Register buffer) - { -- if (ptr != buffer) -- movePtr(ptr, buffer); -- orPtr(Imm32(gc::ChunkMask), buffer); -- loadPtr(Address(buffer, gc::ChunkStoreBufferOffsetFromLastByte), 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 != SecondScratchReg); -- -- if (temp != InvalidReg) { -- movePtr(ptr, temp); -- orPtr(Imm32(gc::ChunkMask), temp); -- branchPtr(InvertCondition(cond), -- Address(temp, gc::ChunkStoreBufferOffsetFromLastByte), -- ImmWord(0), label); -- } else { -- // Why, those cheapskates. We have to provide our own temp too? -- // Did the bean counters cut our temp register budget this year? -- // (Ion hits this.) -- MOZ_ASSERT(ptr != ScratchRegister); -- -- // Both offsets are too big to be immediate displacements. -- ma_li(ScratchRegister, gc::ChunkMask); -- as_or(SecondScratchReg, ptr, ScratchRegister); -- ma_li(ScratchRegister, gc::ChunkStoreBufferOffsetFromLastByte); -- as_add(SecondScratchReg, SecondScratchReg, ScratchRegister); -- as_ld(ScratchRegister, SecondScratchReg, 0); -- as_cmpdi(ScratchRegister, 0); -- ma_bc(InvertCondition(cond), label); -- } -+ MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); -+ MOZ_ASSERT(ptr != temp); -+ MOZ_ASSERT(ptr != ScratchRegister); // Both may be used internally. -+ MOZ_ASSERT(temp != ScratchRegister); -+ MOZ_ASSERT(temp != InvalidReg); -+ -+ ma_and(temp, ptr, Imm32(int32_t(~gc::ChunkMask))); -+ branchPtr(InvertCondition(cond), Address(temp, gc::ChunkStoreBufferOffset), -+ ImmWord(0), label); - } - - void - MacroAssembler::comment(const char* msg) - { - Assembler::comment(msg); - } - -diff -r 671b771fd1de -r e3eda281a1dc js/src/jit/ppc64/MacroAssembler-ppc64.h ---- a/js/src/jit/ppc64/MacroAssembler-ppc64.h Tue Sep 12 10:27:52 2023 -0700 -+++ b/js/src/jit/ppc64/MacroAssembler-ppc64.h Tue Sep 12 19:44:18 2023 -0700 -@@ -647,16 +647,31 @@ - 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" - } - -+ // 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); -@@ -835,16 +850,20 @@ - } - 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. -diff -r 671b771fd1de -r e3eda281a1dc js/src/jit/ppc64/Trampoline-ppc64.cpp ---- a/js/src/jit/ppc64/Trampoline-ppc64.cpp Tue Sep 12 10:27:52 2023 -0700 -+++ b/js/src/jit/ppc64/Trampoline-ppc64.cpp Tue Sep 12 19:44:18 2023 -0700 -@@ -1,28 +1,28 @@ - /* -*- 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/DebugOnly.h" -- - #include "jit/Bailouts.h" -+#include "jit/BaselineFrame.h" -+#include "jit/CalleeToken.h" - #include "jit/JitFrames.h" --#include "jit/JitRealm.h" --#include "jit/JitSpewer.h" --#include "jit/Linker.h" --#include "jit/PerfSpewer.h" --#include "jit/ppc64/SharedICHelpers-ppc64.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/Realm.h" -+#include "vm/JitActivation.h" // js::jit::JitActivation -+#include "vm/JSContext.h" - - #include "jit/MacroAssembler-inl.h" --#include "jit/SharedICHelpers-inl.h" - - #if DEBUG - - /* Useful class to print visual guard blocks. */ - class TrampolineAutoDeBlock - { - private: - const char *blockname; -@@ -891,17 +891,17 @@ - case VMFunctionData::WordByValue: - if (f.argPassedInFloatReg(explicitArg)) - masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::DOUBLE); - else - masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); - argDisp += sizeof(void*); - break; - case VMFunctionData::WordByRef: -- masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), -+ masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::Kind::EffectiveAddress), - MoveOp::GENERAL); - argDisp += sizeof(void*); - break; - case VMFunctionData::DoubleByValue: - case VMFunctionData::DoubleByRef: - MOZ_CRASH("NYI: PPC64 callVM no support for 128-bit values"); - break; - } diff --git a/firefox-esr/power9-jit-744150.diff b/firefox-esr/power9-jit-744150.diff deleted file mode 100644 index 8bf5c859b4..0000000000 --- a/firefox-esr/power9-jit-744150.diff +++ /dev/null @@ -1,146 +0,0 @@ -# HG changeset patch -# User Cameron Kaiser -# Date 1694753553 25200 -# Thu Sep 14 21:52:33 2023 -0700 -# Node ID ba4e29926385c655d979fe4c3726f1bedbcc42b7 -# Parent 4311f1e4d21272333a719950b15b91a486687ee7 -passes blinterp and baseline except for wasm-containing tests - -diff -r 4311f1e4d212 -r ba4e29926385 js/src/jit/ppc64/MacroAssembler-ppc64-inl.h ---- a/js/src/jit/ppc64/MacroAssembler-ppc64-inl.h Thu Sep 14 20:18:54 2023 -0700 -+++ b/js/src/jit/ppc64/MacroAssembler-ppc64-inl.h Thu Sep 14 21:52:33 2023 -0700 -@@ -1910,30 +1910,30 @@ - Label* label) - { - ma_bc(cond, lhs, rhs, label); - } - - void - MacroAssembler::branchTruncateFloat32ToInt32(FloatRegister src, Register dest, Label* fail) - { -- MOZ_CRASH(); -+ 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) - { -- MOZ_CRASH(); -+ 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); - } -diff -r 4311f1e4d212 -r ba4e29926385 js/src/jit/ppc64/MacroAssembler-ppc64.cpp ---- a/js/src/jit/ppc64/MacroAssembler-ppc64.cpp Thu Sep 14 20:18:54 2023 -0700 -+++ b/js/src/jit/ppc64/MacroAssembler-ppc64.cpp Thu Sep 14 21:52:33 2023 -0700 -@@ -2579,22 +2579,19 @@ - Label* label) - { - ADBlock(); - MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); - Label done; - branchTestGCThing(Assembler::NotEqual, value, - cond == Assembler::Equal ? &done : label); - -- // getGCThingValueChunk uses r0 and may use r12. -- ScratchRegisterScope scratch2(*this); -- -- getGCThingValueChunk(value, scratch2); -- loadPtr(Address(scratch2, gc::ChunkStoreBufferOffset), scratch2); -- branchPtr(InvertCondition(cond), scratch2, ImmWord(0), 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) - { -diff -r 4311f1e4d212 -r ba4e29926385 js/src/jit/ppc64/Trampoline-ppc64.cpp ---- a/js/src/jit/ppc64/Trampoline-ppc64.cpp Thu Sep 14 20:18:54 2023 -0700 -+++ b/js/src/jit/ppc64/Trampoline-ppc64.cpp Thu Sep 14 21:52:33 2023 -0700 -@@ -297,22 +297,25 @@ - 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. -- MOZ_ASSERT(reg_code == ReturnReg); // regs.take(ReturnReg); - 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(); - -@@ -765,22 +768,22 @@ - } - - static void - GenerateBailoutThunk(MacroAssembler& masm, Label* bailoutTail) - { - PushBailoutFrame(masm, r3); - - // Put pointer to BailoutInfo. -- static const uint32_t sizeOfBailoutInfo = sizeof(uintptr_t) * 2; -+ 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.setupAlignedABICall(); -+ masm.setupUnalignedABICall(r5); - masm.passABIArg(r3); - masm.passABIArg(r4); - masm.callWithABI(MoveOp::GENERAL, - CheckUnsafeCallWithABI::DontCheckOther); - - // Get BailoutInfo pointer. - masm.loadPtr(Address(StackPointer, 0), r5); - -@@ -986,16 +989,17 @@ - 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. diff --git a/firefox-esr/power9-jit-744151.diff b/firefox-esr/power9-jit-744151.diff deleted file mode 100644 index e3f37e8d53..0000000000 --- a/firefox-esr/power9-jit-744151.diff +++ /dev/null @@ -1,195 +0,0 @@ -# HG changeset patch -# User Cameron Kaiser -# Date 1694807083 25200 -# Fri Sep 15 12:44:43 2023 -0700 -# Node ID 3ac07c6a65bceaeb75d59aafa7728388c31ea11d -# Parent ba4e29926385c655d979fe4c3726f1bedbcc42b7 -PGO build stuff, hardcode ion off in test build - -diff -r ba4e29926385 -r 3ac07c6a65bc build/moz.configure/lto-pgo.configure ---- a/build/moz.configure/lto-pgo.configure Thu Sep 14 21:52:33 2023 -0700 -+++ b/build/moz.configure/lto-pgo.configure Fri Sep 15 12:44:43 2023 -0700 -@@ -81,17 +81,17 @@ - - @depends(c_compiler, pgo_profile_path, target_is_windows) - @imports("multiprocessing") - def pgo_flags(compiler, profdata, target_is_windows): - if compiler.type == "gcc": - return namespace( - gen_cflags=["-fprofile-generate"], - gen_ldflags=["-fprofile-generate"], -- use_cflags=["-fprofile-use", "-fprofile-correction", "-Wcoverage-mismatch"], -+ use_cflags=["-fprofile-use", "-fprofile-correction", "-Wno-coverage-mismatch"], - use_ldflags=["-fprofile-use"], - ) - - if compiler.type in ("clang-cl", "clang"): - prefix = "" - if compiler.type == "clang-cl": - prefix = "/clang:" - gen_ldflags = None -diff -r ba4e29926385 -r 3ac07c6a65bc build/pgo/profileserver.py ---- a/build/pgo/profileserver.py Thu Sep 14 21:52:33 2023 -0700 -+++ b/build/pgo/profileserver.py Fri Sep 15 12:44:43 2023 -0700 -@@ -82,19 +82,32 @@ - docroot=os.path.join(build.topsrcdir, "build", "pgo"), - path_mappings=path_mappings, - ) - httpd.start(block=False) - - locations = ServerLocations() - locations.add_host(host="127.0.0.1", port=PORT, options="primary,privileged") - -- old_profraw_files = glob.glob("*.profraw") -- for f in old_profraw_files: -- os.remove(f) -+ using_gcc = False -+ try: -+ if build.config_environment.substs.get('CC_TYPE') == 'gcc': -+ using_gcc = True -+ except BuildEnvironmentNotFoundException: -+ pass -+ -+ if using_gcc: -+ for dirpath, _, filenames in os.walk('.'): -+ for f in filenames: -+ if f.endswith('.gcda'): -+ os.remove(os.path.join(dirpath, f)) -+ else: -+ old_profraw_files = glob.glob('*.profraw') -+ for f in old_profraw_files: -+ os.remove(f) - - with TemporaryDirectory() as profilePath: - # TODO: refactor this into mozprofile - profile_data_dir = os.path.join(build.topsrcdir, "testing", "profiles") - with open(os.path.join(profile_data_dir, "profiles.json"), "r") as fh: - base_profiles = json.load(fh)["profileserver"] - - prefpaths = [ -@@ -208,16 +221,20 @@ - - # Try to move the crash reports to the artifacts even if Firefox appears - # to exit successfully, in case there's a crash that doesn't set the - # return code to non-zero for some reason. - if get_crashreports(profilePath, name="Firefox exited successfully?") != 0: - print("Firefox exited successfully, but produced a crashreport") - sys.exit(1) - -+ print('Copying profile data....') -+ os.system('pwd'); -+ os.system('tar cf profdata.tar.gz `find . -name "*.gcda"`; cd ..; tar xf instrumented/profdata.tar.gz;'); -+ - llvm_profdata = env.get("LLVM_PROFDATA") - if llvm_profdata: - profraw_files = glob.glob("*.profraw") - if not profraw_files: - print( - "Could not find profraw files in the current directory: %s" - % os.getcwd() - ) -diff -r ba4e29926385 -r 3ac07c6a65bc js/xpconnect/src/XPCJSContext.cpp ---- a/js/xpconnect/src/XPCJSContext.cpp Thu Sep 14 21:52:33 2023 -0700 -+++ b/js/xpconnect/src/XPCJSContext.cpp Fri Sep 15 12:44:43 2023 -0700 -@@ -895,18 +895,18 @@ - false); - JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_JIT_HINTS_ENABLE, false); - sSelfHostedUseSharedMemory = false; - } else { - JS_SetGlobalJitCompilerOption( - cx, JSJITCOMPILER_BASELINE_ENABLE, - StaticPrefs::javascript_options_baselinejit_DoNotUseDirectly()); - JS_SetGlobalJitCompilerOption( -- cx, JSJITCOMPILER_ION_ENABLE, -- StaticPrefs::javascript_options_ion_DoNotUseDirectly()); -+ cx, JSJITCOMPILER_ION_ENABLE, false); // XXX -+ //StaticPrefs::javascript_options_ion_DoNotUseDirectly()); - JS_SetGlobalJitCompilerOption(cx, - JSJITCOMPILER_JIT_TRUSTEDPRINCIPALS_ENABLE, - useJitForTrustedPrincipals); - JS_SetGlobalJitCompilerOption( - cx, JSJITCOMPILER_NATIVE_REGEXP_ENABLE, - StaticPrefs::javascript_options_native_regexp_DoNotUseDirectly()); - // Only enable the jit hints cache for the content process to avoid - // any possible jank or delays on the parent process. -diff -r ba4e29926385 -r 3ac07c6a65bc modules/libpref/init/all.js ---- a/modules/libpref/init/all.js Thu Sep 14 21:52:33 2023 -0700 -+++ b/modules/libpref/init/all.js Fri Sep 15 12:44:43 2023 -0700 -@@ -951,21 +951,21 @@ - // that are associated with other domains which have - // user interaction (even if they don't have user - // interaction directly). - pref("privacy.purge_trackers.consider_entity_list", false); - - pref("dom.event.contextmenu.enabled", true); - - pref("javascript.enabled", true); --pref("javascript.options.asmjs", true); --pref("javascript.options.wasm", true); --pref("javascript.options.wasm_trustedprincipals", true); -+pref("javascript.options.asmjs", false); -+pref("javascript.options.wasm", false); -+pref("javascript.options.wasm_trustedprincipals", false); - pref("javascript.options.wasm_verbose", false); --pref("javascript.options.wasm_baselinejit", true); -+pref("javascript.options.wasm_baselinejit", false); - - pref("javascript.options.parallel_parsing", true); - pref("javascript.options.source_pragmas", true); - - pref("javascript.options.asyncstack", true); - // Broadly capturing async stack data adds overhead that is only advisable for - // developers, so we only enable it when the devtools are open, by default. - pref("javascript.options.asyncstack_capture_debuggee_only", true); -diff -r ba4e29926385 -r 3ac07c6a65bc third_party/libwebrtc/moz.build ---- a/third_party/libwebrtc/moz.build Thu Sep 14 21:52:33 2023 -0700 -+++ b/third_party/libwebrtc/moz.build Fri Sep 15 12:44:43 2023 -0700 -@@ -675,17 +675,22 @@ - "/third_party/libwebrtc/modules/audio_processing/agc2/rnn_vad/vector_math_avx2_gn", - "/third_party/libwebrtc/modules/desktop_capture/desktop_capture_differ_sse2_gn" - ] - - if CONFIG["CPU_ARCH"] == "ppc64" and CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux": - - DIRS += [ - "/third_party/libwebrtc/modules/desktop_capture/desktop_capture_gn", -- "/third_party/libwebrtc/modules/desktop_capture/primitives_gn" -+ "/third_party/libwebrtc/modules/desktop_capture/primitives_gn", -+ "/third_party/libwebrtc/modules/portal/portal_gn", -+ "/third_party/libwebrtc/third_party/drm/drm_gn", -+ "/third_party/libwebrtc/third_party/gbm/gbm_gn", -+ "/third_party/libwebrtc/third_party/libepoxy/libepoxy_gn", -+ "/third_party/libwebrtc/third_party/pipewire/pipewire_gn" - ] - - if CONFIG["CPU_ARCH"] == "riscv64" and CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux": - - DIRS += [ - "/third_party/libwebrtc/modules/desktop_capture/desktop_capture_gn", - "/third_party/libwebrtc/modules/desktop_capture/primitives_gn" - ] -diff -r ba4e29926385 -r 3ac07c6a65bc toolkit/components/terminator/nsTerminator.cpp ---- a/toolkit/components/terminator/nsTerminator.cpp Thu Sep 14 21:52:33 2023 -0700 -+++ b/toolkit/components/terminator/nsTerminator.cpp Fri Sep 15 12:44:43 2023 -0700 -@@ -455,16 +455,21 @@ - // Defend against overflow - crashAfterMS = INT32_MAX; - } else { - crashAfterMS *= scaleUp; - } - } - #endif - -+ // Disable watchdog for PGO train builds - writting profile information at -+ // exit may take time and it is better to make build hang rather than -+ // silently produce poorly performing binary. -+ crashAfterMS = INT32_MAX; -+ - UniquePtr options(new Options()); - // crashAfterTicks is guaranteed to be > 0 as - // crashAfterMS >= ADDITIONAL_WAIT_BEFORE_CRASH_MS >> HEARTBEAT_INTERVAL_MS - options->crashAfterTicks = crashAfterMS / HEARTBEAT_INTERVAL_MS; - - DebugOnly watchdogThread = - CreateSystemThread(RunWatchdog, options.release()); - MOZ_ASSERT(watchdogThread); diff --git a/firefox-esr/power9-jit-744152.diff b/firefox-esr/power9-jit-744152.diff deleted file mode 100644 index a78a3870a6..0000000000 --- a/firefox-esr/power9-jit-744152.diff +++ /dev/null @@ -1,285 +0,0 @@ -# HG changeset patch -# User Cameron Kaiser -# Date 1695240514 25200 -# Wed Sep 20 13:08:34 2023 -0700 -# Node ID bd8eea54a76bd887fd7741eb252ee8bc09bf79f2 -# Parent 3ac07c6a65bceaeb75d59aafa7728388c31ea11d -Ion fixes - -diff -r 3ac07c6a65bc -r bd8eea54a76b js/src/jit/JitFrames.cpp ---- a/js/src/jit/JitFrames.cpp Fri Sep 15 12:44:43 2023 -0700 -+++ b/js/src/jit/JitFrames.cpp Wed Sep 20 13:08:34 2023 -0700 -@@ -1701,17 +1701,25 @@ - - case RValueAllocation::CST_NULL: - return NullValue(); - - case RValueAllocation::DOUBLE_REG: - return DoubleValue(fromRegister(alloc.fpuReg())); - - case RValueAllocation::ANY_FLOAT_REG: -+#if defined(JS_CODEGEN_PPC64) -+ // There is no (simple) way from the ISA to determine if an arbitrary -+ // FPR contains a float or a double since the ISA treats them largely -+ // synonymously, so the MachineState will always contain a double even -+ // if it's encoding a float. -+ return Float32Value((float)fromRegister(alloc.fpuReg())); -+#else - return Float32Value(fromRegister(alloc.fpuReg())); -+#endif - - case RValueAllocation::ANY_FLOAT_STACK: - return Float32Value(ReadFrameFloat32Slot(fp_, alloc.stackOffset())); - - case RValueAllocation::TYPED_REG: - return FromTypedPayload(alloc.knownType(), fromRegister(alloc.reg2())); - - case RValueAllocation::TYPED_STACK: { -@@ -2316,20 +2324,21 @@ - uintptr_t* addr = state_.as().addressOfRegister(reg); - return *addr; - } - MOZ_CRASH("Invalid state"); - } - - template - T MachineState::read(FloatRegister reg) const { --#if !defined(JS_CODEGEN_RISCV64) -+#if !defined(JS_CODEGEN_RISCV64) && !defined(JS_CODEGEN_PPC64) - MOZ_ASSERT(reg.size() == sizeof(T)); - #else - // RISCV64 always store FloatRegister as 64bit. -+ // So does Power ISA (see SnapshotIterator::allocationValue). - MOZ_ASSERT(reg.size() == sizeof(double)); - #endif - - #if !defined(JS_CODEGEN_NONE) && !defined(JS_CODEGEN_WASM32) - if (state_.is()) { - uint32_t offset = reg.getRegisterDumpOffsetInBytes(); - MOZ_ASSERT((offset % sizeof(T)) == 0); - MOZ_ASSERT((offset + sizeof(T)) <= sizeof(RegisterDump::FPUArray)); -diff -r 3ac07c6a65bc -r bd8eea54a76b js/src/jit/LIR.h ---- a/js/src/jit/LIR.h Fri Sep 15 12:44:43 2023 -0700 -+++ b/js/src/jit/LIR.h Wed Sep 20 13:08:34 2023 -0700 -@@ -547,17 +547,17 @@ - static LDefinition BogusTemp() { return LDefinition(); } - - Policy policy() const { - return (Policy)((bits_ >> POLICY_SHIFT) & POLICY_MASK); - } - Type type() const { return (Type)((bits_ >> TYPE_SHIFT) & TYPE_MASK); } - - static bool isFloatRegCompatible(Type type, FloatRegister reg) { --#ifdef JS_CODEGEN_RISCV64 -+#if defined(JS_CODEGEN_RISCV64) || defined(JS_CODEGEN_PPC64) - if (type == FLOAT32 || type == DOUBLE) { - return reg.isSingle() || reg.isDouble(); - } - #else - if (type == FLOAT32) { - return reg.isSingle(); - } - if (type == DOUBLE) { -diff -r 3ac07c6a65bc -r bd8eea54a76b js/src/jit/ppc64/CodeGenerator-ppc64.cpp ---- a/js/src/jit/ppc64/CodeGenerator-ppc64.cpp Fri Sep 15 12:44:43 2023 -0700 -+++ b/js/src/jit/ppc64/CodeGenerator-ppc64.cpp Wed Sep 20 13:08:34 2023 -0700 -@@ -1364,18 +1364,17 @@ - MOZ_ASSERT(shift == 1); - masm.x_srwi(tmp, lhs, 31); - masm.add32(lhs, tmp); - } - - // Do the shift. - masm.as_srawi(dest, tmp, shift); - } else { -- if (lhs != dest) -- masm.move32(lhs, dest); -+ masm.move32(lhs, dest); - } - } - - void - CodeGenerator::visitModI(LModI* ins) - { - ADBlock(); - -@@ -1627,45 +1626,35 @@ - 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 if (dest != lhs) -+ else - masm.move32(lhs, dest); - break; - case JSOp::Rsh: - if (shift) - masm.as_srawi(dest, lhs, shift); -- else if (dest != lhs) -+ else - masm.move32(lhs, dest); - break; - case JSOp::Ursh: - if (shift) { - masm.x_srwi(dest, lhs, shift); --#if(0) -- } else if (ins->mir()->toUrsh()->fallible()) { -+ } else { - // x >>> 0 can overflow. -- masm.as_extsw(ScratchRegister, lhs); -- bailoutCmp32(Assembler::LessThan, ScratchRegister, Imm32(0), ins->snapshot()); -- } else { -+ if (ins->mir()->toUrsh()->fallible()) { -+ bailoutCmp32(Assembler::LessThan, lhs, Imm32(0), ins->snapshot()); -+ } - masm.move32(lhs, dest); - } --#else -- } else { -- // x >>> 0 can overflow. -- if (ins->mir()->toUrsh()->fallible()) -- bailoutCmp32(Assembler::LessThan, lhs, Imm32(0), ins->snapshot()); -- if (dest != lhs) -- masm.move32(lhs, dest); -- } --#endif - 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); - -@@ -1675,22 +1664,17 @@ - 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. --#if(0) -- masm.as_extsw(ScratchRegister, lhs); -- bailoutCmp32(Assembler::LessThan, ScratchRegister, Imm32(0), ins->snapshot()); --#else - bailoutCmp32(Assembler::LessThan, dest, Imm32(0), ins->snapshot()); --#endif - } - break; - default: - MOZ_CRASH("Unexpected shift op"); - } - } - } - -diff -r 3ac07c6a65bc -r bd8eea54a76b js/src/jit/ppc64/MacroAssembler-ppc64-inl.h ---- a/js/src/jit/ppc64/MacroAssembler-ppc64-inl.h Fri Sep 15 12:44:43 2023 -0700 -+++ b/js/src/jit/ppc64/MacroAssembler-ppc64-inl.h Wed Sep 20 13:08:34 2023 -0700 -@@ -369,21 +369,29 @@ - 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_mulhw(dest, ScratchRegister, src); -- x_sldi(dest, dest, 32); -+ 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); -diff -r 3ac07c6a65bc -r bd8eea54a76b js/src/jit/ppc64/MacroAssembler-ppc64.cpp ---- a/js/src/jit/ppc64/MacroAssembler-ppc64.cpp Fri Sep 15 12:44:43 2023 -0700 -+++ b/js/src/jit/ppc64/MacroAssembler-ppc64.cpp Wed Sep 20 13:08:34 2023 -0700 -@@ -4769,21 +4769,23 @@ - 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(temp != ScratchRegister); -- MOZ_ASSERT(temp != InvalidReg); -- -- ma_and(temp, ptr, Imm32(int32_t(~gc::ChunkMask))); -- branchPtr(InvertCondition(cond), Address(temp, gc::ChunkStoreBufferOffset), -+ 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); - } -diff -r 3ac07c6a65bc -r bd8eea54a76b js/src/wasm/WasmFrameIter.cpp ---- a/js/src/wasm/WasmFrameIter.cpp Fri Sep 15 12:44:43 2023 -0700 -+++ b/js/src/wasm/WasmFrameIter.cpp Wed Sep 20 13:08:34 2023 -0700 -@@ -1286,17 +1286,33 @@ - #elif defined(JS_CODEGEN_ARM) - if (offsetFromEntry == BeforePushRetAddr || codeRange->isThunk()) { - // The return address is still in lr and fp holds the caller's fp. - fixedPC = (uint8_t*)registers.lr; - fixedFP = fp; - AssertMatchesCallSite(fixedPC, fixedFP); - } else - #elif defined(JS_CODEGEN_PPC64) -- MOZ_ASSERT(0); -+ if (codeRange->isThunk()) { -+ // The FarJumpIsland sequence temporarily scrambles LR. -+ // Don't unwind to the caller. -+ fixedPC = pc; -+ fixedFP = fp; -+ *unwoundCaller = false; -+ AssertMatchesCallSite( -+ Frame::fromUntaggedWasmExitFP(fp)->returnAddress(), -+ Frame::fromUntaggedWasmExitFP(fp)->rawCaller()); -+ } else if (offsetFromEntry < PushedFP) { -+ // On ppc64 we rely on register state instead of state saved on -+ // stack until the wasm::Frame is completely built. -+ // On entry the return address is in LR and fp holds the caller's fp. -+ fixedPC = (uint8_t*)registers.lr; -+ fixedFP = fp; -+ AssertMatchesCallSite(fixedPC, fixedFP); -+ } else - #endif - if (offsetFromEntry == PushedRetAddr || codeRange->isThunk()) { - // The return address has been pushed on the stack but fp still - // points to the caller's fp. - fixedPC = sp[0]; - fixedFP = fp; - AssertMatchesCallSite(fixedPC, fixedFP); - } else if (offsetFromEntry == PushedFP) { diff --git a/firefox-esr/power9-jit-744153.diff b/firefox-esr/power9-jit-744153.diff deleted file mode 100644 index 16d26854ec..0000000000 --- a/firefox-esr/power9-jit-744153.diff +++ /dev/null @@ -1,236 +0,0 @@ -# HG changeset patch -# User Cameron Kaiser -# Date 1695318886 25200 -# Thu Sep 21 10:54:46 2023 -0700 -# Node ID 23890c8cfb6523602d62886442866799431e490d -# Parent bd8eea54a76bd887fd7741eb252ee8bc09bf79f2 -clean up more fails and unfunk wasm - -diff -r bd8eea54a76b -r 23890c8cfb65 js/src/builtin/TestingFunctions.cpp ---- a/js/src/builtin/TestingFunctions.cpp Wed Sep 20 13:08:34 2023 -0700 -+++ b/js/src/builtin/TestingFunctions.cpp Thu Sep 21 10:54:46 2023 -0700 -@@ -455,16 +455,25 @@ - value = BooleanValue(true); - #else - value = BooleanValue(false); - #endif - if (!JS_SetProperty(cx, info, "riscv64", value)) { - return false; - } - -+#ifdef JS_CODEGEN_PPC64 -+ value = BooleanValue(true); -+#else -+ value = BooleanValue(false); -+#endif -+ if (!JS_SetProperty(cx, info, "ppc64", value)) { -+ return false; -+ } -+ - #ifdef JS_SIMULATOR_RISCV64 - value = BooleanValue(true); - #else - value = BooleanValue(false); - #endif - if (!JS_SetProperty(cx, info, "riscv64-simulator", value)) { - return false; - } -diff -r bd8eea54a76b -r 23890c8cfb65 js/src/jit-test/tests/gc/gcparam.js ---- a/js/src/jit-test/tests/gc/gcparam.js Wed Sep 20 13:08:34 2023 -0700 -+++ b/js/src/jit-test/tests/gc/gcparam.js Thu Sep 21 10:54:46 2023 -0700 -@@ -25,17 +25,19 @@ - testGetParam("totalChunks"); - testGetParam("nurseryBytes"); - testGetParam("majorGCNumber"); - testGetParam("minorGCNumber"); - testGetParam("chunkBytes"); - testGetParam("helperThreadCount"); - - testChangeParam("maxBytes"); --testChangeParam("minNurseryBytes", 16 * 1024); -+// This cannot be lower than 64K due to 64K page systems, like some ppc64le -+// machines in Linux. -+testChangeParam("minNurseryBytes", 64 * 1024); - testChangeParam("maxNurseryBytes", 1024 * 1024); - testChangeParam("incrementalGCEnabled"); - testChangeParam("perZoneGCEnabled"); - testChangeParam("sliceTimeBudgetMS"); - testChangeParam("highFrequencyTimeLimit"); - testChangeParam("smallHeapSizeMax"); - testChangeParam("largeHeapSizeMin"); - testChangeParam("highFrequencySmallHeapGrowth"); -diff -r bd8eea54a76b -r 23890c8cfb65 js/src/jit-test/tests/gc/oomInRegExp2.js ---- a/js/src/jit-test/tests/gc/oomInRegExp2.js Wed Sep 20 13:08:34 2023 -0700 -+++ b/js/src/jit-test/tests/gc/oomInRegExp2.js Thu Sep 21 10:54:46 2023 -0700 -@@ -1,5 +1,6 @@ --// |jit-test| skip-if: !('oomTest' in this) -+// |jit-test| skip-if: !('oomTest' in this) || getBuildConfiguration().ppc64 -+// On ppc64, this will never exhaust memory before timing out. - - oomTest(() => assertEq("foobar\xff5baz\u1200".search(/bar\u0178\d/i), 3), {keepFailing: true}); - oomTest(() => assertEq((/(?!(?!(?!6)[\Wc]))/i).test(), false), {keepFailing: true}); - oomTest(() => assertEq((/bar\u0178\d/i).exec("foobar\xff5baz\u1200") != null, true), {keepFailing: true}); -diff -r bd8eea54a76b -r 23890c8cfb65 js/src/jit-test/tests/modules/bug1670236.js ---- a/js/src/jit-test/tests/modules/bug1670236.js Wed Sep 20 13:08:34 2023 -0700 -+++ b/js/src/jit-test/tests/modules/bug1670236.js Thu Sep 21 10:54:46 2023 -0700 -@@ -1,6 +1,8 @@ --// |jit-test| skip-if: !('oomTest' in this) -+// |jit-test| skip-if: !('oomTest' in this) || getBuildConfiguration().ppc64 -+// On ppc64, this will never exhaust memory before timing out. -+ - o0=r=/x/; - this.toString=(function() { - evaluate("",({ element:o0 })); - }) - oomTest(String.prototype.charCodeAt,{ keepFailing:true }) -diff -r bd8eea54a76b -r 23890c8cfb65 js/src/jit-test/tests/promise/unhandled-rejections-oom.js ---- a/js/src/jit-test/tests/promise/unhandled-rejections-oom.js Wed Sep 20 13:08:34 2023 -0700 -+++ b/js/src/jit-test/tests/promise/unhandled-rejections-oom.js Thu Sep 21 10:54:46 2023 -0700 -@@ -1,3 +1,4 @@ --// |jit-test| allow-oom; skip-if: !('oomTest' in this) -+// |jit-test| allow-oom; skip-if: !('oomTest' in this) || getBuildConfiguration().ppc64 -+// On ppc64, this will never exhaust memory before timing out. - - oomTest(async function() {}, { keepFailing: true }); -diff -r bd8eea54a76b -r 23890c8cfb65 js/src/jit/ppc64/Architecture-ppc64.h ---- a/js/src/jit/ppc64/Architecture-ppc64.h Wed Sep 20 13:08:34 2023 -0700 -+++ b/js/src/jit/ppc64/Architecture-ppc64.h Thu Sep 21 10:54:46 2023 -0700 -@@ -12,24 +12,38 @@ - - #include "jit/shared/Architecture-shared.h" - - #include "js/Utility.h" - - namespace js { - namespace jit { - --// Used to protect the stack from linkage area clobbers. Minimum size --// is 4 doublewords for SP, LR, CR and TOC. --static const uint32_t ShadowStackSpace = 32; -+// 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 PowerPC this is a single bl. -+// 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 - { -diff -r bd8eea54a76b -r 23890c8cfb65 js/src/jit/ppc64/MacroAssembler-ppc64.cpp ---- a/js/src/jit/ppc64/MacroAssembler-ppc64.cpp Wed Sep 20 13:08:34 2023 -0700 -+++ b/js/src/jit/ppc64/MacroAssembler-ppc64.cpp Thu Sep 21 10:54:46 2023 -0700 -@@ -4621,18 +4621,36 @@ - - 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); -- return call(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); - } -diff -r bd8eea54a76b -r 23890c8cfb65 js/src/wasm/WasmStubs.cpp ---- a/js/src/wasm/WasmStubs.cpp Wed Sep 20 13:08:34 2023 -0700 -+++ b/js/src/wasm/WasmStubs.cpp Thu Sep 21 10:54:46 2023 -0700 -@@ -2134,26 +2134,17 @@ - masm.storePtr(scratch, - Address(masm.getStackPointer(), i->offsetFromArgBase())); - } - i++; - MOZ_ASSERT(i.done()); - - // Make the call, test whether it succeeded, and extract the return value. - AssertStackAlignment(masm, ABIStackAlignment); --#ifdef JS_CODEGEN_PPC64 -- // Because this is calling an ABI-compliant function, we have to pull down -- // a dummy linkage area or the values on the stack will be stomped on. The -- // minimum size is sufficient. -- masm.as_addi(masm.getStackPointer(), masm.getStackPointer(), -32); --#endif - masm.call(SymbolicAddress::CallImport_General); --#ifdef JS_CODEGEN_PPC64 -- masm.as_addi(masm.getStackPointer(), masm.getStackPointer(), 32); --#endif - masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel); - - ResultType resultType = ResultType::Vector(funcType.results()); - ValType registerResultType; - for (ABIResultIter iter(resultType); !iter.done(); iter.next()) { - if (iter.cur().inRegister()) { - MOZ_ASSERT(!registerResultType.isValid()); - registerResultType = iter.cur().type(); -@@ -2680,19 +2671,24 @@ - // PushRegsInMask strips out the high lanes of the XMM registers in this case, - // while the singles will be stripped as they are aliased by the larger doubles. - static const LiveRegisterSet RegsToPreserve( - GeneralRegisterSet(Registers::AllMask & - ~(Registers::SetType(1) << Registers::StackPointer)), - FloatRegisterSet(FloatRegisters::AllMask)); - #elif defined(JS_CODEGEN_PPC64) - // Note that this includes no SPRs, since the JIT is unaware of them. -+// Since we ass-U-me that traps don't occur while LR (an SPR, not a GPR) is -+// live, then we can clobber it and don't have to push it anyway. - static const LiveRegisterSet RegsToPreserve( - GeneralRegisterSet(Registers::AllMask), - FloatRegisterSet(FloatRegisters::AllMask)); -+# ifdef ENABLE_WASM_SIMD -+# error "high lanes of SIMD registers need to be saved too." -+# endif - #else - static const LiveRegisterSet RegsToPreserve( - GeneralRegisterSet(0), FloatRegisterSet(FloatRegisters::AllDoubleMask)); - # ifdef ENABLE_WASM_SIMD - # error "no SIMD support" - # endif - #endif - diff --git a/firefox-esr/power9-jit-744154.diff b/firefox-esr/power9-jit-744154.diff deleted file mode 100644 index 389af4e3c9..0000000000 --- a/firefox-esr/power9-jit-744154.diff +++ /dev/null @@ -1,70 +0,0 @@ -# HG changeset patch -# User Cameron Kaiser -# Date 1695355123 25200 -# Thu Sep 21 20:58:43 2023 -0700 -# Node ID 1771d1807f7bfd16be4631b7485f010cfb64031d -# Parent 23890c8cfb6523602d62886442866799431e490d -last wasm fails fixed, passes jit_test and jstests - -diff -r 23890c8cfb65 -r 1771d1807f7b js/src/jit/ppc64/Assembler-ppc64.cpp ---- a/js/src/jit/ppc64/Assembler-ppc64.cpp Thu Sep 21 10:54:46 2023 -0700 -+++ b/js/src/jit/ppc64/Assembler-ppc64.cpp Thu Sep 21 20:58:43 2023 -0700 -@@ -38,32 +38,35 @@ - { - switch (type) { - case MIRType::Int32: - case MIRType::Int64: - case MIRType::Pointer: - case MIRType::RefOrNull: - case MIRType::StackResults: { - if (usedGPRs_ > 7) { -- MOZ_ASSERT(IsCompilingWasm(), "no stack corruption from GPR overflow kthxbye"); -+ // 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(IsCompilingWasm(), "no stack corruption from FPR overflow kthxbye"); -+ 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_++; -diff -r 23890c8cfb65 -r 1771d1807f7b js/src/jit/ppc64/MacroAssembler-ppc64.cpp ---- a/js/src/jit/ppc64/MacroAssembler-ppc64.cpp Thu Sep 21 10:54:46 2023 -0700 -+++ b/js/src/jit/ppc64/MacroAssembler-ppc64.cpp Thu Sep 21 20:58:43 2023 -0700 -@@ -2235,16 +2235,17 @@ - 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); - loadPtr(Address(sp, ResumeFromException::offsetOfTarget()), r12); - xs_mtctr(r12); - loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()), - FramePointer); diff --git a/firefox-esr/power9-jit-744155.diff b/firefox-esr/power9-jit-744155.diff deleted file mode 100644 index d96271869c..0000000000 --- a/firefox-esr/power9-jit-744155.diff +++ /dev/null @@ -1,61 +0,0 @@ -# HG changeset patch -# User Cameron Kaiser -# Date 1695355376 25200 -# Thu Sep 21 21:02:56 2023 -0700 -# Node ID 4404797bd39a18f98b2f1a2c65ffe079404c2ee6 -# Parent 1771d1807f7bfd16be4631b7485f010cfb64031d -ion and wasm back on in browser build - -diff -r 1771d1807f7b -r 4404797bd39a js/xpconnect/src/XPCJSContext.cpp ---- a/js/xpconnect/src/XPCJSContext.cpp Thu Sep 21 20:58:43 2023 -0700 -+++ b/js/xpconnect/src/XPCJSContext.cpp Thu Sep 21 21:02:56 2023 -0700 -@@ -895,18 +895,18 @@ - false); - JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_JIT_HINTS_ENABLE, false); - sSelfHostedUseSharedMemory = false; - } else { - JS_SetGlobalJitCompilerOption( - cx, JSJITCOMPILER_BASELINE_ENABLE, - StaticPrefs::javascript_options_baselinejit_DoNotUseDirectly()); - JS_SetGlobalJitCompilerOption( -- cx, JSJITCOMPILER_ION_ENABLE, false); // XXX -- //StaticPrefs::javascript_options_ion_DoNotUseDirectly()); -+ cx, JSJITCOMPILER_ION_ENABLE, -+ StaticPrefs::javascript_options_ion_DoNotUseDirectly()); - JS_SetGlobalJitCompilerOption(cx, - JSJITCOMPILER_JIT_TRUSTEDPRINCIPALS_ENABLE, - useJitForTrustedPrincipals); - JS_SetGlobalJitCompilerOption( - cx, JSJITCOMPILER_NATIVE_REGEXP_ENABLE, - StaticPrefs::javascript_options_native_regexp_DoNotUseDirectly()); - // Only enable the jit hints cache for the content process to avoid - // any possible jank or delays on the parent process. -diff -r 1771d1807f7b -r 4404797bd39a modules/libpref/init/all.js ---- a/modules/libpref/init/all.js Thu Sep 21 20:58:43 2023 -0700 -+++ b/modules/libpref/init/all.js Thu Sep 21 21:02:56 2023 -0700 -@@ -951,21 +951,21 @@ - // that are associated with other domains which have - // user interaction (even if they don't have user - // interaction directly). - pref("privacy.purge_trackers.consider_entity_list", false); - - pref("dom.event.contextmenu.enabled", true); - - pref("javascript.enabled", true); --pref("javascript.options.asmjs", false); --pref("javascript.options.wasm", false); --pref("javascript.options.wasm_trustedprincipals", false); -+pref("javascript.options.asmjs", true); -+pref("javascript.options.wasm", true); -+pref("javascript.options.wasm_trustedprincipals", true); - pref("javascript.options.wasm_verbose", false); --pref("javascript.options.wasm_baselinejit", false); -+pref("javascript.options.wasm_baselinejit", true); - - pref("javascript.options.parallel_parsing", true); - pref("javascript.options.source_pragmas", true); - - pref("javascript.options.asyncstack", true); - // Broadly capturing async stack data adds overhead that is only advisable for - // developers, so we only enable it when the devtools are open, by default. - pref("javascript.options.asyncstack_capture_debuggee_only", true);