michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef jit_mips_Assembler_mips_h michael@0: #define jit_mips_Assembler_mips_h michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/MathAlgorithms.h" michael@0: michael@0: #include "jit/CompactBuffer.h" michael@0: #include "jit/IonCode.h" michael@0: #include "jit/IonSpewer.h" michael@0: #include "jit/mips/Architecture-mips.h" michael@0: #include "jit/shared/Assembler-shared.h" michael@0: #include "jit/shared/IonAssemblerBuffer.h" michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: static MOZ_CONSTEXPR_VAR Register zero = { Registers::zero }; michael@0: static MOZ_CONSTEXPR_VAR Register at = { Registers::at }; michael@0: static MOZ_CONSTEXPR_VAR Register v0 = { Registers::v0 }; michael@0: static MOZ_CONSTEXPR_VAR Register v1 = { Registers::v1 }; michael@0: static MOZ_CONSTEXPR_VAR Register a0 = { Registers::a0 }; michael@0: static MOZ_CONSTEXPR_VAR Register a1 = { Registers::a1 }; michael@0: static MOZ_CONSTEXPR_VAR Register a2 = { Registers::a2 }; michael@0: static MOZ_CONSTEXPR_VAR Register a3 = { Registers::a3 }; michael@0: static MOZ_CONSTEXPR_VAR Register t0 = { Registers::t0 }; michael@0: static MOZ_CONSTEXPR_VAR Register t1 = { Registers::t1 }; michael@0: static MOZ_CONSTEXPR_VAR Register t2 = { Registers::t2 }; michael@0: static MOZ_CONSTEXPR_VAR Register t3 = { Registers::t3 }; michael@0: static MOZ_CONSTEXPR_VAR Register t4 = { Registers::t4 }; michael@0: static MOZ_CONSTEXPR_VAR Register t5 = { Registers::t5 }; michael@0: static MOZ_CONSTEXPR_VAR Register t6 = { Registers::t6 }; michael@0: static MOZ_CONSTEXPR_VAR Register t7 = { Registers::t7 }; michael@0: static MOZ_CONSTEXPR_VAR Register s0 = { Registers::s0 }; michael@0: static MOZ_CONSTEXPR_VAR Register s1 = { Registers::s1 }; michael@0: static MOZ_CONSTEXPR_VAR Register s2 = { Registers::s2 }; michael@0: static MOZ_CONSTEXPR_VAR Register s3 = { Registers::s3 }; michael@0: static MOZ_CONSTEXPR_VAR Register s4 = { Registers::s4 }; michael@0: static MOZ_CONSTEXPR_VAR Register s5 = { Registers::s5 }; michael@0: static MOZ_CONSTEXPR_VAR Register s6 = { Registers::s6 }; michael@0: static MOZ_CONSTEXPR_VAR Register s7 = { Registers::s7 }; michael@0: static MOZ_CONSTEXPR_VAR Register t8 = { Registers::t8 }; michael@0: static MOZ_CONSTEXPR_VAR Register t9 = { Registers::t9 }; michael@0: static MOZ_CONSTEXPR_VAR Register k0 = { Registers::k0 }; michael@0: static MOZ_CONSTEXPR_VAR Register k1 = { Registers::k1 }; michael@0: static MOZ_CONSTEXPR_VAR Register gp = { Registers::gp }; michael@0: static MOZ_CONSTEXPR_VAR Register sp = { Registers::sp }; michael@0: static MOZ_CONSTEXPR_VAR Register fp = { Registers::fp }; michael@0: static MOZ_CONSTEXPR_VAR Register ra = { Registers::ra }; michael@0: michael@0: static MOZ_CONSTEXPR_VAR Register ScratchRegister = at; michael@0: static MOZ_CONSTEXPR_VAR Register SecondScratchReg = t8; michael@0: michael@0: // Use arg reg from EnterJIT function as OsrFrameReg. michael@0: static MOZ_CONSTEXPR_VAR Register OsrFrameReg = a3; michael@0: static MOZ_CONSTEXPR_VAR Register ArgumentsRectifierReg = s3; michael@0: static MOZ_CONSTEXPR_VAR Register CallTempReg0 = t0; michael@0: static MOZ_CONSTEXPR_VAR Register CallTempReg1 = t1; michael@0: static MOZ_CONSTEXPR_VAR Register CallTempReg2 = t2; michael@0: static MOZ_CONSTEXPR_VAR Register CallTempReg3 = t3; michael@0: static MOZ_CONSTEXPR_VAR Register CallTempReg4 = t4; michael@0: static MOZ_CONSTEXPR_VAR Register CallTempReg5 = t5; michael@0: michael@0: static MOZ_CONSTEXPR_VAR Register IntArgReg0 = a0; michael@0: static MOZ_CONSTEXPR_VAR Register IntArgReg1 = a1; michael@0: static MOZ_CONSTEXPR_VAR Register IntArgReg2 = a2; michael@0: static MOZ_CONSTEXPR_VAR Register IntArgReg3 = a3; michael@0: static MOZ_CONSTEXPR_VAR Register GlobalReg = s6; // used by Odin michael@0: static MOZ_CONSTEXPR_VAR Register HeapReg = s7; // used by Odin michael@0: static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = { t0, t1, t2, t3, t4 }; michael@0: static const uint32_t NumCallTempNonArgRegs = mozilla::ArrayLength(CallTempNonArgRegs); michael@0: michael@0: class ABIArgGenerator michael@0: { michael@0: unsigned usedArgSlots_; michael@0: bool firstArgFloat; michael@0: ABIArg current_; michael@0: michael@0: public: michael@0: ABIArgGenerator(); michael@0: ABIArg next(MIRType argType); michael@0: ABIArg ¤t() { return current_; } michael@0: michael@0: uint32_t stackBytesConsumedSoFar() const { michael@0: if (usedArgSlots_ <= 4) michael@0: return 4 * sizeof(intptr_t); michael@0: michael@0: return usedArgSlots_ * sizeof(intptr_t); michael@0: } michael@0: michael@0: static const Register NonArgReturnVolatileReg0; michael@0: static const Register NonArgReturnVolatileReg1; michael@0: }; michael@0: michael@0: static MOZ_CONSTEXPR_VAR Register PreBarrierReg = a1; michael@0: michael@0: static MOZ_CONSTEXPR_VAR Register InvalidReg = { Registers::invalid_reg }; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg = { FloatRegisters::invalid_freg }; michael@0: michael@0: static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = v1; michael@0: static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = v0; michael@0: static MOZ_CONSTEXPR_VAR Register StackPointer = sp; michael@0: static MOZ_CONSTEXPR_VAR Register FramePointer = fp; michael@0: static MOZ_CONSTEXPR_VAR Register ReturnReg = v0; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloatReg = { FloatRegisters::f0 }; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloatReg = { FloatRegisters::f18 }; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister SecondScratchFloatReg = { FloatRegisters::f16 }; michael@0: michael@0: static MOZ_CONSTEXPR_VAR FloatRegister NANReg = { FloatRegisters::f30 }; michael@0: michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f0 = {FloatRegisters::f0}; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f2 = {FloatRegisters::f2}; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f4 = {FloatRegisters::f4}; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f6 = {FloatRegisters::f6}; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f8 = {FloatRegisters::f8}; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f10 = {FloatRegisters::f10}; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f12 = {FloatRegisters::f12}; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f14 = {FloatRegisters::f14}; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f16 = {FloatRegisters::f16}; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f18 = {FloatRegisters::f18}; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f20 = {FloatRegisters::f20}; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f22 = {FloatRegisters::f22}; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f24 = {FloatRegisters::f24}; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f26 = {FloatRegisters::f26}; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f28 = {FloatRegisters::f28}; michael@0: static MOZ_CONSTEXPR_VAR FloatRegister f30 = {FloatRegisters::f30}; michael@0: michael@0: // MIPS CPUs can only load multibyte data that is "naturally" michael@0: // four-byte-aligned, sp register should be eight-byte-aligned. michael@0: static const uint32_t StackAlignment = 8; michael@0: static const uint32_t CodeAlignment = 4; michael@0: static const bool StackKeptAligned = true; michael@0: // NativeFrameSize is the size of return adress on stack in AsmJS functions. michael@0: static const uint32_t NativeFrameSize = sizeof(void*); michael@0: static const uint32_t AlignmentAtPrologue = 0; michael@0: static const uint32_t AlignmentMidPrologue = NativeFrameSize; michael@0: michael@0: static const Scale ScalePointer = TimesFour; michael@0: michael@0: // MIPS instruction types michael@0: // +---------------------------------------------------------------+ michael@0: // | 6 | 5 | 5 | 5 | 5 | 6 | michael@0: // +---------------------------------------------------------------+ michael@0: // Register type | Opcode | Rs | Rt | Rd | Sa | Function | michael@0: // +---------------------------------------------------------------+ michael@0: // | 6 | 5 | 5 | 16 | michael@0: // +---------------------------------------------------------------+ michael@0: // Immediate type | Opcode | Rs | Rt | 2's complement constant | michael@0: // +---------------------------------------------------------------+ michael@0: // | 6 | 26 | michael@0: // +---------------------------------------------------------------+ michael@0: // Jump type | Opcode | jump_target | michael@0: // +---------------------------------------------------------------+ michael@0: // 31 bit bit 0 michael@0: michael@0: // MIPS instruction encoding constants. michael@0: static const uint32_t OpcodeShift = 26; michael@0: static const uint32_t OpcodeBits = 6; michael@0: static const uint32_t RSShift = 21; michael@0: static const uint32_t RSBits = 5; michael@0: static const uint32_t RTShift = 16; michael@0: static const uint32_t RTBits = 5; michael@0: static const uint32_t RDShift = 11; michael@0: static const uint32_t RDBits = 5; michael@0: static const uint32_t SAShift = 6; michael@0: static const uint32_t SABits = 5; michael@0: static const uint32_t FunctionShift = 0; michael@0: static const uint32_t FunctionBits = 5; michael@0: static const uint32_t Imm16Shift = 0; michael@0: static const uint32_t Imm16Bits = 16; michael@0: static const uint32_t Imm26Shift = 0; michael@0: static const uint32_t Imm26Bits = 26; michael@0: static const uint32_t Imm28Shift = 0; michael@0: static const uint32_t Imm28Bits = 28; michael@0: static const uint32_t ImmFieldShift = 2; michael@0: static const uint32_t FccMask = 0x7; michael@0: static const uint32_t FccShift = 2; michael@0: michael@0: michael@0: // MIPS instruction field bit masks. michael@0: static const uint32_t OpcodeMask = ((1 << OpcodeBits) - 1) << OpcodeShift; michael@0: static const uint32_t Imm16Mask = ((1 << Imm16Bits) - 1) << Imm16Shift; michael@0: static const uint32_t Imm26Mask = ((1 << Imm26Bits) - 1) << Imm26Shift; michael@0: static const uint32_t Imm28Mask = ((1 << Imm28Bits) - 1) << Imm28Shift; michael@0: static const uint32_t RSMask = ((1 << RSBits) - 1) << RSShift; michael@0: static const uint32_t RTMask = ((1 << RTBits) - 1) << RTShift; michael@0: static const uint32_t RDMask = ((1 << RDBits) - 1) << RDShift; michael@0: static const uint32_t SAMask = ((1 << SABits) - 1) << SAShift; michael@0: static const uint32_t FunctionMask = ((1 << FunctionBits) - 1) << FunctionShift; michael@0: static const uint32_t RegMask = Registers::Total - 1; michael@0: static const uint32_t StackAlignmentMask = StackAlignment - 1; michael@0: michael@0: static const int32_t MAX_BREAK_CODE = 1024 - 1; michael@0: michael@0: class Instruction; michael@0: class InstReg; michael@0: class InstImm; michael@0: class InstJump; michael@0: class BranchInstBlock; michael@0: michael@0: uint32_t RS(Register r); michael@0: uint32_t RT(Register r); michael@0: uint32_t RT(uint32_t regCode); michael@0: uint32_t RT(FloatRegister r); michael@0: uint32_t RD(Register r); michael@0: uint32_t RD(FloatRegister r); michael@0: uint32_t RD(uint32_t regCode); michael@0: uint32_t SA(uint32_t value); michael@0: uint32_t SA(FloatRegister r); michael@0: michael@0: Register toRS (Instruction &i); michael@0: Register toRT (Instruction &i); michael@0: Register toRD (Instruction &i); michael@0: Register toR (Instruction &i); michael@0: michael@0: // MIPS enums for instruction fields michael@0: enum Opcode { michael@0: op_special = 0 << OpcodeShift, michael@0: op_regimm = 1 << OpcodeShift, michael@0: michael@0: op_j = 2 << OpcodeShift, michael@0: op_jal = 3 << OpcodeShift, michael@0: op_beq = 4 << OpcodeShift, michael@0: op_bne = 5 << OpcodeShift, michael@0: op_blez = 6 << OpcodeShift, michael@0: op_bgtz = 7 << OpcodeShift, michael@0: michael@0: op_addi = 8 << OpcodeShift, michael@0: op_addiu = 9 << OpcodeShift, michael@0: op_slti = 10 << OpcodeShift, michael@0: op_sltiu = 11 << OpcodeShift, michael@0: op_andi = 12 << OpcodeShift, michael@0: op_ori = 13 << OpcodeShift, michael@0: op_xori = 14 << OpcodeShift, michael@0: op_lui = 15 << OpcodeShift, michael@0: michael@0: op_cop1 = 17 << OpcodeShift, michael@0: op_cop1x = 19 << OpcodeShift, michael@0: michael@0: op_beql = 20 << OpcodeShift, michael@0: op_bnel = 21 << OpcodeShift, michael@0: op_blezl = 22 << OpcodeShift, michael@0: op_bgtzl = 23 << OpcodeShift, michael@0: michael@0: op_special2 = 28 << OpcodeShift, michael@0: op_special3 = 31 << OpcodeShift, michael@0: michael@0: op_lb = 32 << OpcodeShift, michael@0: op_lh = 33 << OpcodeShift, michael@0: op_lwl = 34 << OpcodeShift, michael@0: op_lw = 35 << OpcodeShift, michael@0: op_lbu = 36 << OpcodeShift, michael@0: op_lhu = 37 << OpcodeShift, michael@0: op_lwr = 38 << OpcodeShift, michael@0: op_sb = 40 << OpcodeShift, michael@0: op_sh = 41 << OpcodeShift, michael@0: op_swl = 42 << OpcodeShift, michael@0: op_sw = 43 << OpcodeShift, michael@0: op_swr = 46 << OpcodeShift, michael@0: michael@0: op_lwc1 = 49 << OpcodeShift, michael@0: op_ldc1 = 53 << OpcodeShift, michael@0: michael@0: op_swc1 = 57 << OpcodeShift, michael@0: op_sdc1 = 61 << OpcodeShift michael@0: }; michael@0: michael@0: enum RSField { michael@0: rs_zero = 0 << RSShift, michael@0: // cop1 encoding of RS field. michael@0: rs_mfc1 = 0 << RSShift, michael@0: rs_one = 1 << RSShift, michael@0: rs_cfc1 = 2 << RSShift, michael@0: rs_mfhc1 = 3 << RSShift, michael@0: rs_mtc1 = 4 << RSShift, michael@0: rs_ctc1 = 6 << RSShift, michael@0: rs_mthc1 = 7 << RSShift, michael@0: rs_bc1 = 8 << RSShift, michael@0: rs_s = 16 << RSShift, michael@0: rs_d = 17 << RSShift, michael@0: rs_w = 20 << RSShift, michael@0: rs_ps = 22 << RSShift michael@0: }; michael@0: michael@0: enum RTField { michael@0: rt_zero = 0 << RTShift, michael@0: // regimm encoding of RT field. michael@0: rt_bltz = 0 << RTShift, michael@0: rt_bgez = 1 << RTShift, michael@0: rt_bltzal = 16 << RTShift, michael@0: rt_bgezal = 17 << RTShift michael@0: }; michael@0: michael@0: enum FunctionField { michael@0: // special encoding of function field. michael@0: ff_sll = 0, michael@0: ff_movci = 1, michael@0: ff_srl = 2, michael@0: ff_sra = 3, michael@0: ff_sllv = 4, michael@0: ff_srlv = 6, michael@0: ff_srav = 7, michael@0: michael@0: ff_jr = 8, michael@0: ff_jalr = 9, michael@0: ff_movz = 10, michael@0: ff_movn = 11, michael@0: ff_break = 13, michael@0: michael@0: ff_mfhi = 16, michael@0: ff_mflo = 18, michael@0: michael@0: ff_mult = 24, michael@0: ff_multu = 25, michael@0: ff_div = 26, michael@0: ff_divu = 27, michael@0: michael@0: ff_add = 32, michael@0: ff_addu = 33, michael@0: ff_sub = 34, michael@0: ff_subu = 35, michael@0: ff_and = 36, michael@0: ff_or = 37, michael@0: ff_xor = 38, michael@0: ff_nor = 39, michael@0: michael@0: ff_slt = 42, michael@0: ff_sltu = 43, michael@0: michael@0: // special2 encoding of function field. michael@0: ff_mul = 2, michael@0: ff_clz = 32, michael@0: ff_clo = 33, michael@0: michael@0: // special3 encoding of function field. michael@0: ff_ext = 0, michael@0: ff_ins = 4, michael@0: michael@0: // cop1 encoding of function field. michael@0: ff_add_fmt = 0, michael@0: ff_sub_fmt = 1, michael@0: ff_mul_fmt = 2, michael@0: ff_div_fmt = 3, michael@0: ff_sqrt_fmt = 4, michael@0: ff_abs_fmt = 5, michael@0: ff_mov_fmt = 6, michael@0: ff_neg_fmt = 7, michael@0: michael@0: ff_round_w_fmt = 12, michael@0: ff_trunc_w_fmt = 13, michael@0: ff_ceil_w_fmt = 14, michael@0: ff_floor_w_fmt = 15, michael@0: michael@0: ff_cvt_s_fmt = 32, michael@0: ff_cvt_d_fmt = 33, michael@0: ff_cvt_w_fmt = 36, michael@0: michael@0: ff_c_f_fmt = 48, michael@0: ff_c_un_fmt = 49, michael@0: ff_c_eq_fmt = 50, michael@0: ff_c_ueq_fmt = 51, michael@0: ff_c_olt_fmt = 52, michael@0: ff_c_ult_fmt = 53, michael@0: ff_c_ole_fmt = 54, michael@0: ff_c_ule_fmt = 55, michael@0: }; michael@0: michael@0: class MacroAssemblerMIPS; michael@0: class Operand; michael@0: michael@0: // A BOffImm16 is a 16 bit immediate that is used for branches. michael@0: class BOffImm16 michael@0: { michael@0: uint32_t data; michael@0: michael@0: public: michael@0: uint32_t encode() { michael@0: JS_ASSERT(!isInvalid()); michael@0: return data; michael@0: } michael@0: int32_t decode() { michael@0: JS_ASSERT(!isInvalid()); michael@0: return (int32_t(data << 18) >> 16) + 4; michael@0: } michael@0: michael@0: explicit BOffImm16(int offset) michael@0: : data ((offset - 4) >> 2 & Imm16Mask) michael@0: { michael@0: JS_ASSERT((offset & 0x3) == 0); michael@0: JS_ASSERT(isInRange(offset)); michael@0: } michael@0: static bool isInRange(int offset) { michael@0: if ((offset - 4) < (INT16_MIN << 2)) michael@0: return false; michael@0: if ((offset - 4) > (INT16_MAX << 2)) michael@0: return false; michael@0: return true; michael@0: } michael@0: static const uint32_t INVALID = 0x00020000; michael@0: BOffImm16() michael@0: : data(INVALID) michael@0: { } michael@0: michael@0: bool isInvalid() { michael@0: return data == INVALID; michael@0: } michael@0: Instruction *getDest(Instruction *src); michael@0: michael@0: BOffImm16(InstImm inst); michael@0: }; michael@0: michael@0: // A JOffImm26 is a 26 bit immediate that is used for unconditional jumps. michael@0: class JOffImm26 michael@0: { michael@0: uint32_t data; michael@0: michael@0: public: michael@0: uint32_t encode() { michael@0: JS_ASSERT(!isInvalid()); michael@0: return data; michael@0: } michael@0: int32_t decode() { michael@0: JS_ASSERT(!isInvalid()); michael@0: return (int32_t(data << 8) >> 6) + 4; michael@0: } michael@0: michael@0: explicit JOffImm26(int offset) michael@0: : data ((offset - 4) >> 2 & Imm26Mask) michael@0: { michael@0: JS_ASSERT((offset & 0x3) == 0); michael@0: JS_ASSERT(isInRange(offset)); michael@0: } michael@0: static bool isInRange(int offset) { michael@0: if ((offset - 4) < -536870912) michael@0: return false; michael@0: if ((offset - 4) > 536870908) michael@0: return false; michael@0: return true; michael@0: } michael@0: static const uint32_t INVALID = 0x20000000; michael@0: JOffImm26() michael@0: : data(INVALID) michael@0: { } michael@0: michael@0: bool isInvalid() { michael@0: return data == INVALID; michael@0: } michael@0: Instruction *getDest(Instruction *src); michael@0: michael@0: }; michael@0: michael@0: class Imm16 michael@0: { michael@0: uint16_t value; michael@0: michael@0: public: michael@0: Imm16(); michael@0: Imm16(uint32_t imm) michael@0: : value(imm) michael@0: { } michael@0: uint32_t encode() { michael@0: return value; michael@0: } michael@0: int32_t decodeSigned() { michael@0: return value; michael@0: } michael@0: uint32_t decodeUnsigned() { michael@0: return value; michael@0: } michael@0: static bool isInSignedRange(int32_t imm) { michael@0: return imm >= INT16_MIN && imm <= INT16_MAX; michael@0: } michael@0: static bool isInUnsignedRange(uint32_t imm) { michael@0: return imm <= UINT16_MAX ; michael@0: } michael@0: static Imm16 lower (Imm32 imm) { michael@0: return Imm16(imm.value & 0xffff); michael@0: } michael@0: static Imm16 upper (Imm32 imm) { michael@0: return Imm16((imm.value >> 16) & 0xffff); michael@0: } michael@0: }; michael@0: michael@0: class Operand michael@0: { michael@0: public: michael@0: enum Tag { michael@0: REG, michael@0: FREG, michael@0: MEM michael@0: }; michael@0: michael@0: private: michael@0: Tag tag : 3; michael@0: uint32_t reg : 5; michael@0: int32_t offset; michael@0: michael@0: public: michael@0: Operand (Register reg_) michael@0: : tag(REG), reg(reg_.code()) michael@0: { } michael@0: michael@0: Operand (FloatRegister freg) michael@0: : tag(FREG), reg(freg.code()) michael@0: { } michael@0: michael@0: Operand (Register base, Imm32 off) michael@0: : tag(MEM), reg(base.code()), offset(off.value) michael@0: { } michael@0: michael@0: Operand (Register base, int32_t off) michael@0: : tag(MEM), reg(base.code()), offset(off) michael@0: { } michael@0: michael@0: Operand (const Address &addr) michael@0: : tag(MEM), reg(addr.base.code()), offset(addr.offset) michael@0: { } michael@0: michael@0: Tag getTag() const { michael@0: return tag; michael@0: } michael@0: michael@0: Register toReg() const { michael@0: JS_ASSERT(tag == REG); michael@0: return Register::FromCode(reg); michael@0: } michael@0: michael@0: FloatRegister toFReg() const { michael@0: JS_ASSERT(tag == FREG); michael@0: return FloatRegister::FromCode(reg); michael@0: } michael@0: michael@0: void toAddr(Register *r, Imm32 *dest) const { michael@0: JS_ASSERT(tag == MEM); michael@0: *r = Register::FromCode(reg); michael@0: *dest = Imm32(offset); michael@0: } michael@0: Address toAddress() const { michael@0: JS_ASSERT(tag == MEM); michael@0: return Address(Register::FromCode(reg), offset); michael@0: } michael@0: int32_t disp() const { michael@0: JS_ASSERT(tag == MEM); michael@0: return offset; michael@0: } michael@0: michael@0: int32_t base() const { michael@0: JS_ASSERT(tag == MEM); michael@0: return reg; michael@0: } michael@0: Register baseReg() const { michael@0: JS_ASSERT(tag == MEM); michael@0: return Register::FromCode(reg); michael@0: } michael@0: }; michael@0: michael@0: void michael@0: PatchJump(CodeLocationJump &jump_, CodeLocationLabel label); michael@0: class Assembler; michael@0: typedef js::jit::AssemblerBuffer<1024, Instruction> MIPSBuffer; michael@0: michael@0: class Assembler : public AssemblerShared michael@0: { michael@0: public: michael@0: michael@0: enum Condition { michael@0: Equal, michael@0: NotEqual, michael@0: Above, michael@0: AboveOrEqual, michael@0: Below, michael@0: BelowOrEqual, michael@0: GreaterThan, michael@0: GreaterThanOrEqual, michael@0: LessThan, michael@0: LessThanOrEqual, michael@0: Overflow, michael@0: Signed, michael@0: NotSigned, michael@0: Zero, michael@0: NonZero, michael@0: Always, michael@0: }; michael@0: michael@0: enum DoubleCondition { michael@0: // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. michael@0: DoubleOrdered, michael@0: DoubleEqual, michael@0: DoubleNotEqual, michael@0: DoubleGreaterThan, michael@0: DoubleGreaterThanOrEqual, michael@0: DoubleLessThan, michael@0: DoubleLessThanOrEqual, michael@0: // If either operand is NaN, these conditions always evaluate to true. michael@0: DoubleUnordered, michael@0: DoubleEqualOrUnordered, michael@0: DoubleNotEqualOrUnordered, michael@0: DoubleGreaterThanOrUnordered, michael@0: DoubleGreaterThanOrEqualOrUnordered, michael@0: DoubleLessThanOrUnordered, michael@0: DoubleLessThanOrEqualOrUnordered michael@0: }; michael@0: michael@0: enum FPConditionBit { michael@0: FCC0 = 0, michael@0: FCC1, michael@0: FCC2, michael@0: FCC3, michael@0: FCC4, michael@0: FCC5, michael@0: FCC6, michael@0: FCC7 michael@0: }; michael@0: michael@0: enum FloatFormat { michael@0: SingleFloat, michael@0: DoubleFloat michael@0: }; michael@0: michael@0: enum JumpOrCall { michael@0: BranchIsJump, michael@0: BranchIsCall michael@0: }; michael@0: michael@0: enum FloatTestKind { michael@0: TestForTrue, michael@0: TestForFalse michael@0: }; michael@0: michael@0: // :( this should be protected, but since CodeGenerator michael@0: // wants to use it, It needs to go out here :( michael@0: michael@0: BufferOffset nextOffset() { michael@0: return m_buffer.nextOffset(); michael@0: } michael@0: michael@0: protected: michael@0: Instruction * editSrc (BufferOffset bo) { michael@0: return m_buffer.getInst(bo); michael@0: } michael@0: public: michael@0: uint32_t actualOffset(uint32_t) const; michael@0: uint32_t actualIndex(uint32_t) const; michael@0: static uint8_t *PatchableJumpAddress(JitCode *code, uint32_t index); michael@0: protected: michael@0: michael@0: // structure for fixing up pc-relative loads/jumps when a the machine code michael@0: // gets moved (executable copy, gc, etc.) michael@0: struct RelativePatch michael@0: { michael@0: // the offset within the code buffer where the value is loaded that michael@0: // we want to fix-up michael@0: BufferOffset offset; michael@0: void *target; michael@0: Relocation::Kind kind; michael@0: michael@0: RelativePatch(BufferOffset offset, void *target, Relocation::Kind kind) michael@0: : offset(offset), michael@0: target(target), michael@0: kind(kind) michael@0: { } michael@0: }; michael@0: michael@0: js::Vector codeLabels_; michael@0: js::Vector jumps_; michael@0: js::Vector longJumps_; michael@0: michael@0: CompactBufferWriter jumpRelocations_; michael@0: CompactBufferWriter dataRelocations_; michael@0: CompactBufferWriter relocations_; michael@0: CompactBufferWriter preBarriers_; michael@0: michael@0: bool enoughMemory_; michael@0: michael@0: MIPSBuffer m_buffer; michael@0: michael@0: public: michael@0: Assembler() michael@0: : enoughMemory_(true), michael@0: m_buffer(), michael@0: isFinished(false) michael@0: { } michael@0: michael@0: static Condition InvertCondition(Condition cond); michael@0: static DoubleCondition InvertCondition(DoubleCondition cond); michael@0: michael@0: // MacroAssemblers hold onto gcthings, so they are traced by the GC. michael@0: void trace(JSTracer *trc); michael@0: void writeRelocation(BufferOffset src) { michael@0: jumpRelocations_.writeUnsigned(src.getOffset()); michael@0: } michael@0: michael@0: // As opposed to x86/x64 version, the data relocation has to be executed michael@0: // before to recover the pointer, and not after. michael@0: void writeDataRelocation(const ImmGCPtr &ptr) { michael@0: if (ptr.value) michael@0: dataRelocations_.writeUnsigned(nextOffset().getOffset()); michael@0: } michael@0: void writePrebarrierOffset(CodeOffsetLabel label) { michael@0: preBarriers_.writeUnsigned(label.offset()); michael@0: } michael@0: michael@0: public: michael@0: static uintptr_t getPointer(uint8_t *); michael@0: michael@0: bool oom() const; michael@0: michael@0: void setPrinter(Sprinter *sp) { michael@0: } michael@0: michael@0: private: michael@0: bool isFinished; michael@0: public: michael@0: void finish(); michael@0: void executableCopy(void *buffer); michael@0: void copyJumpRelocationTable(uint8_t *dest); michael@0: void copyDataRelocationTable(uint8_t *dest); michael@0: void copyPreBarrierTable(uint8_t *dest); michael@0: michael@0: bool addCodeLabel(CodeLabel label); michael@0: size_t numCodeLabels() const { michael@0: return codeLabels_.length(); michael@0: } michael@0: CodeLabel codeLabel(size_t i) { michael@0: return codeLabels_[i]; michael@0: } michael@0: michael@0: // Size of the instruction stream, in bytes. michael@0: size_t size() const; michael@0: // Size of the jump relocation table, in bytes. michael@0: size_t jumpRelocationTableBytes() const; michael@0: size_t dataRelocationTableBytes() const; michael@0: size_t preBarrierTableBytes() const; michael@0: michael@0: // Size of the data table, in bytes. michael@0: size_t bytesNeeded() const; michael@0: michael@0: // Write a blob of binary into the instruction stream *OR* michael@0: // into a destination address. If dest is nullptr (the default), then the michael@0: // instruction gets written into the instruction stream. If dest is not null michael@0: // it is interpreted as a pointer to the location that we want the michael@0: // instruction to be written. michael@0: BufferOffset writeInst(uint32_t x, uint32_t *dest = nullptr); michael@0: // A static variant for the cases where we don't want to have an assembler michael@0: // object at all. Normally, you would use the dummy (nullptr) object. michael@0: static void writeInstStatic(uint32_t x, uint32_t *dest); michael@0: michael@0: public: michael@0: BufferOffset align(int alignment); michael@0: BufferOffset as_nop(); michael@0: michael@0: // Branch and jump instructions michael@0: BufferOffset as_bal(BOffImm16 off); michael@0: michael@0: InstImm getBranchCode(JumpOrCall jumpOrCall); michael@0: InstImm getBranchCode(Register s, Register t, Condition c); michael@0: InstImm getBranchCode(Register s, Condition c); michael@0: InstImm getBranchCode(FloatTestKind testKind, FPConditionBit fcc); michael@0: michael@0: BufferOffset as_j(JOffImm26 off); michael@0: BufferOffset as_jal(JOffImm26 off); michael@0: michael@0: BufferOffset as_jr(Register rs); michael@0: BufferOffset as_jalr(Register rs); michael@0: michael@0: // Arithmetic instructions michael@0: BufferOffset as_addu(Register rd, Register rs, Register rt); michael@0: BufferOffset as_addiu(Register rd, Register rs, int32_t j); michael@0: BufferOffset as_subu(Register rd, Register rs, Register rt); michael@0: BufferOffset as_mult(Register rs, Register rt); michael@0: BufferOffset as_multu(Register rs, Register rt); michael@0: BufferOffset as_div(Register rs, Register rt); michael@0: BufferOffset as_divu(Register rs, Register rt); michael@0: BufferOffset as_mul(Register rd, Register rs, Register rt); michael@0: michael@0: // Logical instructions michael@0: BufferOffset as_and(Register rd, Register rs, Register rt); michael@0: BufferOffset as_or(Register rd, Register rs, Register rt); michael@0: BufferOffset as_xor(Register rd, Register rs, Register rt); michael@0: BufferOffset as_nor(Register rd, Register rs, Register rt); michael@0: michael@0: BufferOffset as_andi(Register rd, Register rs, int32_t j); michael@0: BufferOffset as_ori(Register rd, Register rs, int32_t j); michael@0: BufferOffset as_xori(Register rd, Register rs, int32_t j); michael@0: BufferOffset as_lui(Register rd, int32_t j); michael@0: michael@0: // Shift instructions michael@0: // as_sll(zero, zero, x) instructions are reserved as nop michael@0: BufferOffset as_sll(Register rd, Register rt, uint16_t sa); michael@0: BufferOffset as_sllv(Register rd, Register rt, Register rs); michael@0: BufferOffset as_srl(Register rd, Register rt, uint16_t sa); michael@0: BufferOffset as_srlv(Register rd, Register rt, Register rs); michael@0: BufferOffset as_sra(Register rd, Register rt, uint16_t sa); michael@0: BufferOffset as_srav(Register rd, Register rt, Register rs); michael@0: BufferOffset as_rotr(Register rd, Register rt, uint16_t sa); michael@0: BufferOffset as_rotrv(Register rd, Register rt, Register rs); michael@0: michael@0: // Load and store instructions michael@0: BufferOffset as_lb(Register rd, Register rs, int16_t off); michael@0: BufferOffset as_lbu(Register rd, Register rs, int16_t off); michael@0: BufferOffset as_lh(Register rd, Register rs, int16_t off); michael@0: BufferOffset as_lhu(Register rd, Register rs, int16_t off); michael@0: BufferOffset as_lw(Register rd, Register rs, int16_t off); michael@0: BufferOffset as_lwl(Register rd, Register rs, int16_t off); michael@0: BufferOffset as_lwr(Register rd, Register rs, int16_t off); michael@0: BufferOffset as_sb(Register rd, Register rs, int16_t off); michael@0: BufferOffset as_sh(Register rd, Register rs, int16_t off); michael@0: BufferOffset as_sw(Register rd, Register rs, int16_t off); michael@0: BufferOffset as_swl(Register rd, Register rs, int16_t off); michael@0: BufferOffset as_swr(Register rd, Register rs, int16_t off); michael@0: michael@0: // Move from HI/LO register. michael@0: BufferOffset as_mfhi(Register rd); michael@0: BufferOffset as_mflo(Register rd); michael@0: michael@0: // Set on less than. michael@0: BufferOffset as_slt(Register rd, Register rs, Register rt); michael@0: BufferOffset as_sltu(Register rd, Register rs, Register rt); michael@0: BufferOffset as_slti(Register rd, Register rs, int32_t j); michael@0: BufferOffset as_sltiu(Register rd, Register rs, uint32_t j); michael@0: michael@0: // Conditional move. michael@0: BufferOffset as_movz(Register rd, Register rs, Register rt); michael@0: BufferOffset as_movn(Register rd, Register rs, Register rt); michael@0: BufferOffset as_movt(Register rd, Register rs, uint16_t cc = 0); michael@0: BufferOffset as_movf(Register rd, Register rs, uint16_t cc = 0); michael@0: michael@0: // Bit twiddling. michael@0: BufferOffset as_clz(Register rd, Register rs, Register rt = Register::FromCode(0)); michael@0: BufferOffset as_ins(Register rt, Register rs, uint16_t pos, uint16_t size); michael@0: BufferOffset as_ext(Register rt, Register rs, uint16_t pos, uint16_t size); michael@0: michael@0: // FP instructions michael@0: michael@0: // Use these two functions only when you are sure address is aligned. michael@0: // Otherwise, use ma_ld and ma_sd. michael@0: BufferOffset as_ld(FloatRegister fd, Register base, int32_t off); michael@0: BufferOffset as_sd(FloatRegister fd, Register base, int32_t off); michael@0: michael@0: BufferOffset as_ls(FloatRegister fd, Register base, int32_t off); michael@0: BufferOffset as_ss(FloatRegister fd, Register base, int32_t off); michael@0: michael@0: BufferOffset as_movs(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_movd(FloatRegister fd, FloatRegister fs); michael@0: michael@0: BufferOffset as_mtc1(Register rt, FloatRegister fs); michael@0: BufferOffset as_mfc1(Register rt, FloatRegister fs); michael@0: michael@0: protected: michael@0: // This is used to access the odd regiter form the pair of single michael@0: // precision registers that make one double register. michael@0: FloatRegister getOddPair(FloatRegister reg) { michael@0: JS_ASSERT(reg.code() % 2 == 0); michael@0: return FloatRegister::FromCode(reg.code() + 1); michael@0: } michael@0: michael@0: public: michael@0: // FP convert instructions michael@0: BufferOffset as_ceilws(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_floorws(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_roundws(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_truncws(FloatRegister fd, FloatRegister fs); michael@0: michael@0: BufferOffset as_ceilwd(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_floorwd(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_roundwd(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_truncwd(FloatRegister fd, FloatRegister fs); michael@0: michael@0: BufferOffset as_cvtdl(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_cvtds(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_cvtdw(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_cvtld(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_cvtls(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_cvtsd(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_cvtsl(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_cvtsw(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_cvtwd(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_cvtws(FloatRegister fd, FloatRegister fs); michael@0: michael@0: // FP arithmetic instructions michael@0: BufferOffset as_adds(FloatRegister fd, FloatRegister fs, FloatRegister ft); michael@0: BufferOffset as_addd(FloatRegister fd, FloatRegister fs, FloatRegister ft); michael@0: BufferOffset as_subs(FloatRegister fd, FloatRegister fs, FloatRegister ft); michael@0: BufferOffset as_subd(FloatRegister fd, FloatRegister fs, FloatRegister ft); michael@0: michael@0: BufferOffset as_abss(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_absd(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_negd(FloatRegister fd, FloatRegister fs); michael@0: michael@0: BufferOffset as_muls(FloatRegister fd, FloatRegister fs, FloatRegister ft); michael@0: BufferOffset as_muld(FloatRegister fd, FloatRegister fs, FloatRegister ft); michael@0: BufferOffset as_divs(FloatRegister fd, FloatRegister fs, FloatRegister ft); michael@0: BufferOffset as_divd(FloatRegister fd, FloatRegister fs, FloatRegister ft); michael@0: BufferOffset as_sqrts(FloatRegister fd, FloatRegister fs); michael@0: BufferOffset as_sqrtd(FloatRegister fd, FloatRegister fs); michael@0: michael@0: // FP compare instructions michael@0: BufferOffset as_cf(FloatFormat fmt, FloatRegister fs, FloatRegister ft, michael@0: FPConditionBit fcc = FCC0); michael@0: BufferOffset as_cun(FloatFormat fmt, FloatRegister fs, FloatRegister ft, michael@0: FPConditionBit fcc = FCC0); michael@0: BufferOffset as_ceq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, michael@0: FPConditionBit fcc = FCC0); michael@0: BufferOffset as_cueq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, michael@0: FPConditionBit fcc = FCC0); michael@0: BufferOffset as_colt(FloatFormat fmt, FloatRegister fs, FloatRegister ft, michael@0: FPConditionBit fcc = FCC0); michael@0: BufferOffset as_cult(FloatFormat fmt, FloatRegister fs, FloatRegister ft, michael@0: FPConditionBit fcc = FCC0); michael@0: BufferOffset as_cole(FloatFormat fmt, FloatRegister fs, FloatRegister ft, michael@0: FPConditionBit fcc = FCC0); michael@0: BufferOffset as_cule(FloatFormat fmt, FloatRegister fs, FloatRegister ft, michael@0: FPConditionBit fcc = FCC0); michael@0: michael@0: // label operations michael@0: void bind(Label *label, BufferOffset boff = BufferOffset()); michael@0: void bind(RepatchLabel *label); michael@0: uint32_t currentOffset() { michael@0: return nextOffset().getOffset(); michael@0: } michael@0: void retarget(Label *label, Label *target); michael@0: void Bind(uint8_t *rawCode, AbsoluteLabel *label, const void *address); michael@0: michael@0: // See Bind michael@0: size_t labelOffsetToPatchOffset(size_t offset) { michael@0: return actualOffset(offset); michael@0: } michael@0: michael@0: void call(Label *label); michael@0: void call(void *target); michael@0: michael@0: void as_break(uint32_t code); michael@0: michael@0: public: michael@0: static void TraceJumpRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader); michael@0: static void TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader); michael@0: michael@0: protected: michael@0: InstImm invertBranch(InstImm branch, BOffImm16 skipOffset); michael@0: void bind(InstImm *inst, uint32_t branch, uint32_t target); michael@0: void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) { michael@0: enoughMemory_ &= jumps_.append(RelativePatch(src, target.value, kind)); michael@0: if (kind == Relocation::JITCODE) michael@0: writeRelocation(src); michael@0: } michael@0: michael@0: void addLongJump(BufferOffset src) { michael@0: enoughMemory_ &= longJumps_.append(src.getOffset()); michael@0: } michael@0: michael@0: public: michael@0: size_t numLongJumps() const { michael@0: return longJumps_.length(); michael@0: } michael@0: uint32_t longJump(size_t i) { michael@0: return longJumps_[i]; michael@0: } michael@0: michael@0: // Copy the assembly code to the given buffer, and perform any pending michael@0: // relocations relying on the target address. michael@0: void executableCopy(uint8_t *buffer); michael@0: michael@0: void flushBuffer() { michael@0: } michael@0: michael@0: static uint32_t patchWrite_NearCallSize(); michael@0: static uint32_t nopSize() { return 4; } michael@0: michael@0: static uint32_t extractLuiOriValue(Instruction *inst0, Instruction *inst1); michael@0: static void updateLuiOriValue(Instruction *inst0, Instruction *inst1, uint32_t value); michael@0: static void writeLuiOriInstructions(Instruction *inst, Instruction *inst1, michael@0: Register reg, uint32_t value); michael@0: michael@0: static void patchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall); michael@0: static void patchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue, michael@0: PatchedImmPtr expectedValue); michael@0: static void patchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, michael@0: ImmPtr expectedValue); michael@0: static void patchWrite_Imm32(CodeLocationLabel label, Imm32 imm); michael@0: static uint32_t alignDoubleArg(uint32_t offset) { michael@0: return (offset + 1U) &~ 1U; michael@0: } michael@0: michael@0: static uint8_t *nextInstruction(uint8_t *instruction, uint32_t *count = nullptr); michael@0: michael@0: static void ToggleToJmp(CodeLocationLabel inst_); michael@0: static void ToggleToCmp(CodeLocationLabel inst_); michael@0: michael@0: static void ToggleCall(CodeLocationLabel inst_, bool enabled); michael@0: michael@0: static void updateBoundsCheck(uint32_t logHeapSize, Instruction *inst); michael@0: void processCodeLabels(uint8_t *rawCode); michael@0: michael@0: bool bailed() { michael@0: return m_buffer.bail(); michael@0: } michael@0: }; // Assembler michael@0: michael@0: // An Instruction is a structure for both encoding and decoding any and all michael@0: // MIPS instructions. michael@0: class Instruction michael@0: { michael@0: protected: michael@0: // sll zero, zero, 0 michael@0: static const uint32_t NopInst = 0x00000000; michael@0: michael@0: uint32_t data; michael@0: michael@0: // Standard constructor michael@0: Instruction (uint32_t data_) : data(data_) { } michael@0: michael@0: // You should never create an instruction directly. You should create a michael@0: // more specific instruction which will eventually call one of these michael@0: // constructors for you. michael@0: public: michael@0: uint32_t encode() const { michael@0: return data; michael@0: } michael@0: michael@0: void makeNop() { michael@0: data = NopInst; michael@0: } michael@0: michael@0: void setData(uint32_t data) { michael@0: this->data = data; michael@0: } michael@0: michael@0: const Instruction & operator=(const Instruction &src) { michael@0: data = src.data; michael@0: return *this; michael@0: } michael@0: michael@0: // Extract the one particular bit. michael@0: uint32_t extractBit(uint32_t bit) { michael@0: return (encode() >> bit) & 1; michael@0: } michael@0: // Extract a bit field out of the instruction michael@0: uint32_t extractBitField(uint32_t hi, uint32_t lo) { michael@0: return (encode() >> lo) & ((2 << (hi - lo)) - 1); michael@0: } michael@0: // Since all MIPS instructions have opcode, the opcode michael@0: // extractor resides in the base class. michael@0: uint32_t extractOpcode() { michael@0: return extractBitField(OpcodeShift + OpcodeBits - 1, OpcodeShift); michael@0: } michael@0: // Return the fields at their original place in the instruction encoding. michael@0: Opcode OpcodeFieldRaw() const { michael@0: return static_cast(encode() & OpcodeMask); michael@0: } michael@0: michael@0: // Get the next instruction in the instruction stream. michael@0: // This does neat things like ignoreconstant pools and their guards. michael@0: Instruction *next(); michael@0: michael@0: // Sometimes, an api wants a uint32_t (or a pointer to it) rather than michael@0: // an instruction. raw() just coerces this into a pointer to a uint32_t michael@0: const uint32_t *raw() const { return &data; } michael@0: uint32_t size() const { return 4; } michael@0: }; // Instruction michael@0: michael@0: // make sure that it is the right size michael@0: static_assert(sizeof(Instruction) == 4, "Size of Instruction class has to be 4 bytes."); michael@0: michael@0: class InstNOP : public Instruction michael@0: { michael@0: public: michael@0: InstNOP() michael@0: : Instruction(NopInst) michael@0: { } michael@0: michael@0: }; michael@0: michael@0: // Class for register type instructions. michael@0: class InstReg : public Instruction michael@0: { michael@0: public: michael@0: InstReg(Opcode op, Register rd, FunctionField ff) michael@0: : Instruction(op | RD(rd) | ff) michael@0: { } michael@0: InstReg(Opcode op, Register rs, Register rt, FunctionField ff) michael@0: : Instruction(op | RS(rs) | RT(rt) | ff) michael@0: { } michael@0: InstReg(Opcode op, Register rs, Register rt, Register rd, FunctionField ff) michael@0: : Instruction(op | RS(rs) | RT(rt) | RD(rd) | ff) michael@0: { } michael@0: InstReg(Opcode op, Register rs, Register rt, Register rd, uint32_t sa, FunctionField ff) michael@0: : Instruction(op | RS(rs) | RT(rt) | RD(rd) | SA(sa) | ff) michael@0: { } michael@0: InstReg(Opcode op, RSField rs, Register rt, Register rd, uint32_t sa, FunctionField ff) michael@0: : Instruction(op | rs | RT(rt) | RD(rd) | SA(sa) | ff) michael@0: { } michael@0: InstReg(Opcode op, Register rs, RTField rt, Register rd, uint32_t sa, FunctionField ff) michael@0: : Instruction(op | RS(rs) | rt | RD(rd) | SA(sa) | ff) michael@0: { } michael@0: InstReg(Opcode op, Register rs, uint32_t cc, Register rd, uint32_t sa, FunctionField ff) michael@0: : Instruction(op | RS(rs) | cc | RD(rd) | SA(sa) | ff) michael@0: { } michael@0: InstReg(Opcode op, uint32_t code, FunctionField ff) michael@0: : Instruction(op | code | ff) michael@0: { } michael@0: // for float point michael@0: InstReg(Opcode op, RSField rs, Register rt, FloatRegister rd) michael@0: : Instruction(op | rs | RT(rt) | RD(rd)) michael@0: { } michael@0: InstReg(Opcode op, RSField rs, Register rt, FloatRegister rd, uint32_t sa, FunctionField ff) michael@0: : Instruction(op | rs | RT(rt) | RD(rd) | SA(sa) | ff) michael@0: { } michael@0: InstReg(Opcode op, RSField rs, Register rt, FloatRegister fs, FloatRegister fd, FunctionField ff) michael@0: : Instruction(op | rs | RT(rt) | RD(fs) | SA(fd) | ff) michael@0: { } michael@0: InstReg(Opcode op, RSField rs, FloatRegister ft, FloatRegister fs, FloatRegister fd, FunctionField ff) michael@0: : Instruction(op | rs | RT(ft) | RD(fs) | SA(fd) | ff) michael@0: { } michael@0: InstReg(Opcode op, RSField rs, FloatRegister ft, FloatRegister fd, uint32_t sa, FunctionField ff) michael@0: : Instruction(op | rs | RT(ft) | RD(fd) | SA(sa) | ff) michael@0: { } michael@0: michael@0: uint32_t extractRS () { michael@0: return extractBitField(RSShift + RSBits - 1, RSShift); michael@0: } michael@0: uint32_t extractRT () { michael@0: return extractBitField(RTShift + RTBits - 1, RTShift); michael@0: } michael@0: uint32_t extractRD () { michael@0: return extractBitField(RDShift + RDBits - 1, RDShift); michael@0: } michael@0: uint32_t extractSA () { michael@0: return extractBitField(SAShift + SABits - 1, SAShift); michael@0: } michael@0: uint32_t extractFunctionField () { michael@0: return extractBitField(FunctionShift + FunctionBits - 1, FunctionShift); michael@0: } michael@0: }; michael@0: michael@0: // Class for branch, load and store instructions with immediate offset. michael@0: class InstImm : public Instruction michael@0: { michael@0: public: michael@0: void extractImm16(BOffImm16 *dest); michael@0: michael@0: InstImm(Opcode op, Register rs, Register rt, BOffImm16 off) michael@0: : Instruction(op | RS(rs) | RT(rt) | off.encode()) michael@0: { } michael@0: InstImm(Opcode op, Register rs, RTField rt, BOffImm16 off) michael@0: : Instruction(op | RS(rs) | rt | off.encode()) michael@0: { } michael@0: InstImm(Opcode op, RSField rs, uint32_t cc, BOffImm16 off) michael@0: : Instruction(op | rs | cc | off.encode()) michael@0: { } michael@0: InstImm(Opcode op, Register rs, Register rt, Imm16 off) michael@0: : Instruction(op | RS(rs) | RT(rt) | off.encode()) michael@0: { } michael@0: InstImm(uint32_t raw) michael@0: : Instruction(raw) michael@0: { } michael@0: // For floating-point loads and stores. michael@0: InstImm(Opcode op, Register rs, FloatRegister rt, Imm16 off) michael@0: : Instruction(op | RS(rs) | RT(rt) | off.encode()) michael@0: { } michael@0: michael@0: uint32_t extractOpcode() { michael@0: return extractBitField(OpcodeShift + OpcodeBits - 1, OpcodeShift); michael@0: } michael@0: void setOpcode(Opcode op) { michael@0: data = (data & ~OpcodeMask) | op; michael@0: } michael@0: uint32_t extractRS() { michael@0: return extractBitField(RSShift + RSBits - 1, RSShift); michael@0: } michael@0: uint32_t extractRT() { michael@0: return extractBitField(RTShift + RTBits - 1, RTShift); michael@0: } michael@0: void setRT(RTField rt) { michael@0: data = (data & ~RTMask) | rt; michael@0: } michael@0: uint32_t extractImm16Value() { michael@0: return extractBitField(Imm16Shift + Imm16Bits - 1, Imm16Shift); michael@0: } michael@0: void setBOffImm16(BOffImm16 off) { michael@0: // Reset immediate field and replace it michael@0: data = (data & ~Imm16Mask) | off.encode(); michael@0: } michael@0: void setImm16(Imm16 off) { michael@0: // Reset immediate field and replace it michael@0: data = (data & ~Imm16Mask) | off.encode(); michael@0: } michael@0: }; michael@0: michael@0: // Class for Jump type instructions. michael@0: class InstJump : public Instruction michael@0: { michael@0: public: michael@0: InstJump(Opcode op, JOffImm26 off) michael@0: : Instruction(op | off.encode()) michael@0: { } michael@0: michael@0: uint32_t extractImm26Value() { michael@0: return extractBitField(Imm26Shift + Imm26Bits - 1, Imm26Shift); michael@0: } michael@0: }; michael@0: michael@0: static const uint32_t NumIntArgRegs = 4; michael@0: michael@0: static inline bool michael@0: GetIntArgReg(uint32_t usedArgSlots, Register *out) michael@0: { michael@0: if (usedArgSlots < NumIntArgRegs) { michael@0: *out = Register::FromCode(a0.code() + usedArgSlots); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // Get a register in which we plan to put a quantity that will be used as an michael@0: // integer argument. This differs from GetIntArgReg in that if we have no more michael@0: // actual argument registers to use we will fall back on using whatever michael@0: // CallTempReg* don't overlap the argument registers, and only fail once those michael@0: // run out too. michael@0: static inline bool michael@0: GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register *out) michael@0: { michael@0: // NOTE: We can't properly determine which regs are used if there are michael@0: // float arguments. If this is needed, we will have to guess. michael@0: JS_ASSERT(usedFloatArgs == 0); michael@0: michael@0: if (GetIntArgReg(usedIntArgs, out)) michael@0: return true; michael@0: // Unfortunately, we have to assume things about the point at which michael@0: // GetIntArgReg returns false, because we need to know how many registers it michael@0: // can allocate. michael@0: usedIntArgs -= NumIntArgRegs; michael@0: if (usedIntArgs >= NumCallTempNonArgRegs) michael@0: return false; michael@0: *out = CallTempNonArgRegs[usedIntArgs]; michael@0: return true; michael@0: } michael@0: michael@0: static inline uint32_t michael@0: GetArgStackDisp(uint32_t usedArgSlots) michael@0: { michael@0: JS_ASSERT(usedArgSlots >= NumIntArgRegs); michael@0: // Even register arguments have place reserved on stack. michael@0: return usedArgSlots * sizeof(intptr_t); michael@0: } michael@0: michael@0: } // namespace jit michael@0: } // namespace js michael@0: michael@0: #endif /* jit_mips_Assembler_mips_h */