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: #include "jit/mips/MacroAssembler-mips.h" michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: #include "mozilla/MathAlgorithms.h" michael@0: michael@0: #include "jit/Bailouts.h" michael@0: #include "jit/BaselineFrame.h" michael@0: #include "jit/BaselineRegisters.h" michael@0: #include "jit/IonFrames.h" michael@0: #include "jit/MoveEmitter.h" michael@0: michael@0: using namespace js; michael@0: using namespace jit; michael@0: michael@0: using mozilla::Abs; michael@0: michael@0: static const int32_t PAYLOAD_OFFSET = NUNBOX32_PAYLOAD_OFFSET; michael@0: static const int32_t TAG_OFFSET = NUNBOX32_TYPE_OFFSET; michael@0: michael@0: static_assert(sizeof(intptr_t) == 4, "Not 64-bit clean."); michael@0: michael@0: void michael@0: MacroAssemblerMIPS::convertBoolToInt32(Register src, Register dest) michael@0: { michael@0: // Note that C++ bool is only 1 byte, so zero extend it to clear the michael@0: // higher-order bits. michael@0: ma_and(dest, src, Imm32(0xff)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::convertInt32ToDouble(const Register &src, const FloatRegister &dest) michael@0: { michael@0: as_mtc1(src, dest); michael@0: as_cvtdw(dest, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::convertInt32ToDouble(const Address &src, FloatRegister dest) michael@0: { michael@0: ma_lw(ScratchRegister, src); michael@0: as_mtc1(ScratchRegister, dest); michael@0: as_cvtdw(dest, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::convertUInt32ToDouble(const Register &src, const FloatRegister &dest) michael@0: { michael@0: // We use SecondScratchFloatReg because MacroAssembler::loadFromTypedArray michael@0: // calls with ScratchFloatReg as dest. michael@0: MOZ_ASSERT(dest != SecondScratchFloatReg); michael@0: michael@0: // Subtract INT32_MIN to get a positive number michael@0: ma_subu(ScratchRegister, src, Imm32(INT32_MIN)); michael@0: michael@0: // Convert value michael@0: as_mtc1(ScratchRegister, dest); michael@0: as_cvtdw(dest, dest); michael@0: michael@0: // Add unsigned value of INT32_MIN michael@0: ma_lid(SecondScratchFloatReg, 2147483648.0); michael@0: as_addd(dest, dest, SecondScratchFloatReg); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::convertUInt32ToFloat32(const Register &src, const FloatRegister &dest) michael@0: { michael@0: MOZ_ASSUME_UNREACHABLE("NYI"); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::convertDoubleToFloat32(const FloatRegister &src, const FloatRegister &dest) michael@0: { michael@0: as_cvtsd(dest, src); michael@0: } michael@0: michael@0: // Convert the floating point value to an integer, if it did not fit, then it michael@0: // was clamped to INT32_MIN/INT32_MAX, and we can test it. michael@0: // NOTE: if the value really was supposed to be INT32_MAX / INT32_MIN then it michael@0: // will be wrong. michael@0: void michael@0: MacroAssemblerMIPS::branchTruncateDouble(const FloatRegister &src, const Register &dest, michael@0: Label *fail) michael@0: { michael@0: Label test, success; michael@0: as_truncwd(ScratchFloatReg, src); michael@0: as_mfc1(dest, ScratchFloatReg); michael@0: michael@0: ma_b(dest, Imm32(INT32_MAX), fail, Assembler::Equal); michael@0: } michael@0: michael@0: // Checks whether a double is representable as a 32-bit integer. If so, the michael@0: // integer is written to the output register. Otherwise, a bailout is taken to michael@0: // the given snapshot. This function overwrites the scratch float register. michael@0: void michael@0: MacroAssemblerMIPS::convertDoubleToInt32(const FloatRegister &src, const Register &dest, michael@0: Label *fail, bool negativeZeroCheck) michael@0: { michael@0: // Convert double to int, then convert back and check if we have the michael@0: // same number. michael@0: as_cvtwd(ScratchFloatReg, src); michael@0: as_mfc1(dest, ScratchFloatReg); michael@0: as_cvtdw(ScratchFloatReg, ScratchFloatReg); michael@0: ma_bc1d(src, ScratchFloatReg, fail, Assembler::DoubleNotEqualOrUnordered); michael@0: michael@0: if (negativeZeroCheck) { michael@0: Label notZero; michael@0: ma_b(dest, Imm32(0), ¬Zero, Assembler::NotEqual, ShortJump); michael@0: // Test and bail for -0.0, when integer result is 0 michael@0: // Move the top word of the double into the output reg, if it is michael@0: // non-zero, then the original value was -0.0 michael@0: moveFromDoubleHi(src, dest); michael@0: ma_b(dest, Imm32(INT32_MIN), fail, Assembler::Equal); michael@0: bind(¬Zero); michael@0: } michael@0: } michael@0: michael@0: // Checks whether a float32 is representable as a 32-bit integer. If so, the michael@0: // integer is written to the output register. Otherwise, a bailout is taken to michael@0: // the given snapshot. This function overwrites the scratch float register. michael@0: void michael@0: MacroAssemblerMIPS::convertFloat32ToInt32(const FloatRegister &src, const Register &dest, michael@0: Label *fail, bool negativeZeroCheck) michael@0: { michael@0: // convert the floating point value to an integer, if it did not fit, then michael@0: // when we convert it *back* to a float, it will have a different value, michael@0: // which we can test. michael@0: as_cvtws(ScratchFloatReg, src); michael@0: as_mfc1(dest, ScratchFloatReg); michael@0: as_cvtsw(ScratchFloatReg, ScratchFloatReg); michael@0: ma_bc1s(src, ScratchFloatReg, fail, Assembler::DoubleNotEqualOrUnordered); michael@0: michael@0: if (negativeZeroCheck) { michael@0: Label notZero; michael@0: ma_b(dest, Imm32(0), ¬Zero, Assembler::NotEqual, ShortJump); michael@0: // Test and bail for -0.0, when integer result is 0 michael@0: // Move the top word of the double into the output reg, michael@0: // if it is non-zero, then the original value was -0.0 michael@0: moveFromDoubleHi(src, dest); michael@0: ma_b(dest, Imm32(INT32_MIN), fail, Assembler::Equal); michael@0: bind(¬Zero); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::convertFloat32ToDouble(const FloatRegister &src, const FloatRegister &dest) michael@0: { michael@0: as_cvtds(dest, src); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::branchTruncateFloat32(const FloatRegister &src, const Register &dest, michael@0: Label *fail) michael@0: { michael@0: Label test, success; michael@0: as_truncws(ScratchFloatReg, src); michael@0: as_mfc1(dest, ScratchFloatReg); michael@0: michael@0: ma_b(dest, Imm32(INT32_MAX), fail, Assembler::Equal); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::convertInt32ToFloat32(const Register &src, const FloatRegister &dest) michael@0: { michael@0: as_mtc1(src, dest); michael@0: as_cvtsw(dest, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::convertInt32ToFloat32(const Address &src, FloatRegister dest) michael@0: { michael@0: ma_lw(ScratchRegister, src); michael@0: as_mtc1(ScratchRegister, dest); michael@0: as_cvtsw(dest, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::addDouble(FloatRegister src, FloatRegister dest) michael@0: { michael@0: as_addd(dest, dest, src); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::subDouble(FloatRegister src, FloatRegister dest) michael@0: { michael@0: as_subd(dest, dest, src); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::mulDouble(FloatRegister src, FloatRegister dest) michael@0: { michael@0: as_muld(dest, dest, src); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::divDouble(FloatRegister src, FloatRegister dest) michael@0: { michael@0: as_divd(dest, dest, src); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::negateDouble(FloatRegister reg) michael@0: { michael@0: as_negd(reg, reg); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::inc64(AbsoluteAddress dest) michael@0: { michael@0: ma_li(ScratchRegister, Imm32((int32_t)dest.addr)); michael@0: as_lw(SecondScratchReg, ScratchRegister, 0); michael@0: michael@0: as_addiu(SecondScratchReg, SecondScratchReg, 1); michael@0: as_sw(SecondScratchReg, ScratchRegister, 0); michael@0: michael@0: as_sltiu(SecondScratchReg, SecondScratchReg, 1); michael@0: as_lw(ScratchRegister, ScratchRegister, 4); michael@0: michael@0: as_addu(SecondScratchReg, ScratchRegister, SecondScratchReg); michael@0: michael@0: ma_li(ScratchRegister, Imm32((int32_t)dest.addr)); michael@0: as_sw(SecondScratchReg, ScratchRegister, 4); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_move(Register rd, Register rs) michael@0: { michael@0: as_or(rd, rs, zero); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_li(Register dest, const ImmGCPtr &ptr) michael@0: { michael@0: writeDataRelocation(ptr); michael@0: ma_liPatchable(dest, Imm32(ptr.value)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_li(const Register &dest, AbsoluteLabel *label) michael@0: { michael@0: MOZ_ASSERT(!label->bound()); michael@0: // Thread the patch list through the unpatched address word in the michael@0: // instruction stream. michael@0: BufferOffset bo = m_buffer.nextOffset(); michael@0: ma_liPatchable(dest, Imm32(label->prev())); michael@0: label->setPrev(bo.getOffset()); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_li(Register dest, Imm32 imm) michael@0: { michael@0: if (Imm16::isInSignedRange(imm.value)) { michael@0: as_addiu(dest, zero, imm.value); michael@0: } else if (Imm16::isInUnsignedRange(imm.value)) { michael@0: as_ori(dest, zero, Imm16::lower(imm).encode()); michael@0: } else if (Imm16::lower(imm).encode() == 0) { michael@0: as_lui(dest, Imm16::upper(imm).encode()); michael@0: } else { michael@0: as_lui(dest, Imm16::upper(imm).encode()); michael@0: as_ori(dest, dest, Imm16::lower(imm).encode()); michael@0: } michael@0: } michael@0: michael@0: michael@0: // This method generates lui and ori instruction pair that can be modified by michael@0: // updateLuiOriValue, either during compilation (eg. Assembler::bind), or michael@0: // during execution (eg. jit::PatchJump). michael@0: void michael@0: MacroAssemblerMIPS::ma_liPatchable(Register dest, Imm32 imm) michael@0: { michael@0: m_buffer.ensureSpace(2 * sizeof(uint32_t)); michael@0: as_lui(dest, Imm16::upper(imm).encode()); michael@0: as_ori(dest, dest, Imm16::lower(imm).encode()); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_liPatchable(Register dest, ImmPtr imm) michael@0: { michael@0: return ma_liPatchable(dest, Imm32(int32_t(imm.value))); michael@0: } michael@0: michael@0: // Shifts michael@0: void michael@0: MacroAssemblerMIPS::ma_sll(Register rd, Register rt, Imm32 shift) michael@0: { michael@0: as_sll(rd, rt, shift.value % 32); michael@0: } michael@0: void michael@0: MacroAssemblerMIPS::ma_srl(Register rd, Register rt, Imm32 shift) michael@0: { michael@0: as_srl(rd, rt, shift.value % 32); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_sra(Register rd, Register rt, Imm32 shift) michael@0: { michael@0: as_sra(rd, rt, shift.value % 32); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_ror(Register rd, Register rt, Imm32 shift) michael@0: { michael@0: as_rotr(rd, rt, shift.value % 32); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_rol(Register rd, Register rt, Imm32 shift) michael@0: { michael@0: as_rotr(rd, rt, 32 - (shift.value % 32)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_sll(Register rd, Register rt, Register shift) michael@0: { michael@0: as_sllv(rd, rt, shift); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_srl(Register rd, Register rt, Register shift) michael@0: { michael@0: as_srlv(rd, rt, shift); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_sra(Register rd, Register rt, Register shift) michael@0: { michael@0: as_srav(rd, rt, shift); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_ror(Register rd, Register rt, Register shift) michael@0: { michael@0: as_rotrv(rd, rt, shift); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_rol(Register rd, Register rt, Register shift) michael@0: { michael@0: ma_negu(ScratchRegister, shift); michael@0: as_rotrv(rd, rt, ScratchRegister); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_negu(Register rd, Register rs) michael@0: { michael@0: as_subu(rd, zero, rs); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_not(Register rd, Register rs) michael@0: { michael@0: as_nor(rd, rs, zero); michael@0: } michael@0: michael@0: // And. michael@0: void michael@0: MacroAssemblerMIPS::ma_and(Register rd, Register rs) michael@0: { michael@0: as_and(rd, rd, rs); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_and(Register rd, Register rs, Register rt) michael@0: { michael@0: as_and(rd, rs, rt); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_and(Register rd, Imm32 imm) michael@0: { michael@0: ma_and(rd, rd, imm); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_and(Register rd, Register rs, Imm32 imm) michael@0: { michael@0: if (Imm16::isInUnsignedRange(imm.value)) { michael@0: as_andi(rd, rs, imm.value); michael@0: } else { michael@0: ma_li(ScratchRegister, imm); michael@0: as_and(rd, rs, ScratchRegister); michael@0: } michael@0: } michael@0: michael@0: // Or. michael@0: void michael@0: MacroAssemblerMIPS::ma_or(Register rd, Register rs) michael@0: { michael@0: as_or(rd, rd, rs); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_or(Register rd, Register rs, Register rt) michael@0: { michael@0: as_or(rd, rs, rt); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_or(Register rd, Imm32 imm) michael@0: { michael@0: ma_or(rd, rd, imm); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_or(Register rd, Register rs, Imm32 imm) michael@0: { michael@0: if (Imm16::isInUnsignedRange(imm.value)) { michael@0: as_ori(rd, rs, imm.value); michael@0: } else { michael@0: ma_li(ScratchRegister, imm); michael@0: as_or(rd, rs, ScratchRegister); michael@0: } michael@0: } michael@0: michael@0: // xor michael@0: void michael@0: MacroAssemblerMIPS::ma_xor(Register rd, Register rs) michael@0: { michael@0: as_xor(rd, rd, rs); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_xor(Register rd, Register rs, Register rt) michael@0: { michael@0: as_xor(rd, rs, rt); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_xor(Register rd, Imm32 imm) michael@0: { michael@0: ma_xor(rd, rd, imm); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_xor(Register rd, Register rs, Imm32 imm) michael@0: { michael@0: if (Imm16::isInUnsignedRange(imm.value)) { michael@0: as_xori(rd, rs, imm.value); michael@0: } else { michael@0: ma_li(ScratchRegister, imm); michael@0: as_xor(rd, rs, ScratchRegister); michael@0: } michael@0: } michael@0: michael@0: // Arithmetic-based ops. michael@0: michael@0: // Add. michael@0: void michael@0: MacroAssemblerMIPS::ma_addu(Register rd, Register rs, Imm32 imm) michael@0: { michael@0: if (Imm16::isInSignedRange(imm.value)) { michael@0: as_addiu(rd, rs, imm.value); michael@0: } else { michael@0: ma_li(ScratchRegister, imm); michael@0: as_addu(rd, rs, ScratchRegister); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_addu(Register rd, Register rs) michael@0: { michael@0: as_addu(rd, rd, rs); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_addu(Register rd, Imm32 imm) michael@0: { michael@0: ma_addu(rd, rd, imm); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_addTestOverflow(Register rd, Register rs, Register rt, Label *overflow) michael@0: { michael@0: Label goodAddition; michael@0: as_addu(SecondScratchReg, rs, rt); michael@0: michael@0: as_xor(ScratchRegister, rs, rt); // If different sign, no overflow michael@0: ma_b(ScratchRegister, Imm32(0), &goodAddition, Assembler::LessThan, ShortJump); michael@0: michael@0: // If different sign, then overflow michael@0: as_xor(ScratchRegister, rs, SecondScratchReg); michael@0: ma_b(ScratchRegister, Imm32(0), overflow, Assembler::LessThan); michael@0: michael@0: bind(&goodAddition); michael@0: ma_move(rd, SecondScratchReg); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_addTestOverflow(Register rd, Register rs, Imm32 imm, Label *overflow) michael@0: { michael@0: // Check for signed range because of as_addiu michael@0: // Check for unsigned range because of as_xori michael@0: if (Imm16::isInSignedRange(imm.value) && Imm16::isInUnsignedRange(imm.value)) { michael@0: Label goodAddition; michael@0: as_addiu(SecondScratchReg, rs, imm.value); michael@0: michael@0: // If different sign, no overflow michael@0: as_xori(ScratchRegister, rs, imm.value); michael@0: ma_b(ScratchRegister, Imm32(0), &goodAddition, Assembler::LessThan, ShortJump); michael@0: michael@0: // If different sign, then overflow michael@0: as_xor(ScratchRegister, rs, SecondScratchReg); michael@0: ma_b(ScratchRegister, Imm32(0), overflow, Assembler::LessThan); michael@0: michael@0: bind(&goodAddition); michael@0: ma_move(rd, SecondScratchReg); michael@0: } else { michael@0: ma_li(ScratchRegister, imm); michael@0: ma_addTestOverflow(rd, rs, ScratchRegister, overflow); michael@0: } michael@0: } michael@0: michael@0: // Subtract. michael@0: void michael@0: MacroAssemblerMIPS::ma_subu(Register rd, Register rs, Register rt) michael@0: { michael@0: as_subu(rd, rs, rt); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_subu(Register rd, Register rs, Imm32 imm) michael@0: { michael@0: if (Imm16::isInSignedRange(-imm.value)) { michael@0: as_addiu(rd, rs, -imm.value); michael@0: } else { michael@0: ma_li(ScratchRegister, imm); michael@0: as_subu(rd, rs, ScratchRegister); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_subu(Register rd, Imm32 imm) michael@0: { michael@0: ma_subu(rd, rd, imm); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_subTestOverflow(Register rd, Register rs, Register rt, Label *overflow) michael@0: { michael@0: Label goodSubtraction; michael@0: // Use second scratch. The instructions generated by ma_b don't use the michael@0: // second scratch register. michael@0: ma_subu(SecondScratchReg, rs, rt); michael@0: michael@0: as_xor(ScratchRegister, rs, rt); // If same sign, no overflow michael@0: ma_b(ScratchRegister, Imm32(0), &goodSubtraction, Assembler::GreaterThanOrEqual, ShortJump); michael@0: michael@0: // If different sign, then overflow michael@0: as_xor(ScratchRegister, rs, SecondScratchReg); michael@0: ma_b(ScratchRegister, Imm32(0), overflow, Assembler::LessThan); michael@0: michael@0: bind(&goodSubtraction); michael@0: ma_move(rd, SecondScratchReg); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_subTestOverflow(Register rd, Register rs, Imm32 imm, Label *overflow) michael@0: { michael@0: if (imm.value != INT32_MIN) { michael@0: ma_addTestOverflow(rd, rs, Imm32(-imm.value), overflow); michael@0: } else { michael@0: ma_li(ScratchRegister, Imm32(imm.value)); michael@0: ma_subTestOverflow(rd, rs, ScratchRegister, overflow); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_mult(Register rs, Imm32 imm) michael@0: { michael@0: ma_li(ScratchRegister, imm); michael@0: as_mult(rs, ScratchRegister); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_mul_branch_overflow(Register rd, Register rs, Register rt, Label *overflow) michael@0: { michael@0: as_mult(rs, rt); michael@0: as_mflo(rd); michael@0: as_sra(ScratchRegister, rd, 31); michael@0: as_mfhi(SecondScratchReg); michael@0: ma_b(ScratchRegister, SecondScratchReg, overflow, Assembler::NotEqual); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_mul_branch_overflow(Register rd, Register rs, Imm32 imm, Label *overflow) michael@0: { michael@0: ma_li(ScratchRegister, imm); michael@0: ma_mul_branch_overflow(rd, rs, ScratchRegister, overflow); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_div_branch_overflow(Register rd, Register rs, Register rt, Label *overflow) michael@0: { michael@0: as_div(rs, rt); michael@0: as_mflo(rd); michael@0: as_mfhi(ScratchRegister); michael@0: ma_b(ScratchRegister, ScratchRegister, overflow, Assembler::NonZero); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_div_branch_overflow(Register rd, Register rs, Imm32 imm, Label *overflow) michael@0: { michael@0: ma_li(ScratchRegister, imm); michael@0: ma_div_branch_overflow(rd, rs, ScratchRegister, overflow); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_mod_mask(Register src, Register dest, Register hold, int32_t shift, michael@0: Label *negZero) michael@0: { michael@0: // MATH: michael@0: // We wish to compute x % (1< 0, store sum - C back into sum, thus performing a michael@0: // modulus. michael@0: ma_b(SecondScratchReg, SecondScratchReg, &sumSigned, Signed, ShortJump); michael@0: ma_move(dest, SecondScratchReg); michael@0: bind(&sumSigned); michael@0: // Get rid of the bits that we extracted before. michael@0: as_srl(ScratchRegister, ScratchRegister, shift); michael@0: // If the shift produced zero, finish, otherwise, continue in the loop. michael@0: ma_b(ScratchRegister, ScratchRegister, &head, NonZero, ShortJump); michael@0: // Check the hold to see if we need to negate the result. michael@0: ma_b(hold, hold, &done, NotSigned, ShortJump); michael@0: michael@0: // If the hold was non-zero, negate the result to be in line with michael@0: // what JS wants michael@0: if (negZero != nullptr) { michael@0: // Jump out in case of negative zero. michael@0: ma_b(hold, hold, negZero, Zero); michael@0: ma_negu(dest, dest); michael@0: } else { michael@0: ma_negu(dest, dest); michael@0: } michael@0: michael@0: bind(&done); michael@0: } michael@0: michael@0: // Memory. michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_load(const Register &dest, Address address, michael@0: LoadStoreSize size, LoadStoreExtension extension) michael@0: { michael@0: int16_t encodedOffset; michael@0: Register base; michael@0: if (!Imm16::isInSignedRange(address.offset)) { michael@0: ma_li(ScratchRegister, Imm32(address.offset)); michael@0: as_addu(ScratchRegister, address.base, ScratchRegister); michael@0: base = ScratchRegister; michael@0: encodedOffset = Imm16(0).encode(); michael@0: } else { michael@0: encodedOffset = Imm16(address.offset).encode(); michael@0: base = address.base; michael@0: } michael@0: michael@0: switch (size) { michael@0: case SizeByte: michael@0: if (ZeroExtend == extension) michael@0: as_lbu(dest, base, encodedOffset); michael@0: else michael@0: as_lb(dest, base, encodedOffset); michael@0: break; michael@0: case SizeHalfWord: michael@0: if (ZeroExtend == extension) michael@0: as_lhu(dest, base, encodedOffset); michael@0: else michael@0: as_lh(dest, base, encodedOffset); michael@0: break; michael@0: case SizeWord: michael@0: as_lw(dest, base, encodedOffset); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid argument for ma_load"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_load(const Register &dest, const BaseIndex &src, michael@0: LoadStoreSize size, LoadStoreExtension extension) michael@0: { michael@0: computeScaledAddress(src, SecondScratchReg); michael@0: ma_load(dest, Address(SecondScratchReg, src.offset), size, extension); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_store(const Register &data, Address address, LoadStoreSize size, michael@0: LoadStoreExtension extension) michael@0: { michael@0: int16_t encodedOffset; michael@0: Register base; michael@0: if (!Imm16::isInSignedRange(address.offset)) { michael@0: ma_li(ScratchRegister, Imm32(address.offset)); michael@0: as_addu(ScratchRegister, address.base, ScratchRegister); michael@0: base = ScratchRegister; michael@0: encodedOffset = Imm16(0).encode(); michael@0: } else { michael@0: encodedOffset = Imm16(address.offset).encode(); michael@0: base = address.base; michael@0: } michael@0: michael@0: switch (size) { michael@0: case SizeByte: michael@0: as_sb(data, base, encodedOffset); michael@0: break; michael@0: case SizeHalfWord: michael@0: as_sh(data, base, encodedOffset); michael@0: break; michael@0: case SizeWord: michael@0: as_sw(data, base, encodedOffset); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid argument for ma_store"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_store(const Register &data, const BaseIndex &dest, michael@0: LoadStoreSize size, LoadStoreExtension extension) michael@0: { michael@0: computeScaledAddress(dest, SecondScratchReg); michael@0: ma_store(data, Address(SecondScratchReg, dest.offset), size, extension); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_store(const Imm32 &imm, const BaseIndex &dest, michael@0: LoadStoreSize size, LoadStoreExtension extension) michael@0: { michael@0: // Make sure that SecondScratchReg contains absolute address so that michael@0: // offset is 0. michael@0: computeEffectiveAddress(dest, SecondScratchReg); michael@0: michael@0: // Scrach register is free now, use it for loading imm value michael@0: ma_li(ScratchRegister, imm); michael@0: michael@0: // with offset=0 ScratchRegister will not be used in ma_store() michael@0: // so we can use it as a parameter here michael@0: ma_store(ScratchRegister, Address(SecondScratchReg, 0), size, extension); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::computeScaledAddress(const BaseIndex &address, Register dest) michael@0: { michael@0: int32_t shift = Imm32::ShiftOf(address.scale).value; michael@0: if (shift) { michael@0: ma_sll(dest, address.index, Imm32(shift)); michael@0: as_addu(dest, address.base, dest); michael@0: } else { michael@0: as_addu(dest, address.base, address.index); michael@0: } michael@0: } michael@0: michael@0: // Shortcut for when we know we're transferring 32 bits of data. michael@0: void michael@0: MacroAssemblerMIPS::ma_lw(Register data, Address address) michael@0: { michael@0: ma_load(data, address, SizeWord); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_sw(Register data, Address address) michael@0: { michael@0: ma_store(data, address, SizeWord); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_sw(Imm32 imm, Address address) michael@0: { michael@0: MOZ_ASSERT(address.base != ScratchRegister); michael@0: ma_li(ScratchRegister, imm); michael@0: michael@0: if (Imm16::isInSignedRange(address.offset)) { michael@0: as_sw(ScratchRegister, address.base, Imm16(address.offset).encode()); michael@0: } else { michael@0: MOZ_ASSERT(address.base != SecondScratchReg); michael@0: michael@0: ma_li(SecondScratchReg, Imm32(address.offset)); michael@0: as_addu(SecondScratchReg, address.base, SecondScratchReg); michael@0: as_sw(ScratchRegister, SecondScratchReg, 0); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_pop(Register r) michael@0: { michael@0: as_lw(r, StackPointer, 0); michael@0: as_addiu(StackPointer, StackPointer, sizeof(intptr_t)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_push(Register r) michael@0: { michael@0: if (r == sp) { michael@0: // Pushing sp requires one more instruction. michael@0: ma_move(ScratchRegister, sp); michael@0: r = ScratchRegister; michael@0: } michael@0: michael@0: as_addiu(StackPointer, StackPointer, -sizeof(intptr_t)); michael@0: as_sw(r, StackPointer, 0); michael@0: } michael@0: michael@0: // Branches when done from within mips-specific code. michael@0: void michael@0: MacroAssemblerMIPS::ma_b(Register lhs, Register rhs, Label *label, Condition c, JumpKind jumpKind) michael@0: { michael@0: switch (c) { michael@0: case Equal : michael@0: case NotEqual: michael@0: branchWithCode(getBranchCode(lhs, rhs, c), label, jumpKind); michael@0: break; michael@0: case Always: michael@0: ma_b(label, jumpKind); michael@0: break; michael@0: case Zero: michael@0: case NonZero: michael@0: case Signed: michael@0: case NotSigned: michael@0: MOZ_ASSERT(lhs == rhs); michael@0: branchWithCode(getBranchCode(lhs, c), label, jumpKind); michael@0: break; michael@0: default: michael@0: Condition cond = ma_cmp(ScratchRegister, lhs, rhs, c); michael@0: branchWithCode(getBranchCode(ScratchRegister, cond), label, jumpKind); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_b(Register lhs, Imm32 imm, Label *label, Condition c, JumpKind jumpKind) michael@0: { michael@0: MOZ_ASSERT(c != Overflow); michael@0: if (imm.value == 0) { michael@0: if (c == Always || c == AboveOrEqual) michael@0: ma_b(label, jumpKind); michael@0: else if (c == Below) michael@0: ; // This condition is always false. No branch required. michael@0: else michael@0: branchWithCode(getBranchCode(lhs, c), label, jumpKind); michael@0: } else { michael@0: MOZ_ASSERT(lhs != ScratchRegister); michael@0: ma_li(ScratchRegister, imm); michael@0: ma_b(lhs, ScratchRegister, label, c, jumpKind); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_b(Register lhs, Address addr, Label *label, Condition c, JumpKind jumpKind) michael@0: { michael@0: MOZ_ASSERT(lhs != ScratchRegister); michael@0: ma_lw(ScratchRegister, addr); michael@0: ma_b(lhs, ScratchRegister, label, c, jumpKind); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_b(Address addr, Imm32 imm, Label *label, Condition c, JumpKind jumpKind) michael@0: { michael@0: ma_lw(SecondScratchReg, addr); michael@0: ma_b(SecondScratchReg, imm, label, c, jumpKind); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_b(Label *label, JumpKind jumpKind) michael@0: { michael@0: branchWithCode(getBranchCode(BranchIsJump), label, jumpKind); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_bal(Label *label, JumpKind jumpKind) michael@0: { michael@0: branchWithCode(getBranchCode(BranchIsCall), label, jumpKind); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::branchWithCode(InstImm code, Label *label, JumpKind jumpKind) michael@0: { michael@0: InstImm inst_bgezal = InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)); michael@0: InstImm inst_beq = InstImm(op_beq, zero, zero, BOffImm16(0)); michael@0: michael@0: if (label->bound()) { michael@0: int32_t offset = label->offset() - m_buffer.nextOffset().getOffset(); michael@0: michael@0: if (BOffImm16::isInRange(offset)) michael@0: jumpKind = ShortJump; michael@0: michael@0: if (jumpKind == ShortJump) { michael@0: MOZ_ASSERT(BOffImm16::isInRange(offset)); michael@0: code.setBOffImm16(BOffImm16(offset)); michael@0: writeInst(code.encode()); michael@0: as_nop(); michael@0: return; michael@0: } michael@0: michael@0: // Generate long jump because target is out of range of short jump. michael@0: if (code.encode() == inst_bgezal.encode()) { michael@0: // Handle long call michael@0: addLongJump(nextOffset()); michael@0: ma_liPatchable(ScratchRegister, Imm32(label->offset())); michael@0: as_jalr(ScratchRegister); michael@0: as_nop(); michael@0: return; michael@0: } michael@0: if (code.encode() == inst_beq.encode()) { michael@0: // Handle long jump michael@0: addLongJump(nextOffset()); michael@0: ma_liPatchable(ScratchRegister, Imm32(label->offset())); michael@0: as_jr(ScratchRegister); michael@0: as_nop(); michael@0: return; michael@0: } michael@0: michael@0: // Handle long conditional branch michael@0: writeInst(invertBranch(code, BOffImm16(5 * sizeof(uint32_t))).encode()); michael@0: // No need for a "nop" here because we can clobber scratch. michael@0: addLongJump(nextOffset()); michael@0: ma_liPatchable(ScratchRegister, Imm32(label->offset())); michael@0: as_jr(ScratchRegister); michael@0: as_nop(); michael@0: return; michael@0: } michael@0: michael@0: // Generate open jump and link it to a label. michael@0: michael@0: // Second word holds a pointer to the next branch in label's chain. michael@0: uint32_t nextInChain = label->used() ? label->offset() : LabelBase::INVALID_OFFSET; michael@0: michael@0: if (jumpKind == ShortJump) { michael@0: // Make the whole branch continous in the buffer. michael@0: m_buffer.ensureSpace(2 * sizeof(uint32_t)); michael@0: michael@0: // Indicate that this is short jump with offset 4. michael@0: code.setBOffImm16(BOffImm16(4)); michael@0: BufferOffset bo = writeInst(code.encode()); michael@0: writeInst(nextInChain); michael@0: label->use(bo.getOffset()); michael@0: return; michael@0: } michael@0: michael@0: bool conditional = (code.encode() != inst_bgezal.encode() && michael@0: code.encode() != inst_beq.encode()); michael@0: michael@0: // Make the whole branch continous in the buffer. michael@0: m_buffer.ensureSpace((conditional ? 5 : 4) * sizeof(uint32_t)); michael@0: michael@0: BufferOffset bo = writeInst(code.encode()); michael@0: writeInst(nextInChain); michael@0: label->use(bo.getOffset()); michael@0: // Leave space for potential long jump. michael@0: as_nop(); michael@0: as_nop(); michael@0: if (conditional) michael@0: as_nop(); michael@0: } michael@0: michael@0: Assembler::Condition michael@0: MacroAssemblerMIPS::ma_cmp(Register scratch, Register lhs, Register rhs, Condition c) michael@0: { michael@0: switch (c) { michael@0: case Above: michael@0: // bgtu s,t,label => michael@0: // sltu at,t,s michael@0: // bne at,$zero,offs michael@0: as_sltu(scratch, rhs, lhs); michael@0: return NotEqual; michael@0: case AboveOrEqual: michael@0: // bgeu s,t,label => michael@0: // sltu at,s,t michael@0: // beq at,$zero,offs michael@0: as_sltu(scratch, lhs, rhs); michael@0: return Equal; michael@0: case Below: michael@0: // bltu s,t,label => michael@0: // sltu at,s,t michael@0: // bne at,$zero,offs michael@0: as_sltu(scratch, lhs, rhs); michael@0: return NotEqual; michael@0: case BelowOrEqual: michael@0: // bleu s,t,label => michael@0: // sltu at,t,s michael@0: // beq at,$zero,offs michael@0: as_sltu(scratch, rhs, lhs); michael@0: return Equal; michael@0: case GreaterThan: michael@0: // bgt s,t,label => michael@0: // slt at,t,s michael@0: // bne at,$zero,offs michael@0: as_slt(scratch, rhs, lhs); michael@0: return NotEqual; michael@0: case GreaterThanOrEqual: michael@0: // bge s,t,label => michael@0: // slt at,s,t michael@0: // beq at,$zero,offs michael@0: as_slt(scratch, lhs, rhs); michael@0: return Equal; michael@0: case LessThan: michael@0: // blt s,t,label => michael@0: // slt at,s,t michael@0: // bne at,$zero,offs michael@0: as_slt(scratch, lhs, rhs); michael@0: return NotEqual; michael@0: case LessThanOrEqual: michael@0: // ble s,t,label => michael@0: // slt at,t,s michael@0: // beq at,$zero,offs michael@0: as_slt(scratch, rhs, lhs); michael@0: return Equal; michael@0: case Equal : michael@0: case NotEqual: michael@0: case Zero: michael@0: case NonZero: michael@0: case Always: michael@0: case Signed: michael@0: case NotSigned: michael@0: MOZ_ASSUME_UNREACHABLE("There is a better way to compare for equality."); michael@0: break; michael@0: case Overflow: michael@0: MOZ_ASSUME_UNREACHABLE("Overflow condition not supported for MIPS."); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid condition for branch."); michael@0: } michael@0: return Always; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_cmp_set(Register rd, Register rs, Register rt, Condition c) michael@0: { michael@0: switch (c) { michael@0: case Equal : michael@0: // seq d,s,t => michael@0: // xor d,s,t michael@0: // sltiu d,d,1 michael@0: as_xor(rd, rs, rt); michael@0: as_sltiu(rd, rd, 1); michael@0: break; michael@0: case NotEqual: michael@0: // sne d,s,t => michael@0: // xor d,s,t michael@0: // sltu d,$zero,d michael@0: as_xor(rd, rs, rt); michael@0: as_sltu(rd, zero, rd); michael@0: break; michael@0: case Above: michael@0: // sgtu d,s,t => michael@0: // sltu d,t,s michael@0: as_sltu(rd, rt, rs); michael@0: break; michael@0: case AboveOrEqual: michael@0: // sgeu d,s,t => michael@0: // sltu d,s,t michael@0: // xori d,d,1 michael@0: as_sltu(rd, rs, rt); michael@0: as_xori(rd, rd, 1); michael@0: break; michael@0: case Below: michael@0: // sltu d,s,t michael@0: as_sltu(rd, rs, rt); michael@0: break; michael@0: case BelowOrEqual: michael@0: // sleu d,s,t => michael@0: // sltu d,t,s michael@0: // xori d,d,1 michael@0: as_sltu(rd, rt, rs); michael@0: as_xori(rd, rd, 1); michael@0: break; michael@0: case GreaterThan: michael@0: // sgt d,s,t => michael@0: // slt d,t,s michael@0: as_slt(rd, rt, rs); michael@0: break; michael@0: case GreaterThanOrEqual: michael@0: // sge d,s,t => michael@0: // slt d,s,t michael@0: // xori d,d,1 michael@0: as_slt(rd, rs, rt); michael@0: as_xori(rd, rd, 1); michael@0: break; michael@0: case LessThan: michael@0: // slt d,s,t michael@0: as_slt(rd, rs, rt); michael@0: break; michael@0: case LessThanOrEqual: michael@0: // sle d,s,t => michael@0: // slt d,t,s michael@0: // xori d,d,1 michael@0: as_slt(rd, rt, rs); michael@0: as_xori(rd, rd, 1); michael@0: break; michael@0: case Zero: michael@0: MOZ_ASSERT(rs == rt); michael@0: // seq d,s,$zero => michael@0: // xor d,s,$zero michael@0: // sltiu d,d,1 michael@0: as_xor(rd, rs, zero); michael@0: as_sltiu(rd, rd, 1); michael@0: break; michael@0: case NonZero: michael@0: // sne d,s,$zero => michael@0: // xor d,s,$zero michael@0: // sltu d,$zero,d michael@0: as_xor(rd, rs, zero); michael@0: as_sltu(rd, zero, rd); michael@0: break; michael@0: case Signed: michael@0: as_slt(rd, rs, zero); michael@0: break; michael@0: case NotSigned: michael@0: // sge d,s,$zero => michael@0: // slt d,s,$zero michael@0: // xori d,d,1 michael@0: as_slt(rd, rs, zero); michael@0: as_xori(rd, rd, 1); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid condition for ma_cmp_set."); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::compareFloatingPoint(FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, michael@0: DoubleCondition c, FloatTestKind *testKind, michael@0: FPConditionBit fcc) michael@0: { michael@0: switch (c) { michael@0: case DoubleOrdered: michael@0: as_cun(fmt, lhs, rhs, fcc); michael@0: *testKind = TestForFalse; michael@0: break; michael@0: case DoubleEqual: michael@0: as_ceq(fmt, lhs, rhs, fcc); michael@0: *testKind = TestForTrue; michael@0: break; michael@0: case DoubleNotEqual: michael@0: as_cueq(fmt, lhs, rhs, fcc); michael@0: *testKind = TestForFalse; michael@0: break; michael@0: case DoubleGreaterThan: michael@0: as_colt(fmt, rhs, lhs, fcc); michael@0: *testKind = TestForTrue; michael@0: break; michael@0: case DoubleGreaterThanOrEqual: michael@0: as_cole(fmt, rhs, lhs, fcc); michael@0: *testKind = TestForTrue; michael@0: break; michael@0: case DoubleLessThan: michael@0: as_colt(fmt, lhs, rhs, fcc); michael@0: *testKind = TestForTrue; michael@0: break; michael@0: case DoubleLessThanOrEqual: michael@0: as_cole(fmt, lhs, rhs, fcc); michael@0: *testKind = TestForTrue; michael@0: break; michael@0: case DoubleUnordered: michael@0: as_cun(fmt, lhs, rhs, fcc); michael@0: *testKind = TestForTrue; michael@0: break; michael@0: case DoubleEqualOrUnordered: michael@0: as_cueq(fmt, lhs, rhs, fcc); michael@0: *testKind = TestForTrue; michael@0: break; michael@0: case DoubleNotEqualOrUnordered: michael@0: as_ceq(fmt, lhs, rhs, fcc); michael@0: *testKind = TestForFalse; michael@0: break; michael@0: case DoubleGreaterThanOrUnordered: michael@0: as_cult(fmt, rhs, lhs, fcc); michael@0: *testKind = TestForTrue; michael@0: break; michael@0: case DoubleGreaterThanOrEqualOrUnordered: michael@0: as_cule(fmt, rhs, lhs, fcc); michael@0: *testKind = TestForTrue; michael@0: break; michael@0: case DoubleLessThanOrUnordered: michael@0: as_cult(fmt, lhs, rhs, fcc); michael@0: *testKind = TestForTrue; michael@0: break; michael@0: case DoubleLessThanOrEqualOrUnordered: michael@0: as_cule(fmt, lhs, rhs, fcc); michael@0: *testKind = TestForTrue; michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid DoubleCondition."); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_cmp_set_double(Register dest, FloatRegister lhs, FloatRegister rhs, michael@0: DoubleCondition c) michael@0: { michael@0: ma_li(dest, Imm32(0)); michael@0: ma_li(ScratchRegister, Imm32(1)); michael@0: michael@0: FloatTestKind moveCondition; michael@0: compareFloatingPoint(DoubleFloat, lhs, rhs, c, &moveCondition); michael@0: michael@0: if (moveCondition == TestForTrue) michael@0: as_movt(dest, ScratchRegister); michael@0: else michael@0: as_movf(dest, ScratchRegister); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_cmp_set_float32(Register dest, FloatRegister lhs, FloatRegister rhs, michael@0: DoubleCondition c) michael@0: { michael@0: ma_li(dest, Imm32(0)); michael@0: ma_li(ScratchRegister, Imm32(1)); michael@0: michael@0: FloatTestKind moveCondition; michael@0: compareFloatingPoint(SingleFloat, lhs, rhs, c, &moveCondition); michael@0: michael@0: if (moveCondition == TestForTrue) michael@0: as_movt(dest, ScratchRegister); michael@0: else michael@0: as_movf(dest, ScratchRegister); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_cmp_set(Register rd, Register rs, Imm32 imm, Condition c) michael@0: { michael@0: ma_li(ScratchRegister, imm); michael@0: ma_cmp_set(rd, rs, ScratchRegister, c); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_cmp_set(Register rd, Register rs, Address addr, Condition c) michael@0: { michael@0: ma_lw(ScratchRegister, addr); michael@0: ma_cmp_set(rd, rs, ScratchRegister, c); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_cmp_set(Register dst, Address lhs, Register rhs, Condition c) michael@0: { michael@0: ma_lw(ScratchRegister, lhs); michael@0: ma_cmp_set(dst, ScratchRegister, rhs, c); michael@0: } michael@0: michael@0: // fp instructions michael@0: void michael@0: MacroAssemblerMIPS::ma_lis(FloatRegister dest, float value) michael@0: { michael@0: Imm32 imm(mozilla::BitwiseCast(value)); michael@0: michael@0: ma_li(ScratchRegister, imm); michael@0: moveToFloat32(ScratchRegister, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_lid(FloatRegister dest, double value) michael@0: { michael@0: struct DoubleStruct { michael@0: uint32_t lo; michael@0: uint32_t hi; michael@0: } ; michael@0: DoubleStruct intStruct = mozilla::BitwiseCast(value); michael@0: michael@0: // put hi part of 64 bit value into the odd register michael@0: if (intStruct.hi == 0) { michael@0: moveToDoubleHi(zero, dest); michael@0: } else { michael@0: ma_li(ScratchRegister, Imm32(intStruct.hi)); michael@0: moveToDoubleHi(ScratchRegister, dest); michael@0: } michael@0: michael@0: // put low part of 64 bit value into the even register michael@0: if (intStruct.lo == 0) { michael@0: moveToDoubleLo(zero, dest); michael@0: } else { michael@0: ma_li(ScratchRegister, Imm32(intStruct.lo)); michael@0: moveToDoubleLo(ScratchRegister, dest); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_liNegZero(FloatRegister dest) michael@0: { michael@0: moveToDoubleLo(zero, dest); michael@0: ma_li(ScratchRegister, Imm32(INT_MIN)); michael@0: moveToDoubleHi(ScratchRegister, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_mv(FloatRegister src, ValueOperand dest) michael@0: { michael@0: moveFromDoubleLo(src, dest.payloadReg()); michael@0: moveFromDoubleHi(src, dest.typeReg()); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_mv(ValueOperand src, FloatRegister dest) michael@0: { michael@0: moveToDoubleLo(src.payloadReg(), dest); michael@0: moveToDoubleHi(src.typeReg(), dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_ls(FloatRegister ft, Address address) michael@0: { michael@0: if (Imm16::isInSignedRange(address.offset)) { michael@0: as_ls(ft, address.base, Imm16(address.offset).encode()); michael@0: } else { michael@0: MOZ_ASSERT(address.base != ScratchRegister); michael@0: ma_li(ScratchRegister, Imm32(address.offset)); michael@0: as_addu(ScratchRegister, address.base, ScratchRegister); michael@0: as_ls(ft, ScratchRegister, 0); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_ld(FloatRegister ft, Address address) michael@0: { michael@0: // Use single precision load instructions so we don't have to worry about michael@0: // alignment. michael@0: michael@0: int32_t off2 = address.offset + TAG_OFFSET; michael@0: if (Imm16::isInSignedRange(address.offset) && Imm16::isInSignedRange(off2)) { michael@0: as_ls(ft, address.base, Imm16(address.offset).encode()); michael@0: as_ls(getOddPair(ft), address.base, Imm16(off2).encode()); michael@0: } else { michael@0: ma_li(ScratchRegister, Imm32(address.offset)); michael@0: as_addu(ScratchRegister, address.base, ScratchRegister); michael@0: as_ls(ft, ScratchRegister, PAYLOAD_OFFSET); michael@0: as_ls(getOddPair(ft), ScratchRegister, TAG_OFFSET); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_sd(FloatRegister ft, Address address) michael@0: { michael@0: int32_t off2 = address.offset + TAG_OFFSET; michael@0: if (Imm16::isInSignedRange(address.offset) && Imm16::isInSignedRange(off2)) { michael@0: as_ss(ft, address.base, Imm16(address.offset).encode()); michael@0: as_ss(getOddPair(ft), address.base, Imm16(off2).encode()); michael@0: } else { michael@0: ma_li(ScratchRegister, Imm32(address.offset)); michael@0: as_addu(ScratchRegister, address.base, ScratchRegister); michael@0: as_ss(ft, ScratchRegister, PAYLOAD_OFFSET); michael@0: as_ss(getOddPair(ft), ScratchRegister, TAG_OFFSET); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_sd(FloatRegister ft, BaseIndex address) michael@0: { michael@0: computeScaledAddress(address, SecondScratchReg); michael@0: ma_sd(ft, Address(SecondScratchReg, address.offset)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_ss(FloatRegister ft, Address address) michael@0: { michael@0: if (Imm16::isInSignedRange(address.offset)) { michael@0: as_ss(ft, address.base, Imm16(address.offset).encode()); michael@0: } else { michael@0: ma_li(ScratchRegister, Imm32(address.offset)); michael@0: as_addu(ScratchRegister, address.base, ScratchRegister); michael@0: as_ss(ft, ScratchRegister, 0); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_ss(FloatRegister ft, BaseIndex address) michael@0: { michael@0: computeScaledAddress(address, SecondScratchReg); michael@0: ma_ss(ft, Address(SecondScratchReg, address.offset)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_pop(FloatRegister fs) michael@0: { michael@0: ma_ld(fs, Address(StackPointer, 0)); michael@0: as_addiu(StackPointer, StackPointer, sizeof(double)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_push(FloatRegister fs) michael@0: { michael@0: as_addiu(StackPointer, StackPointer, -sizeof(double)); michael@0: ma_sd(fs, Address(StackPointer, 0)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_bc1s(FloatRegister lhs, FloatRegister rhs, Label *label, michael@0: DoubleCondition c, JumpKind jumpKind, FPConditionBit fcc) michael@0: { michael@0: FloatTestKind testKind; michael@0: compareFloatingPoint(SingleFloat, lhs, rhs, c, &testKind, fcc); michael@0: branchWithCode(getBranchCode(testKind, fcc), label, jumpKind); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_bc1d(FloatRegister lhs, FloatRegister rhs, Label *label, michael@0: DoubleCondition c, JumpKind jumpKind, FPConditionBit fcc) michael@0: { michael@0: FloatTestKind testKind; michael@0: compareFloatingPoint(DoubleFloat, lhs, rhs, c, &testKind, fcc); michael@0: branchWithCode(getBranchCode(testKind, fcc), label, jumpKind); michael@0: } michael@0: michael@0: bool michael@0: MacroAssemblerMIPSCompat::buildFakeExitFrame(const Register &scratch, uint32_t *offset) michael@0: { michael@0: mozilla::DebugOnly initialDepth = framePushed(); michael@0: michael@0: CodeLabel cl; michael@0: ma_li(scratch, cl.dest()); michael@0: michael@0: uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS); michael@0: Push(Imm32(descriptor)); michael@0: Push(scratch); michael@0: michael@0: bind(cl.src()); michael@0: *offset = currentOffset(); michael@0: michael@0: MOZ_ASSERT(framePushed() == initialDepth + IonExitFrameLayout::Size()); michael@0: return addCodeLabel(cl); michael@0: } michael@0: michael@0: bool michael@0: MacroAssemblerMIPSCompat::buildOOLFakeExitFrame(void *fakeReturnAddr) michael@0: { michael@0: DebugOnly initialDepth = framePushed(); michael@0: uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS); michael@0: michael@0: Push(Imm32(descriptor)); // descriptor_ michael@0: Push(ImmPtr(fakeReturnAddr)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::callWithExitFrame(JitCode *target) michael@0: { michael@0: uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS); michael@0: Push(Imm32(descriptor)); // descriptor michael@0: michael@0: addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE); michael@0: ma_liPatchable(ScratchRegister, ImmPtr(target->raw())); michael@0: ma_callIonHalfPush(ScratchRegister); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::callWithExitFrame(JitCode *target, Register dynStack) michael@0: { michael@0: ma_addu(dynStack, dynStack, Imm32(framePushed())); michael@0: makeFrameDescriptor(dynStack, JitFrame_IonJS); michael@0: Push(dynStack); // descriptor michael@0: michael@0: addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE); michael@0: ma_liPatchable(ScratchRegister, ImmPtr(target->raw())); michael@0: ma_callIonHalfPush(ScratchRegister); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::callIon(const Register &callee) michael@0: { michael@0: MOZ_ASSERT((framePushed() & 3) == 0); michael@0: if ((framePushed() & 7) == 4) { michael@0: ma_callIonHalfPush(callee); michael@0: } else { michael@0: adjustFrame(sizeof(uint32_t)); michael@0: ma_callIon(callee); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::reserveStack(uint32_t amount) michael@0: { michael@0: if (amount) michael@0: ma_subu(StackPointer, StackPointer, Imm32(amount)); michael@0: adjustFrame(amount); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::freeStack(uint32_t amount) michael@0: { michael@0: MOZ_ASSERT(amount <= framePushed_); michael@0: if (amount) michael@0: ma_addu(StackPointer, StackPointer, Imm32(amount)); michael@0: adjustFrame(-amount); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::freeStack(Register amount) michael@0: { michael@0: as_addu(StackPointer, StackPointer, amount); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::PushRegsInMask(RegisterSet set) michael@0: { michael@0: int32_t diffF = set.fpus().size() * sizeof(double); michael@0: int32_t diffG = set.gprs().size() * sizeof(intptr_t); michael@0: michael@0: reserveStack(diffG); michael@0: for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) { michael@0: diffG -= sizeof(intptr_t); michael@0: storePtr(*iter, Address(StackPointer, diffG)); michael@0: } michael@0: MOZ_ASSERT(diffG == 0); michael@0: michael@0: // Double values have to be aligned. We reserve extra space so that we can michael@0: // start writing from the first aligned location. michael@0: // We reserve a whole extra double so that the buffer has even size. michael@0: ma_and(SecondScratchReg, sp, Imm32(~(StackAlignment - 1))); michael@0: reserveStack(diffF + sizeof(double)); michael@0: michael@0: for (FloatRegisterForwardIterator iter(set.fpus()); iter.more(); iter++) { michael@0: // Use assembly s.d because we have alligned the stack. michael@0: // :TODO: (Bug 972836) Fix this once odd regs can be used as michael@0: // float32 only. For now we skip saving odd regs for O32 ABI. michael@0: michael@0: // :TODO: (Bug 985881) Make a switch for N32 ABI. michael@0: if ((*iter).code() % 2 == 0) michael@0: as_sd(*iter, SecondScratchReg, -diffF); michael@0: diffF -= sizeof(double); michael@0: } michael@0: MOZ_ASSERT(diffF == 0); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore) michael@0: { michael@0: int32_t diffG = set.gprs().size() * sizeof(intptr_t); michael@0: int32_t diffF = set.fpus().size() * sizeof(double); michael@0: const int32_t reservedG = diffG; michael@0: const int32_t reservedF = diffF; michael@0: michael@0: // Read the buffer form the first aligned location. michael@0: ma_addu(SecondScratchReg, sp, Imm32(reservedF + sizeof(double))); michael@0: ma_and(SecondScratchReg, SecondScratchReg, Imm32(~(StackAlignment - 1))); michael@0: michael@0: for (FloatRegisterForwardIterator iter(set.fpus()); iter.more(); iter++) { michael@0: // :TODO: (Bug 972836) Fix this once odd regs can be used as michael@0: // float32 only. For now we skip loading odd regs for O32 ABI. michael@0: michael@0: // :TODO: (Bug 985881) Make a switch for N32 ABI. michael@0: if (!ignore.has(*iter) && ((*iter).code() % 2 == 0)) michael@0: // Use assembly l.d because we have alligned the stack. michael@0: as_ld(*iter, SecondScratchReg, -diffF); michael@0: diffF -= sizeof(double); michael@0: } michael@0: freeStack(reservedF + sizeof(double)); michael@0: MOZ_ASSERT(diffF == 0); michael@0: michael@0: for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) { michael@0: diffG -= sizeof(intptr_t); michael@0: if (!ignore.has(*iter)) michael@0: loadPtr(Address(StackPointer, diffG), *iter); michael@0: } michael@0: freeStack(reservedG); michael@0: MOZ_ASSERT(diffG == 0); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::add32(Register src, Register dest) michael@0: { michael@0: as_addu(dest, dest, src); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::add32(Imm32 imm, Register dest) michael@0: { michael@0: ma_addu(dest, dest, imm); michael@0: } michael@0: michael@0: void michael@0: michael@0: MacroAssemblerMIPSCompat::add32(Imm32 imm, const Address &dest) michael@0: { michael@0: load32(dest, SecondScratchReg); michael@0: ma_addu(SecondScratchReg, imm); michael@0: store32(SecondScratchReg, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::sub32(Imm32 imm, Register dest) michael@0: { michael@0: ma_subu(dest, dest, imm); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::sub32(Register src, Register dest) michael@0: { michael@0: ma_subu(dest, dest, src); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::addPtr(Register src, Register dest) michael@0: { michael@0: ma_addu(dest, src); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::addPtr(const Address &src, Register dest) michael@0: { michael@0: loadPtr(src, ScratchRegister); michael@0: ma_addu(dest, ScratchRegister); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::subPtr(Register src, Register dest) michael@0: { michael@0: ma_subu(dest, dest, src); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::not32(Register reg) michael@0: { michael@0: ma_not(reg, reg); michael@0: } michael@0: michael@0: // Logical operations michael@0: void michael@0: MacroAssemblerMIPSCompat::and32(Imm32 imm, Register dest) michael@0: { michael@0: ma_and(dest, imm); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::and32(Imm32 imm, const Address &dest) michael@0: { michael@0: load32(dest, SecondScratchReg); michael@0: ma_and(SecondScratchReg, imm); michael@0: store32(SecondScratchReg, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::or32(Imm32 imm, const Address &dest) michael@0: { michael@0: load32(dest, SecondScratchReg); michael@0: ma_or(SecondScratchReg, imm); michael@0: store32(SecondScratchReg, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::xor32(Imm32 imm, Register dest) michael@0: { michael@0: ma_xor(dest, imm); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::xorPtr(Imm32 imm, Register dest) michael@0: { michael@0: ma_xor(dest, imm); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::xorPtr(Register src, Register dest) michael@0: { michael@0: ma_xor(dest, src); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::orPtr(Imm32 imm, Register dest) michael@0: { michael@0: ma_or(dest, imm); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::orPtr(Register src, Register dest) michael@0: { michael@0: ma_or(dest, src); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::andPtr(Imm32 imm, Register dest) michael@0: { michael@0: ma_and(dest, imm); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::andPtr(Register src, Register dest) michael@0: { michael@0: ma_and(dest, src); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::move32(const Imm32 &imm, const Register &dest) michael@0: { michael@0: ma_li(dest, imm); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::move32(const Register &src, const Register &dest) michael@0: { michael@0: ma_move(dest, src); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::movePtr(const Register &src, const Register &dest) michael@0: { michael@0: ma_move(dest, src); michael@0: } michael@0: void michael@0: MacroAssemblerMIPSCompat::movePtr(const ImmWord &imm, const Register &dest) michael@0: { michael@0: ma_li(dest, Imm32(imm.value)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::movePtr(const ImmGCPtr &imm, const Register &dest) michael@0: { michael@0: ma_li(dest, imm); michael@0: } michael@0: void michael@0: MacroAssemblerMIPSCompat::movePtr(const ImmPtr &imm, const Register &dest) michael@0: { michael@0: movePtr(ImmWord(uintptr_t(imm.value)), dest); michael@0: } michael@0: void michael@0: MacroAssemblerMIPSCompat::movePtr(const AsmJSImmPtr &imm, const Register &dest) michael@0: { michael@0: MOZ_ASSUME_UNREACHABLE("NYI"); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::load8ZeroExtend(const Address &address, const Register &dest) michael@0: { michael@0: ma_load(dest, address, SizeByte, ZeroExtend); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::load8ZeroExtend(const BaseIndex &src, const Register &dest) michael@0: { michael@0: ma_load(dest, src, SizeByte, ZeroExtend); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::load8SignExtend(const Address &address, const Register &dest) michael@0: { michael@0: ma_load(dest, address, SizeByte, SignExtend); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::load8SignExtend(const BaseIndex &src, const Register &dest) michael@0: { michael@0: ma_load(dest, src, SizeByte, SignExtend); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::load16ZeroExtend(const Address &address, const Register &dest) michael@0: { michael@0: ma_load(dest, address, SizeHalfWord, ZeroExtend); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::load16ZeroExtend(const BaseIndex &src, const Register &dest) michael@0: { michael@0: ma_load(dest, src, SizeHalfWord, ZeroExtend); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::load16SignExtend(const Address &address, const Register &dest) michael@0: { michael@0: ma_load(dest, address, SizeHalfWord, SignExtend); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::load16SignExtend(const BaseIndex &src, const Register &dest) michael@0: { michael@0: ma_load(dest, src, SizeHalfWord, SignExtend); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::load32(const Address &address, const Register &dest) michael@0: { michael@0: ma_lw(dest, address); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::load32(const BaseIndex &address, const Register &dest) michael@0: { michael@0: ma_load(dest, address, SizeWord); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::load32(const AbsoluteAddress &address, const Register &dest) michael@0: { michael@0: ma_li(ScratchRegister, Imm32((uint32_t)address.addr)); michael@0: as_lw(dest, ScratchRegister, 0); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadPtr(const Address &address, const Register &dest) michael@0: { michael@0: ma_lw(dest, address); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadPtr(const BaseIndex &src, const Register &dest) michael@0: { michael@0: load32(src, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadPtr(const AbsoluteAddress &address, const Register &dest) michael@0: { michael@0: ma_li(ScratchRegister, Imm32((uint32_t)address.addr)); michael@0: as_lw(dest, ScratchRegister, 0); michael@0: } michael@0: void michael@0: MacroAssemblerMIPSCompat::loadPtr(const AsmJSAbsoluteAddress &address, const Register &dest) michael@0: { michael@0: movePtr(AsmJSImmPtr(address.kind()), ScratchRegister); michael@0: loadPtr(Address(ScratchRegister, 0x0), dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadPrivate(const Address &address, const Register &dest) michael@0: { michael@0: ma_lw(dest, Address(address.base, address.offset + PAYLOAD_OFFSET)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadDouble(const Address &address, const FloatRegister &dest) michael@0: { michael@0: ma_ld(dest, address); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadDouble(const BaseIndex &src, const FloatRegister &dest) michael@0: { michael@0: computeScaledAddress(src, SecondScratchReg); michael@0: ma_ld(dest, Address(SecondScratchReg, src.offset)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadFloatAsDouble(const Address &address, const FloatRegister &dest) michael@0: { michael@0: ma_ls(dest, address); michael@0: as_cvtds(dest, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadFloatAsDouble(const BaseIndex &src, const FloatRegister &dest) michael@0: { michael@0: loadFloat32(src, dest); michael@0: as_cvtds(dest, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadFloat32(const Address &address, const FloatRegister &dest) michael@0: { michael@0: ma_ls(dest, address); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadFloat32(const BaseIndex &src, const FloatRegister &dest) michael@0: { michael@0: computeScaledAddress(src, SecondScratchReg); michael@0: ma_ls(dest, Address(SecondScratchReg, src.offset)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::store8(const Imm32 &imm, const Address &address) michael@0: { michael@0: ma_li(SecondScratchReg, imm); michael@0: ma_store(SecondScratchReg, address, SizeByte); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::store8(const Register &src, const Address &address) michael@0: { michael@0: ma_store(src, address, SizeByte); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::store8(const Imm32 &imm, const BaseIndex &dest) michael@0: { michael@0: ma_store(imm, dest, SizeByte); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::store8(const Register &src, const BaseIndex &dest) michael@0: { michael@0: ma_store(src, dest, SizeByte); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::store16(const Imm32 &imm, const Address &address) michael@0: { michael@0: ma_li(SecondScratchReg, imm); michael@0: ma_store(SecondScratchReg, address, SizeHalfWord); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::store16(const Register &src, const Address &address) michael@0: { michael@0: ma_store(src, address, SizeHalfWord); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::store16(const Imm32 &imm, const BaseIndex &dest) michael@0: { michael@0: ma_store(imm, dest, SizeHalfWord); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::store16(const Register &src, const BaseIndex &address) michael@0: { michael@0: ma_store(src, address, SizeHalfWord); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::store32(const Register &src, const AbsoluteAddress &address) michael@0: { michael@0: storePtr(src, address); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::store32(const Register &src, const Address &address) michael@0: { michael@0: storePtr(src, address); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::store32(const Imm32 &src, const Address &address) michael@0: { michael@0: move32(src, ScratchRegister); michael@0: storePtr(ScratchRegister, address); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::store32(const Imm32 &imm, const BaseIndex &dest) michael@0: { michael@0: ma_store(imm, dest, SizeWord); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::store32(const Register &src, const BaseIndex &dest) michael@0: { michael@0: ma_store(src, dest, SizeWord); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storePtr(ImmWord imm, const Address &address) michael@0: { michael@0: ma_li(ScratchRegister, Imm32(imm.value)); michael@0: ma_sw(ScratchRegister, address); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storePtr(ImmPtr imm, const Address &address) michael@0: { michael@0: storePtr(ImmWord(uintptr_t(imm.value)), address); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storePtr(ImmGCPtr imm, const Address &address) michael@0: { michael@0: ma_li(ScratchRegister, imm); michael@0: ma_sw(ScratchRegister, address); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storePtr(Register src, const Address &address) michael@0: { michael@0: ma_sw(src, address); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storePtr(const Register &src, const AbsoluteAddress &dest) michael@0: { michael@0: ma_li(ScratchRegister, Imm32((uint32_t)dest.addr)); michael@0: as_sw(src, ScratchRegister, 0); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::subPtr(Imm32 imm, const Register dest) michael@0: { michael@0: ma_subu(dest, dest, imm); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::addPtr(Imm32 imm, const Register dest) michael@0: { michael@0: ma_addu(dest, imm); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::addPtr(Imm32 imm, const Address &dest) michael@0: { michael@0: loadPtr(dest, ScratchRegister); michael@0: addPtr(imm, ScratchRegister); michael@0: storePtr(ScratchRegister, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchDouble(DoubleCondition cond, const FloatRegister &lhs, michael@0: const FloatRegister &rhs, Label *label) michael@0: { michael@0: ma_bc1d(lhs, rhs, label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchFloat(DoubleCondition cond, const FloatRegister &lhs, michael@0: const FloatRegister &rhs, Label *label) michael@0: { michael@0: ma_bc1s(lhs, rhs, label, cond); michael@0: } michael@0: michael@0: // higher level tag testing code michael@0: Operand michael@0: ToPayload(Operand base) michael@0: { michael@0: return Operand(Register::FromCode(base.base()), base.disp() + PAYLOAD_OFFSET); michael@0: } michael@0: michael@0: Operand michael@0: ToType(Operand base) michael@0: { michael@0: return Operand(Register::FromCode(base.base()), base.disp() + TAG_OFFSET); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestGCThing(Condition cond, const Address &address, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: extractTag(address, SecondScratchReg); michael@0: ma_b(SecondScratchReg, ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET), label, michael@0: (cond == Equal) ? AboveOrEqual : Below); michael@0: } michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestGCThing(Condition cond, const BaseIndex &src, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: extractTag(src, SecondScratchReg); michael@0: ma_b(SecondScratchReg, ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET), label, michael@0: (cond == Equal) ? AboveOrEqual : Below); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestPrimitive(Condition cond, const ValueOperand &value, michael@0: Label *label) michael@0: { michael@0: branchTestPrimitive(cond, value.typeReg(), label); michael@0: } michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestPrimitive(Condition cond, const Register &tag, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: ma_b(tag, ImmTag(JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET), label, michael@0: (cond == Equal) ? Below : AboveOrEqual); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestInt32(Condition cond, const ValueOperand &value, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); michael@0: ma_b(value.typeReg(), ImmType(JSVAL_TYPE_INT32), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestInt32(Condition cond, const Register &tag, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: ma_b(tag, ImmTag(JSVAL_TAG_INT32), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestInt32(Condition cond, const Address &address, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: extractTag(address, SecondScratchReg); michael@0: ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_INT32), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestInt32(Condition cond, const BaseIndex &src, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: extractTag(src, SecondScratchReg); michael@0: ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_INT32), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat:: branchTestBoolean(Condition cond, const ValueOperand &value, michael@0: Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); michael@0: ma_b(value.typeReg(), ImmType(JSVAL_TYPE_BOOLEAN), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat:: branchTestBoolean(Condition cond, const Register &tag, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); michael@0: ma_b(tag, ImmType(JSVAL_TYPE_BOOLEAN), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestBoolean(Condition cond, const BaseIndex &src, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: extractTag(src, SecondScratchReg); michael@0: ma_b(SecondScratchReg, ImmType(JSVAL_TYPE_BOOLEAN), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestDouble(Condition cond, const ValueOperand &value, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); michael@0: Assembler::Condition actual = (cond == Equal) ? Below : AboveOrEqual; michael@0: ma_b(value.typeReg(), ImmTag(JSVAL_TAG_CLEAR), label, actual); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestDouble(Condition cond, const Register &tag, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Assembler::Equal || cond == NotEqual); michael@0: Condition actual = (cond == Equal) ? Below : AboveOrEqual; michael@0: ma_b(tag, ImmTag(JSVAL_TAG_CLEAR), label, actual); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestDouble(Condition cond, const Address &address, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: extractTag(address, SecondScratchReg); michael@0: ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_CLEAR), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestDouble(Condition cond, const BaseIndex &src, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: Condition actual = (cond == Equal) ? Below : AboveOrEqual; michael@0: extractTag(src, SecondScratchReg); michael@0: ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_CLEAR), label, actual); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestNull(Condition cond, const ValueOperand &value, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: ma_b(value.typeReg(), ImmType(JSVAL_TYPE_NULL), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestNull(Condition cond, const Register &tag, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: ma_b(tag, ImmTag(JSVAL_TAG_NULL), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestNull(Condition cond, const BaseIndex &src, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: extractTag(src, SecondScratchReg); michael@0: ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_NULL), label, cond); michael@0: } michael@0: michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestObject(Condition cond, const ValueOperand &value, Label *label) michael@0: { michael@0: branchTestObject(cond, value.typeReg(), label); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestObject(Condition cond, const Register &tag, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: ma_b(tag, ImmTag(JSVAL_TAG_OBJECT), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestObject(Condition cond, const BaseIndex &src, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: extractTag(src, SecondScratchReg); michael@0: ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_OBJECT), label, cond); michael@0: } michael@0: michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestString(Condition cond, const ValueOperand &value, Label *label) michael@0: { michael@0: branchTestString(cond, value.typeReg(), label); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestString(Condition cond, const Register &tag, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: ma_b(tag, ImmTag(JSVAL_TAG_STRING), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestString(Condition cond, const BaseIndex &src, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: extractTag(src, SecondScratchReg); michael@0: ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_STRING), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestUndefined(Condition cond, const ValueOperand &value, michael@0: Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); michael@0: ma_b(value.typeReg(), ImmType(JSVAL_TYPE_UNDEFINED), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestUndefined(Condition cond, const Register &tag, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: ma_b(tag, ImmTag(JSVAL_TAG_UNDEFINED), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestUndefined(Condition cond, const BaseIndex &src, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: extractTag(src, SecondScratchReg); michael@0: ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_UNDEFINED), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestUndefined(Condition cond, const Address &address, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: extractTag(address, SecondScratchReg); michael@0: ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_UNDEFINED), label, cond); michael@0: } michael@0: michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestNumber(Condition cond, const ValueOperand &value, Label *label) michael@0: { michael@0: branchTestNumber(cond, value.typeReg(), label); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestNumber(Condition cond, const Register &tag, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: ma_b(tag, ImmTag(JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET), label, michael@0: cond == Equal ? BelowOrEqual : Above); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestMagic(Condition cond, const ValueOperand &value, Label *label) michael@0: { michael@0: branchTestMagic(cond, value.typeReg(), label); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestMagic(Condition cond, const Register &tag, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: ma_b(tag, ImmTag(JSVAL_TAG_MAGIC), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestMagic(Condition cond, const Address &address, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: extractTag(address, SecondScratchReg); michael@0: ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_MAGIC), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestMagic(Condition cond, const BaseIndex &src, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: extractTag(src, SecondScratchReg); michael@0: ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_MAGIC), label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestValue(Condition cond, const ValueOperand &value, michael@0: const Value &v, Label *label) michael@0: { michael@0: moveData(v, ScratchRegister); michael@0: michael@0: if (cond == Equal) { michael@0: Label done; michael@0: ma_b(value.payloadReg(), ScratchRegister, &done, NotEqual, ShortJump); michael@0: { michael@0: ma_b(value.typeReg(), Imm32(getType(v)), label, Equal); michael@0: } michael@0: bind(&done); michael@0: } else { michael@0: MOZ_ASSERT(cond == NotEqual); michael@0: ma_b(value.payloadReg(), ScratchRegister, label, NotEqual); michael@0: michael@0: ma_b(value.typeReg(), Imm32(getType(v)), label, NotEqual); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestValue(Condition cond, const Address &valaddr, michael@0: const ValueOperand &value, Label *label) michael@0: { michael@0: MOZ_ASSERT(cond == Equal || cond == NotEqual); michael@0: michael@0: // Load tag. michael@0: ma_lw(ScratchRegister, Address(valaddr.base, valaddr.offset + TAG_OFFSET)); michael@0: branchPtr(cond, ScratchRegister, value.typeReg(), label); michael@0: michael@0: // Load payload michael@0: ma_lw(ScratchRegister, Address(valaddr.base, valaddr.offset + PAYLOAD_OFFSET)); michael@0: branchPtr(cond, ScratchRegister, value.payloadReg(), label); michael@0: } michael@0: michael@0: // unboxing code michael@0: void michael@0: MacroAssemblerMIPSCompat::unboxInt32(const ValueOperand &operand, const Register &dest) michael@0: { michael@0: ma_move(dest, operand.payloadReg()); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::unboxInt32(const Address &src, const Register &dest) michael@0: { michael@0: ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::unboxBoolean(const ValueOperand &operand, const Register &dest) michael@0: { michael@0: ma_move(dest, operand.payloadReg()); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::unboxBoolean(const Address &src, const Register &dest) michael@0: { michael@0: ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::unboxDouble(const ValueOperand &operand, const FloatRegister &dest) michael@0: { michael@0: MOZ_ASSERT(dest != ScratchFloatReg); michael@0: moveToDoubleLo(operand.payloadReg(), dest); michael@0: moveToDoubleHi(operand.typeReg(), dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::unboxDouble(const Address &src, const FloatRegister &dest) michael@0: { michael@0: ma_lw(ScratchRegister, Address(src.base, src.offset + PAYLOAD_OFFSET)); michael@0: moveToDoubleLo(ScratchRegister, dest); michael@0: ma_lw(ScratchRegister, Address(src.base, src.offset + TAG_OFFSET)); michael@0: moveToDoubleHi(ScratchRegister, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::unboxString(const ValueOperand &operand, const Register &dest) michael@0: { michael@0: ma_move(dest, operand.payloadReg()); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::unboxString(const Address &src, const Register &dest) michael@0: { michael@0: ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::unboxObject(const ValueOperand &src, const Register &dest) michael@0: { michael@0: ma_move(dest, src.payloadReg()); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::unboxValue(const ValueOperand &src, AnyRegister dest) michael@0: { michael@0: if (dest.isFloat()) { michael@0: Label notInt32, end; michael@0: branchTestInt32(Assembler::NotEqual, src, ¬Int32); michael@0: convertInt32ToDouble(src.payloadReg(), dest.fpu()); michael@0: ma_b(&end, ShortJump); michael@0: bind(¬Int32); michael@0: unboxDouble(src, dest.fpu()); michael@0: bind(&end); michael@0: } else if (src.payloadReg() != dest.gpr()) { michael@0: ma_move(dest.gpr(), src.payloadReg()); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::unboxPrivate(const ValueOperand &src, Register dest) michael@0: { michael@0: ma_move(dest, src.payloadReg()); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::boxDouble(const FloatRegister &src, const ValueOperand &dest) michael@0: { michael@0: moveFromDoubleLo(src, dest.payloadReg()); michael@0: moveFromDoubleHi(src, dest.typeReg()); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::boxNonDouble(JSValueType type, const Register &src, michael@0: const ValueOperand &dest) michael@0: { michael@0: if (src != dest.payloadReg()) michael@0: ma_move(dest.payloadReg(), src); michael@0: ma_li(dest.typeReg(), ImmType(type)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::boolValueToDouble(const ValueOperand &operand, const FloatRegister &dest) michael@0: { michael@0: convertBoolToInt32(ScratchRegister, operand.payloadReg()); michael@0: convertInt32ToDouble(ScratchRegister, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::int32ValueToDouble(const ValueOperand &operand, michael@0: const FloatRegister &dest) michael@0: { michael@0: convertInt32ToDouble(operand.payloadReg(), dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::boolValueToFloat32(const ValueOperand &operand, michael@0: const FloatRegister &dest) michael@0: { michael@0: michael@0: convertBoolToInt32(ScratchRegister, operand.payloadReg()); michael@0: convertInt32ToFloat32(ScratchRegister, dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::int32ValueToFloat32(const ValueOperand &operand, michael@0: const FloatRegister &dest) michael@0: { michael@0: convertInt32ToFloat32(operand.payloadReg(), dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadConstantFloat32(float f, const FloatRegister &dest) michael@0: { michael@0: ma_lis(dest, f); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadInt32OrDouble(const Address &src, const FloatRegister &dest) michael@0: { michael@0: Label notInt32, end; michael@0: // If it's an int, convert it to double. michael@0: ma_lw(SecondScratchReg, Address(src.base, src.offset + TAG_OFFSET)); michael@0: branchTestInt32(Assembler::NotEqual, SecondScratchReg, ¬Int32); michael@0: ma_lw(SecondScratchReg, Address(src.base, src.offset + PAYLOAD_OFFSET)); michael@0: convertInt32ToDouble(SecondScratchReg, dest); michael@0: ma_b(&end, ShortJump); michael@0: michael@0: // Not an int, just load as double. michael@0: bind(¬Int32); michael@0: ma_ld(dest, src); michael@0: bind(&end); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadInt32OrDouble(Register base, Register index, michael@0: const FloatRegister &dest, int32_t shift) michael@0: { michael@0: Label notInt32, end; michael@0: michael@0: // If it's an int, convert it to double. michael@0: michael@0: computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg); michael@0: // Since we only have one scratch, we need to stomp over it with the tag. michael@0: load32(Address(SecondScratchReg, TAG_OFFSET), SecondScratchReg); michael@0: branchTestInt32(Assembler::NotEqual, SecondScratchReg, ¬Int32); michael@0: michael@0: computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg); michael@0: load32(Address(SecondScratchReg, PAYLOAD_OFFSET), SecondScratchReg); michael@0: convertInt32ToDouble(SecondScratchReg, dest); michael@0: ma_b(&end, ShortJump); michael@0: michael@0: // Not an int, just load as double. michael@0: bind(¬Int32); michael@0: // First, recompute the offset that had been stored in the scratch register michael@0: // since the scratch register was overwritten loading in the type. michael@0: computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg); michael@0: loadDouble(Address(SecondScratchReg, 0), dest); michael@0: bind(&end); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadConstantDouble(double dp, const FloatRegister &dest) michael@0: { michael@0: ma_lid(dest, dp); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestInt32Truthy(bool b, const ValueOperand &value, Label *label) michael@0: { michael@0: ma_and(ScratchRegister, value.payloadReg(), value.payloadReg()); michael@0: ma_b(ScratchRegister, ScratchRegister, label, b ? NonZero : Zero); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestStringTruthy(bool b, const ValueOperand &value, Label *label) michael@0: { michael@0: Register string = value.payloadReg(); michael@0: size_t mask = (0xFFFFFFFF << JSString::LENGTH_SHIFT); michael@0: ma_lw(SecondScratchReg, Address(string, JSString::offsetOfLengthAndFlags())); michael@0: michael@0: // Use SecondScratchReg because ma_and will clobber ScratchRegister michael@0: ma_and(ScratchRegister, SecondScratchReg, Imm32(mask)); michael@0: ma_b(ScratchRegister, ScratchRegister, label, b ? NonZero : Zero); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestDoubleTruthy(bool b, const FloatRegister &value, Label *label) michael@0: { michael@0: ma_lid(ScratchFloatReg, 0.0); michael@0: DoubleCondition cond = b ? DoubleNotEqual : DoubleEqualOrUnordered; michael@0: ma_bc1d(value, ScratchFloatReg, label, cond); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchTestBooleanTruthy(bool b, const ValueOperand &operand, michael@0: Label *label) michael@0: { michael@0: ma_b(operand.payloadReg(), operand.payloadReg(), label, b ? NonZero : Zero); michael@0: } michael@0: michael@0: Register michael@0: MacroAssemblerMIPSCompat::extractObject(const Address &address, Register scratch) michael@0: { michael@0: ma_lw(scratch, Address(address.base, address.offset + PAYLOAD_OFFSET)); michael@0: return scratch; michael@0: } michael@0: michael@0: Register michael@0: MacroAssemblerMIPSCompat::extractTag(const Address &address, Register scratch) michael@0: { michael@0: ma_lw(scratch, Address(address.base, address.offset + TAG_OFFSET)); michael@0: return scratch; michael@0: } michael@0: michael@0: Register michael@0: MacroAssemblerMIPSCompat::extractTag(const BaseIndex &address, Register scratch) michael@0: { michael@0: computeScaledAddress(address, scratch); michael@0: return extractTag(Address(scratch, address.offset), scratch); michael@0: } michael@0: michael@0: michael@0: uint32_t michael@0: MacroAssemblerMIPSCompat::getType(const Value &val) michael@0: { michael@0: jsval_layout jv = JSVAL_TO_IMPL(val); michael@0: return jv.s.tag; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::moveData(const Value &val, Register data) michael@0: { michael@0: jsval_layout jv = JSVAL_TO_IMPL(val); michael@0: if (val.isMarkable()) michael@0: ma_li(data, ImmGCPtr(reinterpret_cast(val.toGCThing()))); michael@0: else michael@0: ma_li(data, Imm32(jv.s.payload.i32)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::moveValue(const Value &val, Register type, Register data) michael@0: { michael@0: MOZ_ASSERT(type != data); michael@0: ma_li(type, Imm32(getType(val))); michael@0: moveData(val, data); michael@0: } michael@0: void michael@0: MacroAssemblerMIPSCompat::moveValue(const Value &val, const ValueOperand &dest) michael@0: { michael@0: moveValue(val, dest.typeReg(), dest.payloadReg()); michael@0: } michael@0: michael@0: CodeOffsetJump michael@0: MacroAssemblerMIPSCompat::jumpWithPatch(RepatchLabel *label) michael@0: { michael@0: // Only one branch per label. michael@0: MOZ_ASSERT(!label->used()); michael@0: uint32_t dest = label->bound() ? label->offset() : LabelBase::INVALID_OFFSET; michael@0: michael@0: BufferOffset bo = nextOffset(); michael@0: label->use(bo.getOffset()); michael@0: addLongJump(bo); michael@0: ma_liPatchable(ScratchRegister, Imm32(dest)); michael@0: as_jr(ScratchRegister); michael@0: as_nop(); michael@0: return CodeOffsetJump(bo.getOffset()); michael@0: } michael@0: michael@0: michael@0: ///////////////////////////////////////////////////////////////// michael@0: // X86/X64-common/ARM/MIPS interface. michael@0: ///////////////////////////////////////////////////////////////// michael@0: void michael@0: MacroAssemblerMIPSCompat::storeValue(ValueOperand val, Operand dst) michael@0: { michael@0: storeValue(val, Address(Register::FromCode(dst.base()), dst.disp())); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storeValue(ValueOperand val, const BaseIndex &dest) michael@0: { michael@0: computeScaledAddress(dest, SecondScratchReg); michael@0: storeValue(val, Address(SecondScratchReg, dest.offset)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storeValue(JSValueType type, Register reg, BaseIndex dest) michael@0: { michael@0: computeScaledAddress(dest, ScratchRegister); michael@0: michael@0: // Make sure that ma_sw doesn't clobber ScratchRegister michael@0: int32_t offset = dest.offset; michael@0: if (!Imm16::isInSignedRange(offset)) { michael@0: ma_li(SecondScratchReg, Imm32(offset)); michael@0: as_addu(ScratchRegister, ScratchRegister, SecondScratchReg); michael@0: offset = 0; michael@0: } michael@0: michael@0: storeValue(type, reg, Address(ScratchRegister, offset)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storeValue(ValueOperand val, const Address &dest) michael@0: { michael@0: ma_sw(val.payloadReg(), Address(dest.base, dest.offset + PAYLOAD_OFFSET)); michael@0: ma_sw(val.typeReg(), Address(dest.base, dest.offset + TAG_OFFSET)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storeValue(JSValueType type, Register reg, Address dest) michael@0: { michael@0: MOZ_ASSERT(dest.base != SecondScratchReg); michael@0: michael@0: ma_sw(reg, Address(dest.base, dest.offset + PAYLOAD_OFFSET)); michael@0: ma_li(SecondScratchReg, ImmTag(JSVAL_TYPE_TO_TAG(type))); michael@0: ma_sw(SecondScratchReg, Address(dest.base, dest.offset + TAG_OFFSET)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storeValue(const Value &val, Address dest) michael@0: { michael@0: MOZ_ASSERT(dest.base != SecondScratchReg); michael@0: michael@0: ma_li(SecondScratchReg, Imm32(getType(val))); michael@0: ma_sw(SecondScratchReg, Address(dest.base, dest.offset + TAG_OFFSET)); michael@0: moveData(val, SecondScratchReg); michael@0: ma_sw(SecondScratchReg, Address(dest.base, dest.offset + PAYLOAD_OFFSET)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storeValue(const Value &val, BaseIndex dest) michael@0: { michael@0: computeScaledAddress(dest, ScratchRegister); michael@0: michael@0: // Make sure that ma_sw doesn't clobber ScratchRegister michael@0: int32_t offset = dest.offset; michael@0: if (!Imm16::isInSignedRange(offset)) { michael@0: ma_li(SecondScratchReg, Imm32(offset)); michael@0: as_addu(ScratchRegister, ScratchRegister, SecondScratchReg); michael@0: offset = 0; michael@0: } michael@0: storeValue(val, Address(ScratchRegister, offset)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadValue(const BaseIndex &addr, ValueOperand val) michael@0: { michael@0: computeScaledAddress(addr, SecondScratchReg); michael@0: loadValue(Address(SecondScratchReg, addr.offset), val); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::loadValue(Address src, ValueOperand val) michael@0: { michael@0: // Ensure that loading the payload does not erase the pointer to the michael@0: // Value in memory. michael@0: if (src.base != val.payloadReg()) { michael@0: ma_lw(val.payloadReg(), Address(src.base, src.offset + PAYLOAD_OFFSET)); michael@0: ma_lw(val.typeReg(), Address(src.base, src.offset + TAG_OFFSET)); michael@0: } else { michael@0: ma_lw(val.typeReg(), Address(src.base, src.offset + TAG_OFFSET)); michael@0: ma_lw(val.payloadReg(), Address(src.base, src.offset + PAYLOAD_OFFSET)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::tagValue(JSValueType type, Register payload, ValueOperand dest) michael@0: { michael@0: MOZ_ASSERT(payload != dest.typeReg()); michael@0: ma_li(dest.typeReg(), ImmType(type)); michael@0: if (payload != dest.payloadReg()) michael@0: ma_move(dest.payloadReg(), payload); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::pushValue(ValueOperand val) michael@0: { michael@0: // Allocate stack slots for type and payload. One for each. michael@0: ma_subu(StackPointer, StackPointer, Imm32(sizeof(Value))); michael@0: // Store type and payload. michael@0: storeValue(val, Address(StackPointer, 0)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::pushValue(const Address &addr) michael@0: { michael@0: // Allocate stack slots for type and payload. One for each. michael@0: ma_subu(StackPointer, StackPointer, Imm32(sizeof(Value))); michael@0: // Store type and payload. michael@0: ma_lw(ScratchRegister, Address(addr.base, addr.offset + TAG_OFFSET)); michael@0: ma_sw(ScratchRegister, Address(StackPointer, TAG_OFFSET)); michael@0: ma_lw(ScratchRegister, Address(addr.base, addr.offset + PAYLOAD_OFFSET)); michael@0: ma_sw(ScratchRegister, Address(StackPointer, PAYLOAD_OFFSET)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::popValue(ValueOperand val) michael@0: { michael@0: // Load payload and type. michael@0: as_lw(val.payloadReg(), StackPointer, PAYLOAD_OFFSET); michael@0: as_lw(val.typeReg(), StackPointer, TAG_OFFSET); michael@0: // Free stack. michael@0: as_addiu(StackPointer, StackPointer, sizeof(Value)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storePayload(const Value &val, Address dest) michael@0: { michael@0: moveData(val, SecondScratchReg); michael@0: ma_sw(SecondScratchReg, Address(dest.base, dest.offset + PAYLOAD_OFFSET)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storePayload(Register src, Address dest) michael@0: { michael@0: ma_sw(src, Address(dest.base, dest.offset + PAYLOAD_OFFSET)); michael@0: return; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storePayload(const Value &val, Register base, Register index, michael@0: int32_t shift) michael@0: { michael@0: computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg); michael@0: michael@0: moveData(val, ScratchRegister); michael@0: michael@0: as_sw(ScratchRegister, SecondScratchReg, NUNBOX32_PAYLOAD_OFFSET); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storePayload(Register src, Register base, Register index, int32_t shift) michael@0: { michael@0: computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg); michael@0: as_sw(src, SecondScratchReg, NUNBOX32_PAYLOAD_OFFSET); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storeTypeTag(ImmTag tag, Address dest) michael@0: { michael@0: ma_li(SecondScratchReg, tag); michael@0: ma_sw(SecondScratchReg, Address(dest.base, dest.offset + TAG_OFFSET)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::storeTypeTag(ImmTag tag, Register base, Register index, int32_t shift) michael@0: { michael@0: computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg); michael@0: ma_li(ScratchRegister, tag); michael@0: as_sw(ScratchRegister, SecondScratchReg, TAG_OFFSET); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::linkExitFrame() michael@0: { michael@0: uint8_t *dest = (uint8_t*)GetIonContext()->runtime->addressOfIonTop(); michael@0: movePtr(ImmPtr(dest), ScratchRegister); michael@0: ma_sw(StackPointer, Address(ScratchRegister, 0)); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::linkParallelExitFrame(const Register &pt) michael@0: { michael@0: ma_sw(StackPointer, Address(pt, offsetof(PerThreadData, ionTop))); michael@0: } michael@0: michael@0: // This macrosintruction calls the ion code and pushes the return address to michael@0: // the stack in the case when stack is alligned. michael@0: void michael@0: MacroAssemblerMIPS::ma_callIon(const Register r) michael@0: { michael@0: // This is a MIPS hack to push return address during jalr delay slot. michael@0: as_addiu(StackPointer, StackPointer, -2 * sizeof(intptr_t)); michael@0: as_jalr(r); michael@0: as_sw(ra, StackPointer, 0); michael@0: } michael@0: michael@0: // This macrosintruction calls the ion code and pushes the return address to michael@0: // the stack in the case when stack is not alligned. michael@0: void michael@0: MacroAssemblerMIPS::ma_callIonHalfPush(const Register r) michael@0: { michael@0: // This is a MIPS hack to push return address during jalr delay slot. michael@0: as_addiu(StackPointer, StackPointer, -sizeof(intptr_t)); michael@0: as_jalr(r); michael@0: as_sw(ra, StackPointer, 0); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_call(ImmPtr dest) michael@0: { michael@0: ma_liPatchable(CallReg, dest); michael@0: as_jalr(CallReg); michael@0: as_nop(); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPS::ma_jump(ImmPtr dest) michael@0: { michael@0: ma_liPatchable(ScratchRegister, dest); michael@0: as_jr(ScratchRegister); michael@0: as_nop(); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::breakpoint() michael@0: { michael@0: as_break(0); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::ensureDouble(const ValueOperand &source, FloatRegister dest, michael@0: Label *failure) michael@0: { michael@0: Label isDouble, done; michael@0: branchTestDouble(Assembler::Equal, source.typeReg(), &isDouble); michael@0: branchTestInt32(Assembler::NotEqual, source.typeReg(), failure); michael@0: michael@0: convertInt32ToDouble(source.payloadReg(), dest); michael@0: jump(&done); michael@0: michael@0: bind(&isDouble); michael@0: unboxDouble(source, dest); michael@0: michael@0: bind(&done); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::setupABICall(uint32_t args) michael@0: { michael@0: MOZ_ASSERT(!inCall_); michael@0: inCall_ = true; michael@0: args_ = args; michael@0: passedArgs_ = 0; michael@0: michael@0: usedArgSlots_ = 0; michael@0: firstArgType = MoveOp::GENERAL; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::setupAlignedABICall(uint32_t args) michael@0: { michael@0: setupABICall(args); michael@0: michael@0: dynamicAlignment_ = false; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::setupUnalignedABICall(uint32_t args, const Register &scratch) michael@0: { michael@0: setupABICall(args); michael@0: dynamicAlignment_ = true; michael@0: michael@0: ma_move(scratch, StackPointer); michael@0: michael@0: // Force sp to be aligned michael@0: ma_subu(StackPointer, StackPointer, Imm32(sizeof(uint32_t))); michael@0: ma_and(StackPointer, StackPointer, Imm32(~(StackAlignment - 1))); michael@0: as_sw(scratch, StackPointer, 0); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::passABIArg(const MoveOperand &from, MoveOp::Type type) michael@0: { michael@0: ++passedArgs_; michael@0: if (!enoughMemory_) michael@0: return; michael@0: switch (type) { michael@0: case MoveOp::FLOAT32: michael@0: if (!usedArgSlots_) { michael@0: if (from.floatReg() != f12) michael@0: enoughMemory_ = moveResolver_.addMove(from, MoveOperand(f12), type); michael@0: firstArgType = MoveOp::FLOAT32; michael@0: } else if ((usedArgSlots_ == 1 && firstArgType == MoveOp::FLOAT32) || michael@0: (usedArgSlots_ == 2 && firstArgType == MoveOp::DOUBLE)) { michael@0: if (from.floatReg() != f14) michael@0: enoughMemory_ = moveResolver_.addMove(from, MoveOperand(f14), type); michael@0: } else { michael@0: Register destReg; michael@0: if (GetIntArgReg(usedArgSlots_, &destReg)) { michael@0: if (from.isGeneralReg() && from.reg() == destReg) { michael@0: // Nothing to do. Value is in the right register already michael@0: } else { michael@0: enoughMemory_ = moveResolver_.addMove(from, MoveOperand(destReg), type); michael@0: } michael@0: } else { michael@0: uint32_t disp = GetArgStackDisp(usedArgSlots_); michael@0: enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), type); michael@0: } michael@0: } michael@0: usedArgSlots_++; michael@0: break; michael@0: case MoveOp::DOUBLE: michael@0: if (!usedArgSlots_) { michael@0: if (from.floatReg() != f12) michael@0: enoughMemory_ = moveResolver_.addMove(from, MoveOperand(f12), type); michael@0: usedArgSlots_ = 2; michael@0: firstArgType = MoveOp::DOUBLE; michael@0: } else if (usedArgSlots_ <= 2) { michael@0: if ((usedArgSlots_ == 1 && firstArgType == MoveOp::FLOAT32) || michael@0: (usedArgSlots_ == 2 && firstArgType == MoveOp::DOUBLE)) { michael@0: if (from.floatReg() != f14) michael@0: enoughMemory_ = moveResolver_.addMove(from, MoveOperand(f14), type); michael@0: } else { michael@0: // Create two moves so that cycles are found. Move emitter michael@0: // will have special case to handle this. michael@0: enoughMemory_ = moveResolver_.addMove(from, MoveOperand(a2), type); michael@0: enoughMemory_ = moveResolver_.addMove(from, MoveOperand(a3), type); michael@0: } michael@0: usedArgSlots_ = 4; michael@0: } else { michael@0: // Align if necessary michael@0: usedArgSlots_ += usedArgSlots_ % 2; michael@0: michael@0: uint32_t disp = GetArgStackDisp(usedArgSlots_); michael@0: enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), type); michael@0: usedArgSlots_ += 2; michael@0: } michael@0: break; michael@0: case MoveOp::GENERAL: michael@0: Register destReg; michael@0: if (GetIntArgReg(usedArgSlots_, &destReg)) { michael@0: if (from.isGeneralReg() && from.reg() == destReg) { michael@0: // Nothing to do. Value is in the right register already michael@0: } else { michael@0: enoughMemory_ = moveResolver_.addMove(from, MoveOperand(destReg), type); michael@0: } michael@0: } else { michael@0: uint32_t disp = GetArgStackDisp(usedArgSlots_); michael@0: enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), type); michael@0: } michael@0: usedArgSlots_++; michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected argument type"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::passABIArg(const Register ®) michael@0: { michael@0: passABIArg(MoveOperand(reg), MoveOp::GENERAL); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::passABIArg(const FloatRegister &freg, MoveOp::Type type) michael@0: { michael@0: passABIArg(MoveOperand(freg), type); michael@0: } michael@0: michael@0: void MacroAssemblerMIPSCompat::checkStackAlignment() michael@0: { michael@0: #ifdef DEBUG michael@0: Label aligned; michael@0: as_andi(ScratchRegister, sp, StackAlignment - 1); michael@0: ma_b(ScratchRegister, zero, &aligned, Equal, ShortJump); michael@0: as_break(MAX_BREAK_CODE); michael@0: bind(&aligned); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::alignPointerUp(Register src, Register dest, uint32_t alignment) michael@0: { michael@0: MOZ_ASSERT(alignment > 1); michael@0: ma_addu(dest, src, Imm32(alignment - 1)); michael@0: ma_and(dest, dest, Imm32(~(alignment - 1))); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::callWithABIPre(uint32_t *stackAdjust) michael@0: { michael@0: MOZ_ASSERT(inCall_); michael@0: michael@0: // Reserve place for $ra. michael@0: *stackAdjust = sizeof(intptr_t); michael@0: michael@0: *stackAdjust += usedArgSlots_ > NumIntArgRegs ? michael@0: usedArgSlots_ * sizeof(intptr_t) : michael@0: NumIntArgRegs * sizeof(intptr_t); michael@0: michael@0: if (dynamicAlignment_) { michael@0: *stackAdjust += ComputeByteAlignment(*stackAdjust, StackAlignment); michael@0: } else { michael@0: *stackAdjust += ComputeByteAlignment(framePushed_ + *stackAdjust, StackAlignment); michael@0: } michael@0: michael@0: reserveStack(*stackAdjust); michael@0: michael@0: // Save $ra because call is going to clobber it. Restore it in michael@0: // callWithABIPost. NOTE: This is needed for calls from BaselineIC. michael@0: // Maybe we can do this differently. michael@0: ma_sw(ra, Address(StackPointer, *stackAdjust - sizeof(intptr_t))); michael@0: michael@0: // Position all arguments. michael@0: { michael@0: enoughMemory_ = enoughMemory_ && moveResolver_.resolve(); michael@0: if (!enoughMemory_) michael@0: return; michael@0: michael@0: MoveEmitter emitter(*this); michael@0: emitter.emit(moveResolver_); michael@0: emitter.finish(); michael@0: } michael@0: michael@0: checkStackAlignment(); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result) michael@0: { michael@0: // Restore ra value (as stored in callWithABIPre()). michael@0: ma_lw(ra, Address(StackPointer, stackAdjust - sizeof(intptr_t))); michael@0: michael@0: if (dynamicAlignment_) { michael@0: // Restore sp value from stack (as stored in setupUnalignedABICall()). michael@0: ma_lw(StackPointer, Address(StackPointer, stackAdjust)); michael@0: // Use adjustFrame instead of freeStack because we already restored sp. michael@0: adjustFrame(-stackAdjust); michael@0: } else { michael@0: freeStack(stackAdjust); michael@0: } michael@0: michael@0: MOZ_ASSERT(inCall_); michael@0: inCall_ = false; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::callWithABI(void *fun, MoveOp::Type result) michael@0: { michael@0: uint32_t stackAdjust; michael@0: callWithABIPre(&stackAdjust); michael@0: ma_call(ImmPtr(fun)); michael@0: callWithABIPost(stackAdjust, result); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::callWithABI(AsmJSImmPtr imm, MoveOp::Type result) michael@0: { michael@0: uint32_t stackAdjust; michael@0: callWithABIPre(&stackAdjust); michael@0: call(imm); michael@0: callWithABIPost(stackAdjust, result); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::callWithABI(const Address &fun, MoveOp::Type result) michael@0: { michael@0: // Load the callee in t9, no instruction between the lw and call michael@0: // should clobber it. Note that we can't use fun.base because it may michael@0: // be one of the IntArg registers clobbered before the call. michael@0: ma_lw(t9, Address(fun.base, fun.offset)); michael@0: uint32_t stackAdjust; michael@0: callWithABIPre(&stackAdjust); michael@0: call(t9); michael@0: callWithABIPost(stackAdjust, result); michael@0: michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::handleFailureWithHandler(void *handler) michael@0: { michael@0: // Reserve space for exception information. michael@0: int size = (sizeof(ResumeFromException) + StackAlignment) & ~(StackAlignment - 1); michael@0: ma_subu(StackPointer, StackPointer, Imm32(size)); michael@0: ma_move(a0, StackPointer); // Use a0 since it is a first function argument michael@0: michael@0: // Ask for an exception handler. michael@0: setupUnalignedABICall(1, a1); michael@0: passABIArg(a0); michael@0: callWithABI(handler); michael@0: michael@0: JitCode *excTail = GetIonContext()->runtime->jitRuntime()->getExceptionTail(); michael@0: branch(excTail); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::handleFailureWithHandlerTail() michael@0: { michael@0: Label entryFrame; michael@0: Label catch_; michael@0: Label finally; michael@0: Label return_; michael@0: Label bailout; michael@0: michael@0: // Already clobbered a0, so use it... michael@0: ma_lw(a0, Address(StackPointer, offsetof(ResumeFromException, kind))); michael@0: branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame); michael@0: branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_CATCH), &catch_); michael@0: branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_FINALLY), &finally); michael@0: branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_); michael@0: branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout); michael@0: michael@0: breakpoint(); // Invalid kind. michael@0: michael@0: // No exception handler. Load the error value, load the new stack pointer michael@0: // and return from the entry frame. michael@0: bind(&entryFrame); michael@0: moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); michael@0: ma_lw(StackPointer, Address(StackPointer, offsetof(ResumeFromException, stackPointer))); michael@0: michael@0: // We're going to be returning by the ion calling convention michael@0: ma_pop(ra); michael@0: as_jr(ra); michael@0: as_nop(); michael@0: michael@0: // If we found a catch handler, this must be a baseline frame. Restore michael@0: // state and jump to the catch block. michael@0: bind(&catch_); michael@0: ma_lw(a0, Address(StackPointer, offsetof(ResumeFromException, target))); michael@0: ma_lw(BaselineFrameReg, Address(StackPointer, offsetof(ResumeFromException, framePointer))); michael@0: ma_lw(StackPointer, Address(StackPointer, offsetof(ResumeFromException, stackPointer))); michael@0: jump(a0); michael@0: michael@0: // If we found a finally block, this must be a baseline frame. Push michael@0: // two values expected by JSOP_RETSUB: BooleanValue(true) and the michael@0: // exception. michael@0: bind(&finally); michael@0: ValueOperand exception = ValueOperand(a1, a2); michael@0: loadValue(Address(sp, offsetof(ResumeFromException, exception)), exception); michael@0: michael@0: ma_lw(a0, Address(sp, offsetof(ResumeFromException, target))); michael@0: ma_lw(BaselineFrameReg, Address(sp, offsetof(ResumeFromException, framePointer))); michael@0: ma_lw(sp, Address(sp, offsetof(ResumeFromException, stackPointer))); michael@0: michael@0: pushValue(BooleanValue(true)); michael@0: pushValue(exception); michael@0: jump(a0); michael@0: michael@0: // Only used in debug mode. Return BaselineFrame->returnValue() to the michael@0: // caller. michael@0: bind(&return_); michael@0: ma_lw(BaselineFrameReg, Address(StackPointer, offsetof(ResumeFromException, framePointer))); michael@0: ma_lw(StackPointer, Address(StackPointer, offsetof(ResumeFromException, stackPointer))); michael@0: loadValue(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfReturnValue()), michael@0: JSReturnOperand); michael@0: ma_move(StackPointer, BaselineFrameReg); michael@0: pop(BaselineFrameReg); michael@0: ret(); michael@0: michael@0: // If we are bailing out to baseline to handle an exception, jump to michael@0: // the bailout tail stub. michael@0: bind(&bailout); michael@0: ma_lw(a2, Address(sp, offsetof(ResumeFromException, bailoutInfo))); michael@0: ma_li(ReturnReg, Imm32(BAILOUT_RETURN_OK)); michael@0: ma_lw(a1, Address(sp, offsetof(ResumeFromException, target))); michael@0: jump(a1); michael@0: } michael@0: michael@0: CodeOffsetLabel michael@0: MacroAssemblerMIPSCompat::toggledJump(Label *label) michael@0: { michael@0: CodeOffsetLabel ret(nextOffset().getOffset()); michael@0: ma_b(label); michael@0: return ret; michael@0: } michael@0: michael@0: CodeOffsetLabel michael@0: MacroAssemblerMIPSCompat::toggledCall(JitCode *target, bool enabled) michael@0: { michael@0: BufferOffset bo = nextOffset(); michael@0: CodeOffsetLabel offset(bo.getOffset()); michael@0: addPendingJump(bo, ImmPtr(target->raw()), Relocation::JITCODE); michael@0: ma_liPatchable(ScratchRegister, ImmPtr(target->raw())); michael@0: if (enabled) { michael@0: as_jalr(ScratchRegister); michael@0: as_nop(); michael@0: } else { michael@0: as_nop(); michael@0: as_nop(); michael@0: } michael@0: MOZ_ASSERT(nextOffset().getOffset() - offset.offset() == ToggledCallSize()); michael@0: return offset; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerMIPSCompat::branchPtrInNurseryRange(Register ptr, Register temp, Label *label) michael@0: { michael@0: JS_ASSERT(temp != InvalidReg); michael@0: const Nursery &nursery = GetIonContext()->runtime->gcNursery(); michael@0: michael@0: // ptr and temp may be the same register, in which case we mustn't trash it michael@0: // before we use its contents. michael@0: if (ptr == temp) { michael@0: addPtr(ImmWord(-ptrdiff_t(nursery.start())), ptr); michael@0: branchPtr(Assembler::Below, ptr, Imm32(Nursery::NurserySize), label); michael@0: } else { michael@0: movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp); michael@0: addPtr(ptr, temp); michael@0: branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), label); michael@0: } michael@0: }