mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-30 23:29:40 +00:00
1388 lines
50 KiB
C
1388 lines
50 KiB
C
|
/* -*- 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
|
||
|
|
||
|
/*
|
||
|
|
||
|
IonPower (C)2015 Contributors to TenFourFox. All rights reserved.
|
||
|
|
||
|
Authors: Cameron Kaiser <classilla@floodgap.com>
|
||
|
with thanks to Ben Stuhl and David Kilbridge
|
||
|
and the authors of the ARM and MIPS ports
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include "mozilla/ArrayUtils.h"
|
||
|
#include "mozilla/Attributes.h"
|
||
|
#include "mozilla/MathAlgorithms.h"
|
||
|
|
||
|
#include "jit/CompactBuffer.h"
|
||
|
#include "jit/IonCode.h"
|
||
|
#include "jit/JitCompartment.h"
|
||
|
#include "jit/JitSpewer.h"
|
||
|
#include "jit/osxppc/Architecture-ppc.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 MOZ_CONSTEXPR_VAR Register r0 = { Registers::r0 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r1 = { Registers::r1 };
|
||
|
static MOZ_CONSTEXPR_VAR Register sp = { Registers::r1 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r2 = { Registers::r2 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r3 = { Registers::r3 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r4 = { Registers::r4 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r5 = { Registers::r5 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r6 = { Registers::r6 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r7 = { Registers::r7 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r8 = { Registers::r8 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r9 = { Registers::r9 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r10 = { Registers::r10 };
|
||
|
static MOZ_CONSTEXPR_VAR Register lr = { Registers::lr }; /* LIE! */
|
||
|
static MOZ_CONSTEXPR_VAR Register r12 = { Registers::r12 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r13 = { Registers::r13 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r14 = { Registers::r14 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r15 = { Registers::r15 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r16 = { Registers::r16 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r17 = { Registers::r17 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r18 = { Registers::r18 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r19 = { Registers::r19 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r20 = { Registers::r20 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r21 = { Registers::r21 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r22 = { Registers::r22 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r23 = { Registers::r23 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r24 = { Registers::r24 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r25 = { Registers::r25 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r26 = { Registers::r26 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r27 = { Registers::r27 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r28 = { Registers::r28 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r29 = { Registers::r29 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r30 = { Registers::r30 };
|
||
|
static MOZ_CONSTEXPR_VAR Register r31 = { Registers::r31 };
|
||
|
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister f0 = { FloatRegisters::f0 };
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister f1 = { FloatRegisters::f1 };
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister f2 = { FloatRegisters::f2 };
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister f3 = { FloatRegisters::f3 };
|
||
|
// 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).
|
||
|
|
||
|
// Old JM holdovers for convenience.
|
||
|
static MOZ_CONSTEXPR_VAR Register stackPointerRegister = r1;
|
||
|
static MOZ_CONSTEXPR_VAR Register tempRegister = r0;
|
||
|
static MOZ_CONSTEXPR_VAR Register addressTempRegister = r12;
|
||
|
// The OS X ABI documentation recommends r2 for this, not r11 like we used to.
|
||
|
static MOZ_CONSTEXPR_VAR Register emergencyTempRegister = r2;
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister fpTempRegister = f0;
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister fpConversionRegister = f2;
|
||
|
|
||
|
// Use the same assignments as PPCBC for simplicity.
|
||
|
static MOZ_CONSTEXPR_VAR Register OsrFrameReg = r6;
|
||
|
static MOZ_CONSTEXPR_VAR Register ArgumentsRectifierReg = r19;
|
||
|
static MOZ_CONSTEXPR_VAR Register CallTempReg0 = r8;
|
||
|
static MOZ_CONSTEXPR_VAR Register CallTempReg1 = r9;
|
||
|
static MOZ_CONSTEXPR_VAR Register CallTempReg2 = r10;
|
||
|
static MOZ_CONSTEXPR_VAR Register CallTempReg3 = r7;
|
||
|
static MOZ_CONSTEXPR_VAR Register CallTempReg4 = r5; // Bad things! Try not to use these!
|
||
|
static MOZ_CONSTEXPR_VAR Register CallTempReg5 = r6;
|
||
|
|
||
|
// irregexp
|
||
|
static MOZ_CONSTEXPR_VAR Register IntArgReg0 = r3;
|
||
|
static MOZ_CONSTEXPR_VAR Register IntArgReg1 = r4;
|
||
|
static MOZ_CONSTEXPR_VAR Register IntArgReg2 = r5;
|
||
|
static MOZ_CONSTEXPR_VAR Register IntArgReg3 = r6;
|
||
|
|
||
|
static MOZ_CONSTEXPR_VAR Register GlobalReg = r20; // used by AsmJS. Allocatable, but non-volatile.
|
||
|
static MOZ_CONSTEXPR_VAR Register HeapReg = r21; // Ditto.
|
||
|
|
||
|
// These are defined, but not actually used, at least by us (see GetTempRegForIntArg).
|
||
|
static MOZ_CONSTEXPR_VAR 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_; }
|
||
|
|
||
|
static const Register NonArgReturnReg0;
|
||
|
static const Register NonArgReturnReg1;
|
||
|
static const Register NonArg_VolatileReg;
|
||
|
static const Register NonReturn_VolatileReg0;
|
||
|
static const Register NonReturn_VolatileReg1;
|
||
|
};
|
||
|
|
||
|
static MOZ_CONSTEXPR_VAR Register PreBarrierReg = r4;
|
||
|
|
||
|
static MOZ_CONSTEXPR_VAR Register InvalidReg = { Registers::invalid_reg };
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg;
|
||
|
|
||
|
static MOZ_CONSTEXPR_VAR Register StackPointer = sp;
|
||
|
static MOZ_CONSTEXPR_VAR Register FramePointer = InvalidReg;
|
||
|
|
||
|
// All return registers must be allocatable.
|
||
|
static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = r6;
|
||
|
static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = r5;
|
||
|
static MOZ_CONSTEXPR_VAR Register ReturnReg = r3;
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32Reg = f1;
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister ReturnDoubleReg = f1;
|
||
|
|
||
|
// Gawd, Mozilla. Must FPRs be vector registers in all your damn architectures?
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister ReturnSimdReg = InvalidFloatReg;
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister ReturnSimd128Reg = InvalidFloatReg;
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister ReturnInt32x4Reg = InvalidFloatReg;
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32x4Reg = InvalidFloatReg;
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister ScratchSimdReg = InvalidFloatReg;
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister ScratchSimd128Reg = InvalidFloatReg;
|
||
|
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloat32Reg = f0;
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister ScratchDoubleReg = f0;
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister SecondScratchFloat32Reg = f2;
|
||
|
static MOZ_CONSTEXPR_VAR FloatRegister SecondScratchDoubleReg = f2;
|
||
|
|
||
|
// 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 MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = r7;
|
||
|
static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = r3;
|
||
|
static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = r4;
|
||
|
static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = r5;
|
||
|
static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = r6;
|
||
|
|
||
|
// Registers used in the GenerateFFIIonExit Disable Activation block.
|
||
|
static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = JSReturnReg_Data;
|
||
|
static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = JSReturnReg_Type;
|
||
|
static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = r3;
|
||
|
static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = r4;
|
||
|
static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = r7;
|
||
|
|
||
|
// Try to favour word alignment, though we consider the ABI stack-smashed in Ion code.
|
||
|
static const uint32_t ABIStackAlignment = 4;
|
||
|
static const uint32_t CodeAlignment = 4;
|
||
|
// As of Mozilla 40, the JIT requires Value alignment at minimum, which is stupid and unnecessary.
|
||
|
static const uint32_t JitStackAlignment = 8;
|
||
|
static const uint32_t JitStackValueAlignment = 1;
|
||
|
|
||
|
// Future.
|
||
|
static const bool SupportsSimd = false;
|
||
|
static const uint32_t SimdStackAlignment = 16;
|
||
|
static const uint32_t SimdMemoryAlignment = 16; // damn AltiVec restrictions
|
||
|
static const uint32_t AsmJSStackAlignment = 16; // wtf wtf wtf
|
||
|
|
||
|
static const Scale ScalePointer = TimesFour;
|
||
|
|
||
|
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_addo = 0x7C000614, // add & OE=1 (can set OV)
|
||
|
PPC_addi = 0x38000000, // add immediate
|
||
|
PPC_addic = 0x30000000, // add immediate carrying
|
||
|
PPC_addis = 0x3C000000, // add immediate shifted
|
||
|
PPC_addme = 0x7C0001D4, // add -1 extended
|
||
|
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_cmpw = 0x7C000000, // compare
|
||
|
PPC_cmpwi = 0x2C000000, // compare immediate
|
||
|
PPC_cmplw = 0x7C000040, // compare logical
|
||
|
PPC_cmplwi = 0x28000000, // compare logical immediate
|
||
|
PPC_cntlzw = 0x7C000034, // 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_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_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_fcfid = 0xFC00069C, // floating convert from integer doubleword
|
||
|
PPC_fctiw = 0xFC00001C, // floating convert to integer (to -Inf)
|
||
|
PPC_fctiwz = 0xFC00001E, // floating convert to integer (to zero)
|
||
|
PPC_fcmpu = 0xFC000000, // floating compare unordered
|
||
|
PPC_fdiv = 0xFC000024, // floating divide (double precision)
|
||
|
PPC_fdivs = 0xEC000024, // floating divide (single precision)
|
||
|
PPC_fmr = 0xFC000090, // floating move register
|
||
|
PPC_fmul = 0xFC000032, // floating multiply (double precision)
|
||
|
PPC_fmuls = 0xEC000032, // floating multiply (single precision)
|
||
|
PPC_fneg = 0xFC000050, // floating negate
|
||
|
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 (G5 only) (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_lbz = 0x88000000, // load byte and zero
|
||
|
PPC_lbzx = 0x7C0000AE, // load byte and zero indexed
|
||
|
PPC_ld = 0xE8000000, // load doubleword
|
||
|
PPC_ldx = 0x7C00002A, // load doubleword indexed
|
||
|
PPC_lfd = 0xC8000000, // load floating point double
|
||
|
PPC_lfdx = 0x7C0004AE, // load floating-point double 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_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_mcrxr = 0x7C000400, // move XER[0-3] to CR[0-3] (G4 and earlier)
|
||
|
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_mffs = 0xFC00048E, // move from fpscr to fpr
|
||
|
PPC_mfspr = 0x7C0002A6, // move from spr (special purpose register)
|
||
|
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_mtspr = 0x7C0003A6, // move to spr
|
||
|
PPC_mulhw = 0x7C000096, // multiply high signed
|
||
|
PPC_mulhwu = 0x7C000016, // multiply high unsigned
|
||
|
PPC_mulli = 0x1C000000, // multiply low immediate
|
||
|
PPC_mullw = 0x7C0001D6, // multiply low word
|
||
|
PPC_mullwo = 0x7C0005D6, // multiply low word with overflow
|
||
|
PPC_nand = 0x7C0003B8, // nand
|
||
|
PPC_neg = 0x7C0000D0, // negate
|
||
|
PPC_nor = 0x7C0000F8, // nor
|
||
|
PPC_or_ = 0x7C000378, // or
|
||
|
PPC_ori = 0x60000000, // or immediate
|
||
|
PPC_oris = 0x64000000, // or immediate shifted
|
||
|
PPC_rlwimi = 0x50000000, // rotate left word imm then mask insert
|
||
|
PPC_rlwinm = 0x54000000, // rotate left word then and with mask
|
||
|
PPC_rldicl = 0x78000000, // rotate left doubleword immediate then clear left
|
||
|
PPC_rldicr = 0x78000004, // rotate left doubleword immediate then clear right
|
||
|
PPC_rldimi = 0x7800000C, // rotate left doubleword immediate then mask insert
|
||
|
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_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_stfs = 0xD0000000, // 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_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_subf = 0x7C000050, // subtract from
|
||
|
PPC_subfc = 0x7C000010, // subtract from with carry
|
||
|
PPC_subfe = 0x7C000110, // subtract from extended
|
||
|
PPC_subfze = 0x7C000190, // subtract from zero extended
|
||
|
PPC_subfo = 0x7C000450, // subtract from with overflow
|
||
|
#ifdef __APPLE__
|
||
|
PPC_trap = 0x7FE00008, // trap word (extended from tw 31,r0,r0)
|
||
|
#else
|
||
|
#error Specify the trap word for your PPC operating system
|
||
|
#endif
|
||
|
PPC_xor_ = 0x7C000278, // xor
|
||
|
PPC_xori = 0x68000000, // xor immediate
|
||
|
PPC_xoris = 0x6C000000, // xor immediate shifted
|
||
|
|
||
|
// simplified mnemonics
|
||
|
PPC_mr = PPC_or_,
|
||
|
PPC_not = PPC_nor,
|
||
|
PPC_nop = PPC_ori,
|
||
|
|
||
|
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 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!
|
||
|
}
|
||
|
|
||
|
void
|
||
|
PatchJump(CodeLocationJump &jump_, CodeLocationLabel label,
|
||
|
ReprotectCode reprotect = DontReprotect);
|
||
|
|
||
|
void
|
||
|
PatchBackedge(CodeLocationJump &jump_, CodeLocationLabel label, JitRuntime::BackedgeTarget target);
|
||
|
|
||
|
class Assembler;
|
||
|
typedef js::jit::AssemblerBuffer<1024, Instruction> PPCBuffer;
|
||
|
|
||
|
class PPCBufferWithExecutableCopy : public PPCBuffer
|
||
|
{
|
||
|
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();
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class Assembler : public AssemblerShared
|
||
|
{
|
||
|
public:
|
||
|
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
|
||
|
|
||
|
// XER-only codes. We need to have XER in the CR using mcrxr or
|
||
|
// an equivalent first, but we don't need to check CR itself.
|
||
|
// This is a synthetic code.
|
||
|
ConditionOnlyXER = 0x200, // Computation only
|
||
|
ConditionXERCA = 0x22c, // same as EQ bit
|
||
|
ConditionXERNCA = 0x224,
|
||
|
ConditionXEROV = 0x21c, // 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,
|
||
|
Overflow = ConditionXEROV,
|
||
|
Signed = LessThan,
|
||
|
NotSigned = GreaterThan,
|
||
|
Zero = Equal,
|
||
|
NonZero = NotEqual,
|
||
|
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 LinkBit {
|
||
|
DontLinkB = 0,
|
||
|
LinkB = 1,
|
||
|
};
|
||
|
|
||
|
enum LikelyBit {
|
||
|
NotLikelyB = 0,
|
||
|
LikelyB = 1,
|
||
|
};
|
||
|
|
||
|
enum BranchAddressType {
|
||
|
RelativeBranch = 0,
|
||
|
AbsoluteBranch = 2,
|
||
|
};
|
||
|
|
||
|
BufferOffset nextOffset() {
|
||
|
return m_buffer.nextOffset();
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
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;
|
||
|
Relocation::Kind kind;
|
||
|
|
||
|
RelativePatch(BufferOffset offset, void *target, Relocation::Kind kind)
|
||
|
: offset(offset),
|
||
|
target(target),
|
||
|
kind(kind)
|
||
|
{ }
|
||
|
};
|
||
|
|
||
|
js::Vector<CodeLabel, 0, SystemAllocPolicy> codeLabels_;
|
||
|
js::Vector<RelativePatch, 8, SystemAllocPolicy> jumps_;
|
||
|
js::Vector<uint32_t, 8, SystemAllocPolicy> longJumps_;
|
||
|
|
||
|
CompactBufferWriter jumpRelocations_;
|
||
|
CompactBufferWriter dataRelocations_;
|
||
|
CompactBufferWriter relocations_;
|
||
|
CompactBufferWriter preBarriers_;
|
||
|
|
||
|
PPCBufferWithExecutableCopy m_buffer;
|
||
|
|
||
|
private:
|
||
|
char const * nGPR(Register rreg)
|
||
|
{
|
||
|
uint32_t reg = rreg.code();
|
||
|
MOZ_ASSERT(reg <= 31);
|
||
|
//MOZ_ASSERT(reg >= 0);
|
||
|
static char const *names[] = {
|
||
|
"r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7",
|
||
|
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
||
|
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
||
|
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"
|
||
|
};
|
||
|
return names[reg];
|
||
|
}
|
||
|
|
||
|
char const * nFPR(FloatRegister freg)
|
||
|
{
|
||
|
uint32_t reg = freg.code();
|
||
|
MOZ_ASSERT(reg <= 31);
|
||
|
//MOZ_ASSERT(reg >= 0);
|
||
|
static char const *names[] = {
|
||
|
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
|
||
|
"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
|
||
|
"f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
|
||
|
"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31"
|
||
|
};
|
||
|
return names[reg];
|
||
|
}
|
||
|
|
||
|
char const * nCR(CRegisterID reg)
|
||
|
{
|
||
|
MOZ_ASSERT(reg <= 7);
|
||
|
MOZ_ASSERT(reg >= 0);
|
||
|
static char const *names[] = {
|
||
|
"cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7"
|
||
|
};
|
||
|
return names[reg];
|
||
|
}
|
||
|
|
||
|
char const * nSPR(SPRegisterID reg)
|
||
|
{
|
||
|
// XXX: we don't handle VRSAVE with this, but we don't use it yet.
|
||
|
MOZ_ASSERT(reg >= 1);
|
||
|
MOZ_ASSERT(reg <= 9);
|
||
|
static char const *names[] = {
|
||
|
"", "xer", "", "", "", "", "", "", "lr", "ctr"
|
||
|
};
|
||
|
return names[reg];
|
||
|
}
|
||
|
|
||
|
public: // used by MacroAssembler
|
||
|
// Which absolute bit number does a condition register + Condition pair
|
||
|
// refer to?
|
||
|
static uint8_t crBit(CRegisterID cr, Condition cond)
|
||
|
{
|
||
|
return (cr << 2) + ((cond & 0xf0) >> 4);
|
||
|
}
|
||
|
|
||
|
static uint8_t crBit(CRegisterID cr, DoubleCondition cond)
|
||
|
{
|
||
|
return (cr << 2) + ((cond & 0xf0) >> 4);
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
Assembler()
|
||
|
: m_buffer(),
|
||
|
isFinished(false)
|
||
|
{ }
|
||
|
|
||
|
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());
|
||
|
}
|
||
|
|
||
|
// 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());
|
||
|
}
|
||
|
}
|
||
|
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();
|
||
|
void executableCopy(void *buffer);
|
||
|
void copyJumpRelocationTable(uint8_t *dest);
|
||
|
void copyDataRelocationTable(uint8_t *dest);
|
||
|
void copyPreBarrierTable(uint8_t *dest);
|
||
|
|
||
|
void addCodeLabel(CodeLabel label);
|
||
|
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);
|
||
|
uint32_t computeConditionCode(Condition op, CRegisterID cr = cr0);
|
||
|
uint32_t computeConditionCode(DoubleCondition op, CRegisterID cr = cr0);
|
||
|
|
||
|
// To preserve compatibility with older code, opcode functions emit raw instructions
|
||
|
// (i.e., no as_* prefix).
|
||
|
BufferOffset x_nop();
|
||
|
|
||
|
// Branch and jump instructions.
|
||
|
BufferOffset b(JOffImm26 off, BranchAddressType bat = RelativeBranch, LinkBit lb = DontLinkB);
|
||
|
BufferOffset b(int32_t off, BranchAddressType bat = RelativeBranch, LinkBit lb = DontLinkB); // stubs into the above
|
||
|
BufferOffset blr(LinkBit lb = DontLinkB);
|
||
|
BufferOffset bctr(LinkBit lb = DontLinkB);
|
||
|
|
||
|
// Conditional branches.
|
||
|
BufferOffset bc(BOffImm16 off, Condition cond, CRegisterID cr = cr0, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
||
|
BufferOffset bc(int16_t off, Condition cond, CRegisterID cr = cr0, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
||
|
BufferOffset bc(BOffImm16 off, DoubleCondition cond, CRegisterID = cr0, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
||
|
BufferOffset bc(int16_t off, DoubleCondition cond, CRegisterID = cr0, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
||
|
BufferOffset bcctr(Condition cond, CRegisterID cr = cr0, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
||
|
BufferOffset bcctr(DoubleCondition cond, CRegisterID cr = cr0, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
||
|
|
||
|
BufferOffset bc(int16_t off, uint32_t op, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
||
|
BufferOffset bcctr(uint32_t op, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
||
|
|
||
|
// SPR operations.
|
||
|
BufferOffset mtspr(SPRegisterID spr, Register ra);
|
||
|
BufferOffset mfspr(Register rd, SPRegisterID spr);
|
||
|
|
||
|
// CR operations.
|
||
|
#define DEF_CRCR(op) BufferOffset 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 mtcrf(uint32_t mask, Register rs);
|
||
|
BufferOffset mfcr(Register rd);
|
||
|
BufferOffset mfocrf(Register rd, CRegisterID crfs); // G5 only
|
||
|
BufferOffset x_mcrxr(CRegisterID crt, Register temp = r12); // emulated on G5, EEEK!
|
||
|
|
||
|
// GPR operations and load-stores.
|
||
|
BufferOffset neg(Register rd, Register rs);
|
||
|
|
||
|
BufferOffset cmpw(CRegisterID cr, Register ra, Register rb);
|
||
|
BufferOffset cmpwi(CRegisterID cr, Register ra, int16_t im);
|
||
|
BufferOffset cmplw(CRegisterID cr, Register ra, Register rb);
|
||
|
BufferOffset cmplwi(CRegisterID cr, Register ra, int16_t im);
|
||
|
BufferOffset cmpw(Register ra, Register rb); // implied cr0
|
||
|
BufferOffset cmpwi(Register ra, int16_t im);
|
||
|
BufferOffset cmplw(Register ra, Register rb);
|
||
|
BufferOffset cmplwi(Register ra, int16_t im);
|
||
|
|
||
|
BufferOffset srawi(Register id, Register rs, uint8_t n);
|
||
|
|
||
|
BufferOffset rlwinm(Register rd, Register rs, uint8_t sh, uint8_t mb, uint8_t me);
|
||
|
BufferOffset rlwimi(Register rd, Register rs, uint8_t sh, uint8_t mb, uint8_t me); // cracked on G5
|
||
|
|
||
|
#define DEF_ALU2(op) BufferOffset op(Register rd, Register ra, Register rb); \
|
||
|
BufferOffset 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(divw)
|
||
|
DEF_ALU2(divwo)
|
||
|
DEF_ALU2(divwu)
|
||
|
DEF_ALU2(divwuo)
|
||
|
DEF_ALU2(mullw)
|
||
|
DEF_ALU2(mulhw)
|
||
|
DEF_ALU2(mulhwu)
|
||
|
DEF_ALU2(mullwo)
|
||
|
DEF_ALU2(eqv) // NB: Implemented differently.
|
||
|
#undef DEF_ALU2
|
||
|
|
||
|
#define DEF_ALUI(op) BufferOffset op(Register rd, Register ra, int16_t im); \
|
||
|
BufferOffset op##_rc(Register rd, Register ra, int16_t im);
|
||
|
DEF_ALUI(addi)
|
||
|
DEF_ALUI(addic)
|
||
|
DEF_ALUI(addis)
|
||
|
// NB: mulli is usually strength-reduced, since it can take up to five
|
||
|
// cycles in the worst case. See x_sr_mulli.
|
||
|
DEF_ALUI(mulli)
|
||
|
#undef DEF_ALUI
|
||
|
|
||
|
#define DEF_ALUE(op) BufferOffset op(Register rd, Register ra); \
|
||
|
BufferOffset 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.
|
||
|
#undef DEF_ALUE
|
||
|
|
||
|
#define DEF_BITALU2(op) BufferOffset op(Register rd, Register rs, Register rb); \
|
||
|
BufferOffset 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 op(Register rd, Register ra, uint16_t im); \
|
||
|
BufferOffset op##_rc(Register rd, Register ra, uint16_t im);
|
||
|
DEF_BITALUI(ori)
|
||
|
DEF_BITALUI(oris)
|
||
|
DEF_BITALUI(xori)
|
||
|
DEF_BITALUI(xoris)
|
||
|
// There is no Rc-less version of andi/andis.
|
||
|
BufferOffset andi_rc(Register rd, Register ra, uint16_t im);
|
||
|
BufferOffset andis_rc(Register rd, Register ra, uint16_t im);
|
||
|
#undef DEF_BITALUI
|
||
|
|
||
|
#define DEF_ALUEXT(op) BufferOffset op(Register rd, Register rs); \
|
||
|
BufferOffset op##_rc(Register rd, Register rs);
|
||
|
DEF_ALUEXT(extsb)
|
||
|
DEF_ALUEXT(extsh)
|
||
|
DEF_ALUEXT(extsw)
|
||
|
#undef DEF_ALUEXT
|
||
|
|
||
|
#define DEF_MEMd(op) BufferOffset op(Register rd, Register rb, int16_t off);
|
||
|
DEF_MEMd(lbz)
|
||
|
DEF_MEMd(lha)
|
||
|
DEF_MEMd(lhz)
|
||
|
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 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(ldx)
|
||
|
|
||
|
DEF_MEMx(stbx)
|
||
|
DEF_MEMx(stwx)
|
||
|
DEF_MEMx(stwux)
|
||
|
DEF_MEMx(stwbrx)
|
||
|
DEF_MEMx(sthx)
|
||
|
DEF_MEMx(sthbrx)
|
||
|
DEF_MEMx(stdx)
|
||
|
DEF_MEMx(stdux)
|
||
|
#undef DEF_MEMx
|
||
|
|
||
|
// FPR operations and load-stores.
|
||
|
BufferOffset fcmpu(CRegisterID cr, FloatRegister ra, FloatRegister rb);
|
||
|
BufferOffset fcmpu(FloatRegister ra, FloatRegister rb); // implied cr0
|
||
|
#define DEF_FPUAC(op) BufferOffset op(FloatRegister rd, FloatRegister ra, FloatRegister rc); \
|
||
|
BufferOffset op##_rc(FloatRegister rd, FloatRegister ra, FloatRegister rc);
|
||
|
DEF_FPUAC(fmul)
|
||
|
DEF_FPUAC(fmuls)
|
||
|
#undef DEF_FPUAC
|
||
|
|
||
|
#define DEF_FPUAB(op) BufferOffset op(FloatRegister rd, FloatRegister ra, FloatRegister rc); \
|
||
|
BufferOffset 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)
|
||
|
#undef DEF_FPUAB
|
||
|
|
||
|
#define DEF_FPUDS(op) BufferOffset op(FloatRegister rd, FloatRegister rs); \
|
||
|
BufferOffset op##_rc(FloatRegister rd, FloatRegister rs);
|
||
|
DEF_FPUDS(fabs)
|
||
|
DEF_FPUDS(fneg)
|
||
|
DEF_FPUDS(fmr)
|
||
|
DEF_FPUDS(fcfid)
|
||
|
DEF_FPUDS(fctiw)
|
||
|
DEF_FPUDS(fctiwz)
|
||
|
DEF_FPUDS(frsp)
|
||
|
DEF_FPUDS(frsqrte)
|
||
|
|
||
|
// G5 only
|
||
|
DEF_FPUDS(fsqrt)
|
||
|
#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 op(FloatRegister rd, FloatRegister ra, FloatRegister rc, FloatRegister rb); \
|
||
|
BufferOffset 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 op(FloatRegister rd, Register rb, int16_t off);
|
||
|
DEF_FMEMd(lfd)
|
||
|
DEF_FMEMd(lfs)
|
||
|
DEF_FMEMd(stfd)
|
||
|
DEF_FMEMd(stfs)
|
||
|
DEF_FMEMd(stfdu)
|
||
|
#undef DEF_FMEMd
|
||
|
|
||
|
#define DEF_FMEMx(op) BufferOffset op(FloatRegister rd, Register ra, Register rb);
|
||
|
DEF_FMEMx(lfdx)
|
||
|
DEF_FMEMx(lfsx)
|
||
|
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 mtfsb0(uint8_t bt);
|
||
|
BufferOffset mtfsb1(uint8_t bt);
|
||
|
BufferOffset mtfsfi(uint8_t fi, uint8_t imm);
|
||
|
BufferOffset mcrf(CRegisterID bt, CRegisterID bs);
|
||
|
BufferOffset mcrfs(CRegisterID bf, uint8_t bfa);
|
||
|
|
||
|
// Conveniences and generally accepted alternate mnemonics.
|
||
|
BufferOffset x_trap();
|
||
|
BufferOffset x_mtrap(); // Codegen for marking traps in output.
|
||
|
BufferOffset x_mr(Register rd, Register ra);
|
||
|
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 x_bdnz(int16_t off, LikelyBit lkb = NotLikelyB, LinkBit lb = DontLinkB);
|
||
|
BufferOffset x_mtctr(Register ra);
|
||
|
BufferOffset x_mtlr(Register ra);
|
||
|
BufferOffset x_mflr(Register rd);
|
||
|
BufferOffset x_mtcr(Register rs);
|
||
|
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_srwi(Register rd, Register rs, int n);
|
||
|
BufferOffset x_subi(Register rd, Register ra, int16_t im);
|
||
|
BufferOffset x_sr_mulli(Register rd, Register ra, int16_t im);
|
||
|
|
||
|
// Large loads.
|
||
|
BufferOffset x_li(Register rd, int16_t im);
|
||
|
BufferOffset x_lis(Register rd, int16_t im);
|
||
|
BufferOffset x_p_li32(Register rd, int32_t im);
|
||
|
BufferOffset x_li32(Register rd, int32_t im);
|
||
|
|
||
|
// Label operations.
|
||
|
void bind(Label *label, BufferOffset boff = BufferOffset());
|
||
|
void bind(RepatchLabel *label);
|
||
|
void bind(CodeOffset *label) { label->bind(currentOffset()); }
|
||
|
uint32_t currentOffset() {
|
||
|
return nextOffset().getOffset();
|
||
|
}
|
||
|
void retarget(Label *label, Label *target);
|
||
|
void Bind(uint8_t *rawCode, CodeOffset *label, const void *address);
|
||
|
|
||
|
// 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);
|
||
|
*/
|
||
|
|
||
|
static bool SupportsFloatingPoint() {
|
||
|
return true;
|
||
|
}
|
||
|
static bool SupportsSimd() {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
void bind(InstImm *inst, uint32_t branch, uint32_t target);
|
||
|
void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) {
|
||
|
enoughMemory_ &= jumps_.append(RelativePatch(src, target.value, kind));
|
||
|
if (kind == Relocation::JITCODE)
|
||
|
writeRelocation(src);
|
||
|
}
|
||
|
|
||
|
void addLongJump(BufferOffset src) {
|
||
|
enoughMemory_ &= longJumps_.append(src.getOffset());
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
size_t numLongJumps() const {
|
||
|
return longJumps_.length();
|
||
|
}
|
||
|
uint32_t longJump(size_t i) {
|
||
|
return longJumps_[i];
|
||
|
}
|
||
|
|
||
|
// 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);
|
||
|
|
||
|
bool bailed() {
|
||
|
return m_buffer.bail();
|
||
|
}
|
||
|
|
||
|
void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end,
|
||
|
const Disassembler::HeapAccess& heapAccess)
|
||
|
{
|
||
|
// Implement this if we implement a disassembler.
|
||
|
}
|
||
|
|
||
|
// Blah
|
||
|
bool asmMergeWith(const Assembler& other) { MOZ_CRASH(); return false; }
|
||
|
void retargetWithOffset(size_t baseOffset, const LabelBase* label,
|
||
|
Label* target) { MOZ_CRASH(); }
|
||
|
}; // 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();
|
||
|
|
||
|
// 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 InstImm : public Instruction
|
||
|
{
|
||
|
// Valid for reg/reg/imm instructions only and bc.
|
||
|
// XXX: Assert that at some point.
|
||
|
public:
|
||
|
InstImm (PPCOpcodes op) : Instruction(op) { }
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}; // 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
|
||
|
}
|
||
|
|
||
|
// For possible future expansion.
|
||
|
static const uint32_t PPC_STANZA_LENGTH = 16;
|
||
|
|
||
|
} // 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 */
|