diff options
Diffstat (limited to 'src/third_party/mozjs-60/extract/js/src/jit/mips32/MacroAssembler-mips32.cpp')
-rw-r--r-- | src/third_party/mozjs-60/extract/js/src/jit/mips32/MacroAssembler-mips32.cpp | 2939 |
1 files changed, 2939 insertions, 0 deletions
diff --git a/src/third_party/mozjs-60/extract/js/src/jit/mips32/MacroAssembler-mips32.cpp b/src/third_party/mozjs-60/extract/js/src/jit/mips32/MacroAssembler-mips32.cpp new file mode 100644 index 00000000000..d1d85f00383 --- /dev/null +++ b/src/third_party/mozjs-60/extract/js/src/jit/mips32/MacroAssembler-mips32.cpp @@ -0,0 +1,2939 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/mips32/MacroAssembler-mips32.h" + +#include "mozilla/DebugOnly.h" +#include "mozilla/EndianUtils.h" +#include "mozilla/MathAlgorithms.h" + +#include "jit/Bailouts.h" +#include "jit/BaselineFrame.h" +#include "jit/JitFrames.h" +#include "jit/MacroAssembler.h" +#include "jit/mips32/Simulator-mips32.h" +#include "jit/MoveEmitter.h" +#include "jit/SharedICRegisters.h" + +#include "jit/MacroAssembler-inl.h" + +using namespace js; +using namespace jit; + +using mozilla::Abs; + +static const int32_t PAYLOAD_OFFSET = NUNBOX32_PAYLOAD_OFFSET; +static const int32_t TAG_OFFSET = NUNBOX32_TYPE_OFFSET; + +static_assert(sizeof(intptr_t) == 4, "Not 64-bit clean."); + +void +MacroAssemblerMIPSCompat::convertBoolToInt32(Register src, Register dest) +{ + // Note that C++ bool is only 1 byte, so zero extend it to clear the + // higher-order bits. + ma_and(dest, src, Imm32(0xff)); +} + +void +MacroAssemblerMIPSCompat::convertInt32ToDouble(Register src, FloatRegister dest) +{ + as_mtc1(src, dest); + as_cvtdw(dest, dest); +} + +void +MacroAssemblerMIPSCompat::convertInt32ToDouble(const Address& src, FloatRegister dest) +{ + ma_ls(dest, src); + as_cvtdw(dest, dest); +} + +void +MacroAssemblerMIPSCompat::convertInt32ToDouble(const BaseIndex& src, FloatRegister dest) +{ + computeScaledAddress(src, ScratchRegister); + convertInt32ToDouble(Address(ScratchRegister, src.offset), dest); +} + +void +MacroAssemblerMIPSCompat::convertUInt32ToDouble(Register src, FloatRegister dest) +{ + Label positive, done; + ma_b(src, src, &positive, NotSigned, ShortJump); + + const uint32_t kExponentShift = mozilla::FloatingPoint<double>::kExponentShift - 32; + const uint32_t kExponent = (31 + mozilla::FloatingPoint<double>::kExponentBias); + + ma_ext(SecondScratchReg, src, 31 - kExponentShift, kExponentShift); + ma_li(ScratchRegister, Imm32(kExponent << kExponentShift)); + ma_or(SecondScratchReg, ScratchRegister); + ma_sll(ScratchRegister, src, Imm32(kExponentShift + 1)); + moveToDoubleHi(SecondScratchReg, dest); + moveToDoubleLo(ScratchRegister, dest); + + ma_b(&done, ShortJump); + + bind(&positive); + convertInt32ToDouble(src, dest); + + bind(&done); + +} + +void +MacroAssemblerMIPSCompat::convertUInt32ToFloat32(Register src, FloatRegister dest) +{ + Label positive, done; + ma_b(src, src, &positive, NotSigned, ShortJump); + + const uint32_t kExponentShift = mozilla::FloatingPoint<double>::kExponentShift - 32; + const uint32_t kExponent = (31 + mozilla::FloatingPoint<double>::kExponentBias); + + ma_ext(SecondScratchReg, src, 31 - kExponentShift, kExponentShift); + ma_li(ScratchRegister, Imm32(kExponent << kExponentShift)); + ma_or(SecondScratchReg, ScratchRegister); + ma_sll(ScratchRegister, src, Imm32(kExponentShift + 1)); + FloatRegister destDouble = dest.asDouble(); + moveToDoubleHi(SecondScratchReg, destDouble); + moveToDoubleLo(ScratchRegister, destDouble); + + convertDoubleToFloat32(destDouble, dest); + + ma_b(&done, ShortJump); + + bind(&positive); + convertInt32ToFloat32(src, dest); + + bind(&done); +} + +void +MacroAssemblerMIPSCompat::convertDoubleToFloat32(FloatRegister src, FloatRegister dest) +{ + as_cvtsd(dest, src); +} + +// Checks whether a double is representable as a 32-bit integer. If so, the +// integer is written to the output register. Otherwise, a bailout is taken to +// the given snapshot. This function overwrites the scratch float register. +void +MacroAssemblerMIPSCompat::convertDoubleToInt32(FloatRegister src, Register dest, + Label* fail, bool negativeZeroCheck) +{ + if (negativeZeroCheck) { + moveFromDoubleHi(src, dest); + moveFromDoubleLo(src, SecondScratchReg); + ma_xor(dest, Imm32(INT32_MIN)); + ma_or(dest, SecondScratchReg); + ma_b(dest, Imm32(0), fail, Assembler::Equal); + } + + // Truncate double to int ; if result is inexact fail + as_truncwd(ScratchFloat32Reg, src); + as_cfc1(ScratchRegister, Assembler::FCSR); + moveFromFloat32(ScratchFloat32Reg, dest); + ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseI, 1); + ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual); +} + +// Checks whether a float32 is representable as a 32-bit integer. If so, the +// integer is written to the output register. Otherwise, a bailout is taken to +// the given snapshot. This function overwrites the scratch float register. +void +MacroAssemblerMIPSCompat::convertFloat32ToInt32(FloatRegister src, Register dest, + Label* fail, bool negativeZeroCheck) +{ + if (negativeZeroCheck) { + moveFromFloat32(src, dest); + ma_b(dest, Imm32(INT32_MIN), fail, Assembler::Equal); + } + + as_truncws(ScratchFloat32Reg, src); + as_cfc1(ScratchRegister, Assembler::FCSR); + moveFromFloat32(ScratchFloat32Reg, dest); + ma_ext(ScratchRegister, ScratchRegister, Assembler::CauseI, 1); + ma_b(ScratchRegister, Imm32(0), fail, Assembler::NotEqual); +} + +void +MacroAssemblerMIPSCompat::convertFloat32ToDouble(FloatRegister src, FloatRegister dest) +{ + as_cvtds(dest, src); +} + +void +MacroAssemblerMIPSCompat::convertInt32ToFloat32(Register src, FloatRegister dest) +{ + as_mtc1(src, dest); + as_cvtsw(dest, dest); +} + +void +MacroAssemblerMIPSCompat::convertInt32ToFloat32(const Address& src, FloatRegister dest) +{ + ma_ls(dest, src); + as_cvtsw(dest, dest); +} + +void +MacroAssemblerMIPS::ma_li(Register dest, CodeLabel* label) +{ + BufferOffset bo = m_buffer.nextOffset(); + ma_liPatchable(dest, ImmWord(/* placeholder */ 0)); + label->patchAt()->bind(bo.getOffset()); + label->setLinkMode(CodeLabel::MoveImmediate); +} + +void +MacroAssemblerMIPS::ma_li(Register dest, ImmWord imm) +{ + ma_li(dest, Imm32(uint32_t(imm.value))); +} + +void +MacroAssemblerMIPS::ma_liPatchable(Register dest, ImmPtr imm) +{ + ma_liPatchable(dest, ImmWord(uintptr_t(imm.value))); +} + +void +MacroAssemblerMIPS::ma_liPatchable(Register dest, ImmWord imm) +{ + ma_liPatchable(dest, Imm32(int32_t(imm.value))); +} + +// Arithmetic-based ops. + +// Add. +template <typename L> +void +MacroAssemblerMIPS::ma_addTestOverflow(Register rd, Register rs, Register rt, L overflow) +{ + Label goodAddition; + as_addu(rd, rs, rt); + + as_xor(ScratchRegister, rs, rt); // If different sign, no overflow + ma_b(ScratchRegister, Imm32(0), &goodAddition, Assembler::LessThan, ShortJump); + + // If different sign, then overflow + as_xor(ScratchRegister, rs, rd); + ma_b(ScratchRegister, Imm32(0), overflow, Assembler::LessThan); + + bind(&goodAddition); +} + +template void +MacroAssemblerMIPS::ma_addTestOverflow<Label*>(Register rd, Register rs, + Register rt, Label* overflow); +template void +MacroAssemblerMIPS::ma_addTestOverflow<wasm::OldTrapDesc>(Register rd, Register rs, Register rt, + wasm::OldTrapDesc overflow); + +template <typename L> +void +MacroAssemblerMIPS::ma_addTestOverflow(Register rd, Register rs, Imm32 imm, L overflow) +{ + // Check for signed range because of as_addiu + // Check for unsigned range because of as_xori + if (Imm16::IsInSignedRange(imm.value) && Imm16::IsInUnsignedRange(imm.value)) { + Label goodAddition; + as_addiu(rd, rs, imm.value); + + // If different sign, no overflow + as_xori(ScratchRegister, rs, imm.value); + ma_b(ScratchRegister, Imm32(0), &goodAddition, Assembler::LessThan, ShortJump); + + // If different sign, then overflow + as_xor(ScratchRegister, rs, rd); + ma_b(ScratchRegister, Imm32(0), overflow, Assembler::LessThan); + + bind(&goodAddition); + } else { + ma_li(ScratchRegister, imm); + ma_addTestOverflow(rd, rs, ScratchRegister, overflow); + } +} + +template void +MacroAssemblerMIPS::ma_addTestOverflow<Label*>(Register rd, Register rs, + Imm32 imm, Label* overflow); +template void +MacroAssemblerMIPS::ma_addTestOverflow<wasm::OldTrapDesc>(Register rd, Register rs, Imm32 imm, + wasm::OldTrapDesc overflow); + +// Subtract. +void +MacroAssemblerMIPS::ma_subTestOverflow(Register rd, Register rs, Register rt, Label* overflow) +{ + Label goodSubtraction; + // Use second scratch. The instructions generated by ma_b don't use the + // second scratch register. + as_subu(rd, rs, rt); + + as_xor(ScratchRegister, rs, rt); // If same sign, no overflow + ma_b(ScratchRegister, Imm32(0), &goodSubtraction, Assembler::GreaterThanOrEqual, ShortJump); + + // If different sign, then overflow + as_xor(ScratchRegister, rs, rd); + ma_b(ScratchRegister, Imm32(0), overflow, Assembler::LessThan); + + bind(&goodSubtraction); +} + +// Memory. + +void +MacroAssemblerMIPS::ma_load(Register dest, Address address, + LoadStoreSize size, LoadStoreExtension extension) +{ + int16_t encodedOffset; + Register base; + + if (isLoongson() && ZeroExtend != extension && + !Imm16::IsInSignedRange(address.offset)) + { + ma_li(ScratchRegister, Imm32(address.offset)); + base = address.base; + + switch (size) { + case SizeByte: + as_gslbx(dest, base, ScratchRegister, 0); + break; + case SizeHalfWord: + as_gslhx(dest, base, ScratchRegister, 0); + break; + case SizeWord: + as_gslwx(dest, base, ScratchRegister, 0); + break; + case SizeDouble: + as_gsldx(dest, base, ScratchRegister, 0); + break; + default: + MOZ_CRASH("Invalid argument for ma_load"); + } + return; + } + + if (!Imm16::IsInSignedRange(address.offset)) { + ma_li(ScratchRegister, Imm32(address.offset)); + as_addu(ScratchRegister, address.base, ScratchRegister); + base = ScratchRegister; + encodedOffset = Imm16(0).encode(); + } else { + encodedOffset = Imm16(address.offset).encode(); + base = address.base; + } + + switch (size) { + case SizeByte: + if (ZeroExtend == extension) + as_lbu(dest, base, encodedOffset); + else + as_lb(dest, base, encodedOffset); + break; + case SizeHalfWord: + if (ZeroExtend == extension) + as_lhu(dest, base, encodedOffset); + else + as_lh(dest, base, encodedOffset); + break; + case SizeWord: + as_lw(dest, base, encodedOffset); + break; + default: + MOZ_CRASH("Invalid argument for ma_load"); + } +} + +void +MacroAssemblerMIPS::ma_store(Register data, Address address, LoadStoreSize size, + LoadStoreExtension extension) +{ + int16_t encodedOffset; + Register base; + + if (isLoongson() && !Imm16::IsInSignedRange(address.offset)) { + ma_li(ScratchRegister, Imm32(address.offset)); + base = address.base; + + switch (size) { + case SizeByte: + as_gssbx(data, base, ScratchRegister, 0); + break; + case SizeHalfWord: + as_gsshx(data, base, ScratchRegister, 0); + break; + case SizeWord: + as_gsswx(data, base, ScratchRegister, 0); + break; + case SizeDouble: + as_gssdx(data, base, ScratchRegister, 0); + break; + default: + MOZ_CRASH("Invalid argument for ma_store"); + } + return; + } + + if (!Imm16::IsInSignedRange(address.offset)) { + ma_li(ScratchRegister, Imm32(address.offset)); + as_addu(ScratchRegister, address.base, ScratchRegister); + base = ScratchRegister; + encodedOffset = Imm16(0).encode(); + } else { + encodedOffset = Imm16(address.offset).encode(); + base = address.base; + } + + switch (size) { + case SizeByte: + as_sb(data, base, encodedOffset); + break; + case SizeHalfWord: + as_sh(data, base, encodedOffset); + break; + case SizeWord: + as_sw(data, base, encodedOffset); + break; + default: + MOZ_CRASH("Invalid argument for ma_store"); + } +} + +void +MacroAssemblerMIPSCompat::computeScaledAddress(const BaseIndex& address, Register dest) +{ + int32_t shift = Imm32::ShiftOf(address.scale).value; + if (shift) { + ma_sll(ScratchRegister, address.index, Imm32(shift)); + as_addu(dest, address.base, ScratchRegister); + } else { + as_addu(dest, address.base, address.index); + } +} + +// Shortcut for when we know we're transferring 32 bits of data. +void +MacroAssemblerMIPS::ma_lw(Register data, Address address) +{ + ma_load(data, address, SizeWord); +} + +void +MacroAssemblerMIPS::ma_sw(Register data, Address address) +{ + ma_store(data, address, SizeWord); +} + +void +MacroAssemblerMIPS::ma_sw(Imm32 imm, Address address) +{ + MOZ_ASSERT(address.base != ScratchRegister); + ma_li(ScratchRegister, imm); + + if (Imm16::IsInSignedRange(address.offset)) { + as_sw(ScratchRegister, address.base, address.offset); + } else { + MOZ_ASSERT(address.base != SecondScratchReg); + + ma_li(SecondScratchReg, Imm32(address.offset)); + as_addu(SecondScratchReg, address.base, SecondScratchReg); + as_sw(ScratchRegister, SecondScratchReg, 0); + } +} + +void +MacroAssemblerMIPS::ma_sw(Register data, BaseIndex& address) +{ + ma_store(data, address, SizeWord); +} + +void +MacroAssemblerMIPS::ma_pop(Register r) +{ + as_lw(r, StackPointer, 0); + as_addiu(StackPointer, StackPointer, sizeof(intptr_t)); +} + +void +MacroAssemblerMIPS::ma_push(Register r) +{ + if (r == sp) { + // Pushing sp requires one more instruction. + ma_move(ScratchRegister, sp); + r = ScratchRegister; + } + + as_addiu(StackPointer, StackPointer, -sizeof(intptr_t)); + as_sw(r, StackPointer, 0); +} + +// Branches when done from within mips-specific code. +void +MacroAssemblerMIPS::ma_b(Register lhs, Address addr, Label* label, Condition c, JumpKind jumpKind) +{ + MOZ_ASSERT(lhs != ScratchRegister); + ma_lw(ScratchRegister, addr); + ma_b(lhs, ScratchRegister, label, c, jumpKind); +} + +void +MacroAssemblerMIPS::ma_b(Address addr, Imm32 imm, Label* label, Condition c, JumpKind jumpKind) +{ + ma_lw(SecondScratchReg, addr); + ma_b(SecondScratchReg, imm, label, c, jumpKind); +} + +void +MacroAssemblerMIPS::ma_b(Address addr, ImmGCPtr imm, Label* label, Condition c, JumpKind jumpKind) +{ + ma_lw(SecondScratchReg, addr); + ma_b(SecondScratchReg, imm, label, c, jumpKind); +} + +void +MacroAssemblerMIPS::ma_bal(Label* label, DelaySlotFill delaySlotFill) +{ + spew("branch .Llabel %p\n", label); + if (label->bound()) { + // Generate the long jump for calls because return address has to be + // the address after the reserved block. + addLongJump(nextOffset(), BufferOffset(label->offset())); + ma_liPatchable(ScratchRegister, Imm32(LabelBase::INVALID_OFFSET)); + as_jalr(ScratchRegister); + if (delaySlotFill == FillDelaySlot) + as_nop(); + return; + } + + // Second word holds a pointer to the next branch in label's chain. + uint32_t nextInChain = label->used() ? label->offset() : LabelBase::INVALID_OFFSET; + + // Make the whole branch continous in the buffer. + m_buffer.ensureSpace(4 * sizeof(uint32_t)); + + spew("bal .Llabel %p\n", label); + BufferOffset bo = writeInst(getBranchCode(BranchIsCall).encode()); + writeInst(nextInChain); + if (!oom()) + label->use(bo.getOffset()); + // Leave space for long jump. + as_nop(); + if (delaySlotFill == FillDelaySlot) + as_nop(); +} + +void +MacroAssemblerMIPS::branchWithCode(InstImm code, Label* label, JumpKind jumpKind) +{ + spew("branch .Llabel %p", label); + MOZ_ASSERT(code.encode() != InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)).encode()); + InstImm inst_beq = InstImm(op_beq, zero, zero, BOffImm16(0)); + + if (label->bound()) { + int32_t offset = label->offset() - m_buffer.nextOffset().getOffset(); + + if (BOffImm16::IsInRange(offset)) + jumpKind = ShortJump; + + if (jumpKind == ShortJump) { + MOZ_ASSERT(BOffImm16::IsInRange(offset)); + code.setBOffImm16(BOffImm16(offset)); +#ifdef JS_JITSPEW + decodeBranchInstAndSpew(code); +#endif + writeInst(code.encode()); + as_nop(); + return; + } + + if (code.encode() == inst_beq.encode()) { + // Handle long jump + addLongJump(nextOffset(), BufferOffset(label->offset())); + ma_liPatchable(ScratchRegister, Imm32(LabelBase::INVALID_OFFSET)); + as_jr(ScratchRegister); + as_nop(); + return; + } + + // Handle long conditional branch + spew("invert branch .Llabel %p", label); + InstImm code_r = invertBranch(code, BOffImm16(5 * sizeof(uint32_t))); +#ifdef JS_JITSPEW + decodeBranchInstAndSpew(code_r); +#endif + writeInst(code_r.encode()); + + // No need for a "nop" here because we can clobber scratch. + addLongJump(nextOffset(), BufferOffset(label->offset())); + ma_liPatchable(ScratchRegister, Imm32(LabelBase::INVALID_OFFSET)); + as_jr(ScratchRegister); + as_nop(); + return; + } + + // Generate open jump and link it to a label. + + // Second word holds a pointer to the next branch in label's chain. + uint32_t nextInChain = label->used() ? label->offset() : LabelBase::INVALID_OFFSET; + + if (jumpKind == ShortJump) { + // Make the whole branch continous in the buffer. + m_buffer.ensureSpace(2 * sizeof(uint32_t)); + + // Indicate that this is short jump with offset 4. + code.setBOffImm16(BOffImm16(4)); +#ifdef JS_JITSPEW + decodeBranchInstAndSpew(code); +#endif + BufferOffset bo = writeInst(code.encode()); + writeInst(nextInChain); + if (!oom()) + label->use(bo.getOffset()); + return; + } + + bool conditional = code.encode() != inst_beq.encode(); + + // Make the whole branch continous in the buffer. + m_buffer.ensureSpace((conditional ? 5 : 4) * sizeof(uint32_t)); + +#ifdef JS_JITSPEW + decodeBranchInstAndSpew(code); +#endif + BufferOffset bo = writeInst(code.encode()); + writeInst(nextInChain); + if (!oom()) + label->use(bo.getOffset()); + // Leave space for potential long jump. + as_nop(); + as_nop(); + if (conditional) + as_nop(); +} + + +void +MacroAssemblerMIPSCompat::cmp64Set(Condition cond, Register64 lhs, Imm64 val, Register dest) { + + if (val.value == 0) { + switch(cond){ + case Assembler::Equal: + case Assembler::BelowOrEqual: + as_or(dest, lhs.high, lhs.low); + as_sltiu(dest, dest, 1); + break; + case Assembler::NotEqual: + case Assembler::Above: + as_or(dest, lhs.high, lhs.low); + as_sltu(dest, zero, dest); + break; + case Assembler::LessThan: + case Assembler::GreaterThanOrEqual: + as_slt(dest, lhs.high, zero); + if (cond == Assembler::GreaterThanOrEqual) + as_xori(dest, dest, 1); + break; + case Assembler::GreaterThan: + case Assembler::LessThanOrEqual: + as_or(SecondScratchReg, lhs.high, lhs.low); + as_sra(ScratchRegister, lhs.high, 31); + as_sltu(dest, ScratchRegister, SecondScratchReg); + if (cond == Assembler::LessThanOrEqual) + as_xori(dest, dest, 1); + break; + case Assembler::Below: + case Assembler::AboveOrEqual: + as_ori(dest, zero, cond == Assembler::AboveOrEqual ? 1 : 0); + break; + default: + MOZ_CRASH("Condition code not supported"); + break; + } + return; + } + + Condition c = ma_cmp64(cond, lhs, val, dest); + + switch(cond) { + // For Equal/NotEqual cond ma_cmp64 dest holds non boolean result. + case Assembler::Equal: + as_sltiu(dest, dest, 1); + break; + case Assembler::NotEqual: + as_sltu(dest, zero, dest); + break; + default: + if(c == Assembler::Zero) + as_xori(dest, dest, 1); + break; + } +} + +void +MacroAssemblerMIPSCompat::cmp64Set(Condition cond, Register64 lhs, Register64 rhs, Register dest) { + + Condition c = ma_cmp64(cond, lhs, rhs, dest); + + switch(cond) { + // For Equal/NotEqual cond ma_cmp64 dest holds non boolean result. + case Assembler::Equal: + as_sltiu(dest, dest, 1); + break; + case Assembler::NotEqual: + as_sltu(dest, zero, dest); + break; + default: + if(c == Assembler::Zero) + as_xori(dest, dest, 1); + break; + } +} + +Assembler::Condition +MacroAssemblerMIPSCompat::ma_cmp64(Condition cond, Register64 lhs, Register64 rhs, Register dest) { + + switch(cond) { + case Assembler::Equal: + case Assembler::NotEqual: + as_xor(SecondScratchReg, lhs.high, rhs.high); + as_xor(ScratchRegister, lhs.low, rhs.low); + as_or(dest, SecondScratchReg, ScratchRegister); + return (cond == Assembler::Equal) ? Assembler::Zero : Assembler::NonZero; + break; + case Assembler::LessThan: + case Assembler::GreaterThanOrEqual: + as_slt(SecondScratchReg, rhs.high, lhs.high); + as_sltu(ScratchRegister, lhs.low, rhs.low); + as_slt(SecondScratchReg, SecondScratchReg, ScratchRegister); + as_slt(ScratchRegister, lhs.high, rhs.high); + as_or(dest, ScratchRegister, SecondScratchReg); + return (cond == Assembler::GreaterThanOrEqual) ? Assembler::Zero : Assembler::NonZero; + break; + case Assembler::GreaterThan: + case Assembler::LessThanOrEqual: + as_slt(SecondScratchReg, lhs.high, rhs.high); + as_sltu(ScratchRegister, rhs.low, lhs.low); + as_slt(SecondScratchReg, SecondScratchReg, ScratchRegister); + as_slt(ScratchRegister, rhs.high, lhs.high); + as_or(dest, ScratchRegister, SecondScratchReg); + return (cond == Assembler::LessThanOrEqual) ? Assembler::Zero : Assembler::NonZero; + break; + case Assembler::Below: + case Assembler::AboveOrEqual: + as_sltu(SecondScratchReg, rhs.high, lhs.high); + as_sltu(ScratchRegister, lhs.low, rhs.low); + as_slt(SecondScratchReg, SecondScratchReg, ScratchRegister); + as_sltu(ScratchRegister, lhs.high, rhs.high); + as_or(dest, ScratchRegister, SecondScratchReg); + return (cond == Assembler::AboveOrEqual) ? Assembler::Zero : Assembler::NonZero; + break; + case Assembler::Above: + case Assembler::BelowOrEqual: + as_sltu(SecondScratchReg, lhs.high, rhs.high); + as_sltu(ScratchRegister, rhs.low, lhs.low); + as_slt(SecondScratchReg, SecondScratchReg, ScratchRegister); + as_sltu(ScratchRegister, rhs.high, lhs.high); + as_or(dest, ScratchRegister, SecondScratchReg); + return (cond == Assembler::BelowOrEqual) ? Assembler::Zero : Assembler::NonZero; + break; + default: + MOZ_CRASH("Condition code not supported"); + break; + } +} + +Assembler::Condition +MacroAssemblerMIPSCompat::ma_cmp64(Condition cond, Register64 lhs, Imm64 val, Register dest) { + + MOZ_ASSERT(val.value != 0); + + switch(cond) { + case Assembler::Equal: + case Assembler::NotEqual: + ma_xor(SecondScratchReg, lhs.high, val.hi()); + ma_xor(ScratchRegister, lhs.low, val.low()); + as_or(dest, SecondScratchReg, ScratchRegister); + return (cond == Assembler::Equal) ? Assembler::Zero : Assembler::NonZero; + break; + case Assembler::LessThan: + case Assembler::GreaterThanOrEqual: + ma_li(SecondScratchReg, val.hi()); + as_slt(ScratchRegister, lhs.high, SecondScratchReg); + as_slt(SecondScratchReg, SecondScratchReg, lhs.high); + as_subu(SecondScratchReg, SecondScratchReg, ScratchRegister); + ma_li(ScratchRegister, val.low()); + as_sltu(ScratchRegister, lhs.low, ScratchRegister); + as_slt(dest, SecondScratchReg, ScratchRegister); + return (cond == Assembler::GreaterThanOrEqual) ? Assembler::Zero : Assembler::NonZero; + break; + case Assembler::GreaterThan: + case Assembler::LessThanOrEqual: + ma_li(SecondScratchReg, val.hi()); + as_slt(ScratchRegister, SecondScratchReg, lhs.high); + as_slt(SecondScratchReg, lhs.high, SecondScratchReg); + as_subu(SecondScratchReg, SecondScratchReg, ScratchRegister); + ma_li(ScratchRegister, val.low()); + as_sltu(ScratchRegister, ScratchRegister, lhs.low); + as_slt(dest, SecondScratchReg, ScratchRegister); + return (cond == Assembler::LessThanOrEqual) ? Assembler::Zero : Assembler::NonZero; + break; + case Assembler::Below: + case Assembler::AboveOrEqual: + ma_li(SecondScratchReg, val.hi()); + as_sltu(ScratchRegister, lhs.high, SecondScratchReg); + as_sltu(SecondScratchReg, SecondScratchReg, lhs.high); + as_subu(SecondScratchReg, SecondScratchReg, ScratchRegister); + ma_li(ScratchRegister, val.low()); + as_sltu(ScratchRegister, lhs.low, ScratchRegister); + as_slt(dest, SecondScratchReg, ScratchRegister); + return (cond == Assembler::AboveOrEqual) ? Assembler::Zero : Assembler::NonZero; + break; + case Assembler::Above: + case Assembler::BelowOrEqual: + ma_li(SecondScratchReg, val.hi()); + as_sltu(ScratchRegister, SecondScratchReg, lhs.high); + as_sltu(SecondScratchReg, lhs.high, SecondScratchReg); + as_subu(SecondScratchReg, SecondScratchReg, ScratchRegister); + ma_li(ScratchRegister, val.low()); + as_sltu(ScratchRegister, ScratchRegister, lhs.low); + as_slt(dest, SecondScratchReg, ScratchRegister); + return (cond == Assembler::BelowOrEqual) ? Assembler::Zero : Assembler::NonZero; + break; + default: + MOZ_CRASH("Condition code not supported"); + break; + } +} + +// fp instructions +void +MacroAssemblerMIPS::ma_lid(FloatRegister dest, double value) +{ + struct DoubleStruct { + uint32_t lo; + uint32_t hi; + } ; + DoubleStruct intStruct = mozilla::BitwiseCast<DoubleStruct>(value); +#if MOZ_BIG_ENDIAN + mozilla::Swap(intStruct.hi, intStruct.lo); +#endif + + // put hi part of 64 bit value into the odd register + if (intStruct.hi == 0) { + moveToDoubleHi(zero, dest); + } else { + ma_li(ScratchRegister, Imm32(intStruct.hi)); + moveToDoubleHi(ScratchRegister, dest); + } + + // put low part of 64 bit value into the even register + if (intStruct.lo == 0) { + moveToDoubleLo(zero, dest); + } else { + ma_li(ScratchRegister, Imm32(intStruct.lo)); + moveToDoubleLo(ScratchRegister, dest); + } +} + +void +MacroAssemblerMIPS::ma_mv(FloatRegister src, ValueOperand dest) +{ + moveFromDoubleLo(src, dest.payloadReg()); + moveFromDoubleHi(src, dest.typeReg()); +} + +void +MacroAssemblerMIPS::ma_mv(ValueOperand src, FloatRegister dest) +{ + moveToDoubleLo(src.payloadReg(), dest); + moveToDoubleHi(src.typeReg(), dest); +} + +void +MacroAssemblerMIPS::ma_ls(FloatRegister ft, Address address) +{ + if (Imm16::IsInSignedRange(address.offset)) { + as_lwc1(ft, address.base, address.offset); + } else { + MOZ_ASSERT(address.base != ScratchRegister); + ma_li(ScratchRegister, Imm32(address.offset)); + if (isLoongson()) { + as_gslsx(ft, address.base, ScratchRegister, 0); + } else { + as_addu(ScratchRegister, address.base, ScratchRegister); + as_lwc1(ft, ScratchRegister, 0); + } + } +} + +void +MacroAssemblerMIPS::ma_ld(FloatRegister ft, Address address) +{ + if (Imm16::IsInSignedRange(address.offset)) { + as_ldc1(ft, address.base, address.offset); + } else { + MOZ_ASSERT(address.base != ScratchRegister); + ma_li(ScratchRegister, Imm32(address.offset)); + if (isLoongson()) { + as_gsldx(ft, address.base, ScratchRegister, 0); + } else { + as_addu(ScratchRegister, address.base, ScratchRegister); + as_ldc1(ft, ScratchRegister, 0); + } + } +} + +void +MacroAssemblerMIPS::ma_sd(FloatRegister ft, Address address) +{ + if (Imm16::IsInSignedRange(address.offset)) { + as_sdc1(ft, address.base, address.offset); + } else { + MOZ_ASSERT(address.base != ScratchRegister); + ma_li(ScratchRegister, Imm32(address.offset)); + if (isLoongson()) { + as_gssdx(ft, address.base, ScratchRegister, 0); + } else { + as_addu(ScratchRegister, address.base, ScratchRegister); + as_sdc1(ft, ScratchRegister, 0); + } + } +} + +void +MacroAssemblerMIPS::ma_ss(FloatRegister ft, Address address) +{ + if (Imm16::IsInSignedRange(address.offset)) { + as_swc1(ft, address.base, address.offset); + } else { + MOZ_ASSERT(address.base != ScratchRegister); + ma_li(ScratchRegister, Imm32(address.offset)); + if (isLoongson()) { + as_gsssx(ft, address.base, ScratchRegister, 0); + } else { + as_addu(ScratchRegister, address.base, ScratchRegister); + as_swc1(ft, ScratchRegister, 0); + } + } +} + +void +MacroAssemblerMIPS::ma_ldc1WordAligned(FloatRegister ft, Register base, int32_t off) +{ + MOZ_ASSERT(Imm16::IsInSignedRange(off + PAYLOAD_OFFSET) && + Imm16::IsInSignedRange(off + TAG_OFFSET)); + + as_lwc1(ft, base, off + PAYLOAD_OFFSET); + as_lwc1(getOddPair(ft), base, off + TAG_OFFSET); +} + +void +MacroAssemblerMIPS::ma_sdc1WordAligned(FloatRegister ft, Register base, int32_t off) +{ + MOZ_ASSERT(Imm16::IsInSignedRange(off + PAYLOAD_OFFSET) && + Imm16::IsInSignedRange(off + TAG_OFFSET)); + + as_swc1(ft, base, off + PAYLOAD_OFFSET); + as_swc1(getOddPair(ft), base, off + TAG_OFFSET); +} + +void +MacroAssemblerMIPS::ma_pop(FloatRegister f) +{ + if (f.isDouble()) + ma_ldc1WordAligned(f, StackPointer, 0); + else + as_lwc1(f, StackPointer, 0); + + as_addiu(StackPointer, StackPointer, f.size()); +} + +void +MacroAssemblerMIPS::ma_push(FloatRegister f) +{ + as_addiu(StackPointer, StackPointer, -f.size()); + + if(f.isDouble()) + ma_sdc1WordAligned(f, StackPointer, 0); + else + as_swc1(f, StackPointer, 0); +} + +bool +MacroAssemblerMIPSCompat::buildOOLFakeExitFrame(void* fakeReturnAddr) +{ + uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS, + ExitFrameLayout::Size()); + + asMasm().Push(Imm32(descriptor)); // descriptor_ + asMasm().Push(ImmPtr(fakeReturnAddr)); + + return true; +} + +void +MacroAssemblerMIPSCompat::move32(Imm32 imm, Register dest) +{ + ma_li(dest, imm); +} + +void +MacroAssemblerMIPSCompat::move32(Register src, Register dest) +{ + ma_move(dest, src); +} + +void +MacroAssemblerMIPSCompat::movePtr(Register src, Register dest) +{ + ma_move(dest, src); +} +void +MacroAssemblerMIPSCompat::movePtr(ImmWord imm, Register dest) +{ + ma_li(dest, imm); +} + +void +MacroAssemblerMIPSCompat::movePtr(ImmGCPtr imm, Register dest) +{ + ma_li(dest, imm); +} + +void +MacroAssemblerMIPSCompat::movePtr(ImmPtr imm, Register dest) +{ + movePtr(ImmWord(uintptr_t(imm.value)), dest); +} +void +MacroAssemblerMIPSCompat::movePtr(wasm::SymbolicAddress imm, Register dest) +{ + append(wasm::SymbolicAccess(CodeOffset(nextOffset().getOffset()), imm)); + ma_liPatchable(dest, ImmWord(-1)); +} + +void +MacroAssemblerMIPSCompat::load8ZeroExtend(const Address& address, Register dest) +{ + ma_load(dest, address, SizeByte, ZeroExtend); +} + +void +MacroAssemblerMIPSCompat::load8ZeroExtend(const BaseIndex& src, Register dest) +{ + ma_load(dest, src, SizeByte, ZeroExtend); +} + +void +MacroAssemblerMIPSCompat::load8SignExtend(const Address& address, Register dest) +{ + ma_load(dest, address, SizeByte, SignExtend); +} + +void +MacroAssemblerMIPSCompat::load8SignExtend(const BaseIndex& src, Register dest) +{ + ma_load(dest, src, SizeByte, SignExtend); +} + +void +MacroAssemblerMIPSCompat::load16ZeroExtend(const Address& address, Register dest) +{ + ma_load(dest, address, SizeHalfWord, ZeroExtend); +} + +void +MacroAssemblerMIPSCompat::load16ZeroExtend(const BaseIndex& src, Register dest) +{ + ma_load(dest, src, SizeHalfWord, ZeroExtend); +} + +void +MacroAssemblerMIPSCompat::load16SignExtend(const Address& address, Register dest) +{ + ma_load(dest, address, SizeHalfWord, SignExtend); +} + +void +MacroAssemblerMIPSCompat::load16SignExtend(const BaseIndex& src, Register dest) +{ + ma_load(dest, src, SizeHalfWord, SignExtend); +} + +void +MacroAssemblerMIPSCompat::load32(const Address& address, Register dest) +{ + ma_load(dest, address, SizeWord); +} + +void +MacroAssemblerMIPSCompat::load32(const BaseIndex& address, Register dest) +{ + ma_load(dest, address, SizeWord); +} + +void +MacroAssemblerMIPSCompat::load32(AbsoluteAddress address, Register dest) +{ + movePtr(ImmPtr(address.addr), ScratchRegister); + load32(Address(ScratchRegister, 0), dest); +} + +void +MacroAssemblerMIPSCompat::load32(wasm::SymbolicAddress address, Register dest) +{ + movePtr(address, ScratchRegister); + load32(Address(ScratchRegister, 0), dest); +} + +void +MacroAssemblerMIPSCompat::loadPtr(const Address& address, Register dest) +{ + ma_load(dest, address, SizeWord); +} + +void +MacroAssemblerMIPSCompat::loadPtr(const BaseIndex& src, Register dest) +{ + ma_load(dest, src, SizeWord); +} + +void +MacroAssemblerMIPSCompat::loadPtr(AbsoluteAddress address, Register dest) +{ + movePtr(ImmPtr(address.addr), ScratchRegister); + loadPtr(Address(ScratchRegister, 0), dest); +} + +void +MacroAssemblerMIPSCompat::loadPtr(wasm::SymbolicAddress address, Register dest) +{ + movePtr(address, ScratchRegister); + loadPtr(Address(ScratchRegister, 0), dest); +} + +void +MacroAssemblerMIPSCompat::loadPrivate(const Address& address, Register dest) +{ + ma_lw(dest, Address(address.base, address.offset + PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::loadUnalignedDouble(const wasm::MemoryAccessDesc& access, + const BaseIndex& src, Register temp, FloatRegister dest) +{ + MOZ_ASSERT(MOZ_LITTLE_ENDIAN, "Wasm-only; wasm is disabled on big-endian."); + computeScaledAddress(src, SecondScratchReg); + + uint32_t framePushed = asMasm().framePushed(); + BufferOffset load; + if (Imm16::IsInSignedRange(src.offset) && Imm16::IsInSignedRange(src.offset + 7)) { + load = as_lwl(temp, SecondScratchReg, src.offset + INT64LOW_OFFSET + 3); + as_lwr(temp, SecondScratchReg, src.offset + INT64LOW_OFFSET); + append(access, load.getOffset(), framePushed); + moveToDoubleLo(temp, dest); + load = as_lwl(temp, SecondScratchReg, src.offset + INT64HIGH_OFFSET + 3); + as_lwr(temp, SecondScratchReg, src.offset + INT64HIGH_OFFSET); + append(access, load.getOffset(), framePushed); + moveToDoubleHi(temp, dest); + } else { + ma_li(ScratchRegister, Imm32(src.offset)); + as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister); + load = as_lwl(temp, ScratchRegister, INT64LOW_OFFSET + 3); + as_lwr(temp, ScratchRegister, INT64LOW_OFFSET); + append(access, load.getOffset(), framePushed); + moveToDoubleLo(temp, dest); + load = as_lwl(temp, ScratchRegister, INT64HIGH_OFFSET + 3); + as_lwr(temp, ScratchRegister, INT64HIGH_OFFSET); + append(access, load.getOffset(), framePushed); + moveToDoubleHi(temp, dest); + } +} + +void +MacroAssemblerMIPSCompat::loadUnalignedFloat32(const wasm::MemoryAccessDesc& access, + const BaseIndex& src, Register temp, FloatRegister dest) +{ + MOZ_ASSERT(MOZ_LITTLE_ENDIAN, "Wasm-only; wasm is disabled on big-endian."); + computeScaledAddress(src, SecondScratchReg); + BufferOffset load; + if (Imm16::IsInSignedRange(src.offset) && Imm16::IsInSignedRange(src.offset + 3)) { + load = as_lwl(temp, SecondScratchReg, src.offset + 3); + as_lwr(temp, SecondScratchReg, src.offset); + } else { + ma_li(ScratchRegister, Imm32(src.offset)); + as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister); + load = as_lwl(temp, ScratchRegister, 3); + as_lwr(temp, ScratchRegister, 0); + } + append(access, load.getOffset(), asMasm().framePushed()); + moveToFloat32(temp, dest); +} + +void +MacroAssemblerMIPSCompat::store8(Imm32 imm, const Address& address) +{ + ma_li(SecondScratchReg, imm); + ma_store(SecondScratchReg, address, SizeByte); +} + +void +MacroAssemblerMIPSCompat::store8(Register src, const Address& address) +{ + ma_store(src, address, SizeByte); +} + +void +MacroAssemblerMIPSCompat::store8(Imm32 imm, const BaseIndex& dest) +{ + ma_store(imm, dest, SizeByte); +} + +void +MacroAssemblerMIPSCompat::store8(Register src, const BaseIndex& dest) +{ + ma_store(src, dest, SizeByte); +} + +void +MacroAssemblerMIPSCompat::store16(Imm32 imm, const Address& address) +{ + ma_li(SecondScratchReg, imm); + ma_store(SecondScratchReg, address, SizeHalfWord); +} + +void +MacroAssemblerMIPSCompat::store16(Register src, const Address& address) +{ + ma_store(src, address, SizeHalfWord); +} + +void +MacroAssemblerMIPSCompat::store16(Imm32 imm, const BaseIndex& dest) +{ + ma_store(imm, dest, SizeHalfWord); +} + +void +MacroAssemblerMIPSCompat::store16(Register src, const BaseIndex& address) +{ + ma_store(src, address, SizeHalfWord); +} + +void +MacroAssemblerMIPSCompat::store32(Register src, AbsoluteAddress address) +{ + movePtr(ImmPtr(address.addr), ScratchRegister); + store32(src, Address(ScratchRegister, 0)); +} + +void +MacroAssemblerMIPSCompat::store32(Register src, const Address& address) +{ + ma_store(src, address, SizeWord); +} + +void +MacroAssemblerMIPSCompat::store32(Imm32 src, const Address& address) +{ + move32(src, SecondScratchReg); + ma_store(SecondScratchReg, address, SizeWord); +} + +void +MacroAssemblerMIPSCompat::store32(Imm32 imm, const BaseIndex& dest) +{ + ma_store(imm, dest, SizeWord); +} + +void +MacroAssemblerMIPSCompat::store32(Register src, const BaseIndex& dest) +{ + ma_store(src, dest, SizeWord); +} + +template <typename T> +void +MacroAssemblerMIPSCompat::storePtr(ImmWord imm, T address) +{ + ma_li(SecondScratchReg, imm); + ma_store(SecondScratchReg, address, SizeWord); +} + +template void MacroAssemblerMIPSCompat::storePtr<Address>(ImmWord imm, Address address); +template void MacroAssemblerMIPSCompat::storePtr<BaseIndex>(ImmWord imm, BaseIndex address); + +template <typename T> +void +MacroAssemblerMIPSCompat::storePtr(ImmPtr imm, T address) +{ + storePtr(ImmWord(uintptr_t(imm.value)), address); +} + +template void MacroAssemblerMIPSCompat::storePtr<Address>(ImmPtr imm, Address address); +template void MacroAssemblerMIPSCompat::storePtr<BaseIndex>(ImmPtr imm, BaseIndex address); + +template <typename T> +void +MacroAssemblerMIPSCompat::storePtr(ImmGCPtr imm, T address) +{ + movePtr(imm, SecondScratchReg); + storePtr(SecondScratchReg, address); +} + +template void MacroAssemblerMIPSCompat::storePtr<Address>(ImmGCPtr imm, Address address); +template void MacroAssemblerMIPSCompat::storePtr<BaseIndex>(ImmGCPtr imm, BaseIndex address); + +void +MacroAssemblerMIPSCompat::storePtr(Register src, const Address& address) +{ + ma_store(src, address, SizeWord); +} + +void +MacroAssemblerMIPSCompat::storePtr(Register src, const BaseIndex& address) +{ + ma_store(src, address, SizeWord); +} + +void +MacroAssemblerMIPSCompat::storePtr(Register src, AbsoluteAddress dest) +{ + movePtr(ImmPtr(dest.addr), ScratchRegister); + storePtr(src, Address(ScratchRegister, 0)); +} + +void +MacroAssemblerMIPSCompat::storeUnalignedFloat32(const wasm::MemoryAccessDesc& access, + FloatRegister src, Register temp, const BaseIndex& dest) +{ + MOZ_ASSERT(MOZ_LITTLE_ENDIAN, "Wasm-only; wasm is disabled on big-endian."); + computeScaledAddress(dest, SecondScratchReg); + moveFromFloat32(src, temp); + + BufferOffset store; + if (Imm16::IsInSignedRange(dest.offset) && Imm16::IsInSignedRange(dest.offset + 3)) { + store = as_swl(temp, SecondScratchReg, dest.offset + 3); + as_swr(temp, SecondScratchReg, dest.offset); + } else { + ma_li(ScratchRegister, Imm32(dest.offset)); + as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister); + store = as_swl(temp, ScratchRegister, 3); + as_swr(temp, ScratchRegister, 0); + } + append(access, store.getOffset(), asMasm().framePushed()); +} + +void +MacroAssemblerMIPSCompat::storeUnalignedDouble(const wasm::MemoryAccessDesc& access, + FloatRegister src, Register temp, const BaseIndex& dest) +{ + MOZ_ASSERT(MOZ_LITTLE_ENDIAN, "Wasm-only; wasm is disabled on big-endian."); + computeScaledAddress(dest, SecondScratchReg); + + uint32_t framePushed = asMasm().framePushed(); + BufferOffset store; + if (Imm16::IsInSignedRange(dest.offset) && Imm16::IsInSignedRange(dest.offset + 7)) { + moveFromDoubleHi(src, temp); + store = as_swl(temp, SecondScratchReg, dest.offset + INT64HIGH_OFFSET + 3); + as_swr(temp, SecondScratchReg, dest.offset + INT64HIGH_OFFSET); + moveFromDoubleLo(src, temp); + as_swl(temp, SecondScratchReg, dest.offset + INT64LOW_OFFSET + 3); + as_swr(temp, SecondScratchReg, dest.offset + INT64LOW_OFFSET); + + } else { + ma_li(ScratchRegister, Imm32(dest.offset)); + as_daddu(ScratchRegister, SecondScratchReg, ScratchRegister); + moveFromDoubleHi(src, temp); + store = as_swl(temp, ScratchRegister, INT64HIGH_OFFSET + 3); + as_swr(temp, ScratchRegister, INT64HIGH_OFFSET); + moveFromDoubleLo(src, temp); + as_swl(temp, ScratchRegister, INT64LOW_OFFSET + 3); + as_swr(temp, ScratchRegister, INT64LOW_OFFSET); + } + append(access, store.getOffset(), framePushed); +} + +void +MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output) +{ + as_roundwd(ScratchDoubleReg, input); + ma_li(ScratchRegister, Imm32(255)); + as_mfc1(output, ScratchDoubleReg); + zeroDouble(ScratchDoubleReg); + as_sltiu(SecondScratchReg, output, 255); + as_colt(DoubleFloat, ScratchDoubleReg, input); + // if res > 255; res = 255; + as_movz(output, ScratchRegister, SecondScratchReg); + // if !(input > 0); res = 0; + as_movf(output, zero); +} + +// higher level tag testing code +Operand +MacroAssemblerMIPSCompat::ToPayload(Operand base) +{ + return Operand(Register::FromCode(base.base()), base.disp() + PAYLOAD_OFFSET); +} + +Operand +MacroAssemblerMIPSCompat::ToType(Operand base) +{ + return Operand(Register::FromCode(base.base()), base.disp() + TAG_OFFSET); +} + +void +MacroAssemblerMIPSCompat::testNullSet(Condition cond, const ValueOperand& value, Register dest) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + ma_cmp_set(dest, value.typeReg(), ImmType(JSVAL_TYPE_NULL), cond); +} + +void +MacroAssemblerMIPSCompat::testObjectSet(Condition cond, const ValueOperand& value, Register dest) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + ma_cmp_set(dest, value.typeReg(), ImmType(JSVAL_TYPE_OBJECT), cond); +} + +void +MacroAssemblerMIPSCompat::testUndefinedSet(Condition cond, const ValueOperand& value, Register dest) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + ma_cmp_set(dest, value.typeReg(), ImmType(JSVAL_TYPE_UNDEFINED), cond); +} + +// unboxing code +void +MacroAssemblerMIPSCompat::unboxNonDouble(const ValueOperand& operand, Register dest, JSValueType) +{ + if (operand.payloadReg() != dest) + ma_move(dest, operand.payloadReg()); +} + +void +MacroAssemblerMIPSCompat::unboxNonDouble(const Address& src, Register dest, JSValueType) +{ + ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::unboxNonDouble(const BaseIndex& src, Register dest, JSValueType) +{ + computeScaledAddress(src, SecondScratchReg); + ma_lw(dest, Address(SecondScratchReg, src.offset + PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::unboxInt32(const ValueOperand& operand, Register dest) +{ + ma_move(dest, operand.payloadReg()); +} + +void +MacroAssemblerMIPSCompat::unboxInt32(const Address& src, Register dest) +{ + ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::unboxBoolean(const ValueOperand& operand, Register dest) +{ + ma_move(dest, operand.payloadReg()); +} + +void +MacroAssemblerMIPSCompat::unboxBoolean(const Address& src, Register dest) +{ + ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::unboxDouble(const ValueOperand& operand, FloatRegister dest) +{ + moveToDoubleLo(operand.payloadReg(), dest); + moveToDoubleHi(operand.typeReg(), dest); +} + +void +MacroAssemblerMIPSCompat::unboxDouble(const Address& src, FloatRegister dest) +{ + ma_lw(ScratchRegister, Address(src.base, src.offset + PAYLOAD_OFFSET)); + moveToDoubleLo(ScratchRegister, dest); + ma_lw(ScratchRegister, Address(src.base, src.offset + TAG_OFFSET)); + moveToDoubleHi(ScratchRegister, dest); +} + +void +MacroAssemblerMIPSCompat::unboxString(const ValueOperand& operand, Register dest) +{ + ma_move(dest, operand.payloadReg()); +} + +void +MacroAssemblerMIPSCompat::unboxString(const Address& src, Register dest) +{ + ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::unboxObject(const ValueOperand& src, Register dest) +{ + ma_move(dest, src.payloadReg()); +} + +void +MacroAssemblerMIPSCompat::unboxObject(const Address& src, Register dest) +{ + ma_lw(dest, Address(src.base, src.offset + PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::unboxValue(const ValueOperand& src, AnyRegister dest, JSValueType) +{ + if (dest.isFloat()) { + Label notInt32, end; + asMasm().branchTestInt32(Assembler::NotEqual, src, ¬Int32); + convertInt32ToDouble(src.payloadReg(), dest.fpu()); + ma_b(&end, ShortJump); + bind(¬Int32); + unboxDouble(src, dest.fpu()); + bind(&end); + } else if (src.payloadReg() != dest.gpr()) { + ma_move(dest.gpr(), src.payloadReg()); + } +} + +void +MacroAssemblerMIPSCompat::unboxPrivate(const ValueOperand& src, Register dest) +{ + ma_move(dest, src.payloadReg()); +} + +void +MacroAssemblerMIPSCompat::boxDouble(FloatRegister src, const ValueOperand& dest, FloatRegister) +{ + moveFromDoubleLo(src, dest.payloadReg()); + moveFromDoubleHi(src, dest.typeReg()); +} + +void +MacroAssemblerMIPSCompat::boxNonDouble(JSValueType type, Register src, + const ValueOperand& dest) +{ + if (src != dest.payloadReg()) + ma_move(dest.payloadReg(), src); + ma_li(dest.typeReg(), ImmType(type)); +} + +void +MacroAssemblerMIPSCompat::boolValueToDouble(const ValueOperand& operand, FloatRegister dest) +{ + convertBoolToInt32(operand.payloadReg(), ScratchRegister); + convertInt32ToDouble(ScratchRegister, dest); +} + +void +MacroAssemblerMIPSCompat::int32ValueToDouble(const ValueOperand& operand, + FloatRegister dest) +{ + convertInt32ToDouble(operand.payloadReg(), dest); +} + +void +MacroAssemblerMIPSCompat::boolValueToFloat32(const ValueOperand& operand, + FloatRegister dest) +{ + + convertBoolToInt32(operand.payloadReg(), ScratchRegister); + convertInt32ToFloat32(ScratchRegister, dest); +} + +void +MacroAssemblerMIPSCompat::int32ValueToFloat32(const ValueOperand& operand, + FloatRegister dest) +{ + convertInt32ToFloat32(operand.payloadReg(), dest); +} + +void +MacroAssemblerMIPSCompat::loadConstantFloat32(float f, FloatRegister dest) +{ + ma_lis(dest, f); +} + +void +MacroAssemblerMIPSCompat::loadInt32OrDouble(const Address& src, FloatRegister dest) +{ + Label notInt32, end; + // If it's an int, convert it to double. + ma_lw(SecondScratchReg, Address(src.base, src.offset + TAG_OFFSET)); + asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, ¬Int32); + ma_lw(SecondScratchReg, Address(src.base, src.offset + PAYLOAD_OFFSET)); + convertInt32ToDouble(SecondScratchReg, dest); + ma_b(&end, ShortJump); + + // Not an int, just load as double. + bind(¬Int32); + ma_ld(dest, src); + bind(&end); +} + +void +MacroAssemblerMIPSCompat::loadInt32OrDouble(Register base, Register index, + FloatRegister dest, int32_t shift) +{ + Label notInt32, end; + + // If it's an int, convert it to double. + + computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg); + // Since we only have one scratch, we need to stomp over it with the tag. + load32(Address(SecondScratchReg, TAG_OFFSET), SecondScratchReg); + asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, ¬Int32); + + computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg); + load32(Address(SecondScratchReg, PAYLOAD_OFFSET), SecondScratchReg); + convertInt32ToDouble(SecondScratchReg, dest); + ma_b(&end, ShortJump); + + // Not an int, just load as double. + bind(¬Int32); + // First, recompute the offset that had been stored in the scratch register + // since the scratch register was overwritten loading in the type. + computeScaledAddress(BaseIndex(base, index, ShiftToScale(shift)), SecondScratchReg); + loadDouble(Address(SecondScratchReg, 0), dest); + bind(&end); +} + +void +MacroAssemblerMIPSCompat::loadConstantDouble(double dp, FloatRegister dest) +{ + ma_lid(dest, dp); +} + +Register +MacroAssemblerMIPSCompat::extractObject(const Address& address, Register scratch) +{ + ma_lw(scratch, Address(address.base, address.offset + PAYLOAD_OFFSET)); + return scratch; +} + +Register +MacroAssemblerMIPSCompat::extractTag(const Address& address, Register scratch) +{ + ma_lw(scratch, Address(address.base, address.offset + TAG_OFFSET)); + return scratch; +} + +Register +MacroAssemblerMIPSCompat::extractTag(const BaseIndex& address, Register scratch) +{ + computeScaledAddress(address, scratch); + return extractTag(Address(scratch, address.offset), scratch); +} + + +uint32_t +MacroAssemblerMIPSCompat::getType(const Value& val) +{ + return val.toNunboxTag(); +} + +void +MacroAssemblerMIPSCompat::moveData(const Value& val, Register data) +{ + if (val.isGCThing()) + ma_li(data, ImmGCPtr(val.toGCThing())); + else + ma_li(data, Imm32(val.toNunboxPayload())); +} + +/* There are 3 paths trough backedge jump. They are listed here in the order + * in which instructions are executed. + * - The short jump is simple: + * b offset # Jumps directly to target. + * lui at, addr1_hi # In delay slot. Don't care about 'at' here. + * + * - The long jump to loop header: + * b label1 + * lui at, addr1_hi # In delay slot. We use the value in 'at' later. + * label1: + * ori at, addr1_lo + * jr at + * lui at, addr2_hi # In delay slot. Don't care about 'at' here. + * + * - The long jump to interrupt loop: + * b label2 + * lui at, addr1_hi # In delay slot. Don't care about 'at' here. + * label2: + * lui at, addr2_hi + * ori at, addr2_lo + * jr at + * nop # In delay slot. + * + * The backedge is done this way to avoid patching lui+ori pair while it is + * being executed. Look also at jit::PatchBackedge(). + */ +CodeOffsetJump +MacroAssemblerMIPSCompat::backedgeJump(RepatchLabel* label, Label* documentation) +{ + // Only one branch per label. + MOZ_ASSERT(!label->used()); + + BufferOffset bo = nextOffset(); + label->use(bo.getOffset()); + + // Backedges are short jumps when bound, but can become long when patched. + m_buffer.ensureSpace(8 * sizeof(uint32_t)); + // Jump to "label1" by default to jump to the loop header. + as_b(BOffImm16(2 * sizeof(uint32_t))); + // No need for nop here. We can safely put next instruction in delay slot. + ma_liPatchable(ScratchRegister, Imm32(LabelBase::INVALID_OFFSET)); + MOZ_ASSERT(nextOffset().getOffset() - bo.getOffset() == 3 * sizeof(uint32_t)); + as_jr(ScratchRegister); + // No need for nop here. We can safely put next instruction in delay slot. + ma_liPatchable(ScratchRegister, Imm32(LabelBase::INVALID_OFFSET)); + as_jr(ScratchRegister); + as_nop(); + MOZ_ASSERT(nextOffset().getOffset() - bo.getOffset() == 8 * sizeof(uint32_t)); + return CodeOffsetJump(bo.getOffset()); +} + +CodeOffsetJump +MacroAssemblerMIPSCompat::jumpWithPatch(RepatchLabel* label, Label* documentation) +{ + // Only one branch per label. + MOZ_ASSERT(!label->used()); + + BufferOffset bo = nextOffset(); + label->use(bo.getOffset()); + ma_liPatchable(ScratchRegister, Imm32(LabelBase::INVALID_OFFSET)); + as_jr(ScratchRegister); + as_nop(); + return CodeOffsetJump(bo.getOffset()); +} + +///////////////////////////////////////////////////////////////// +// X86/X64-common/ARM/MIPS interface. +///////////////////////////////////////////////////////////////// +void +MacroAssemblerMIPSCompat::storeValue(ValueOperand val, Operand dst) +{ + storeValue(val, Address(Register::FromCode(dst.base()), dst.disp())); +} + +void +MacroAssemblerMIPSCompat::storeValue(ValueOperand val, const BaseIndex& dest) +{ + computeScaledAddress(dest, SecondScratchReg); + storeValue(val, Address(SecondScratchReg, dest.offset)); +} + +void +MacroAssemblerMIPSCompat::storeValue(JSValueType type, Register reg, BaseIndex dest) +{ + computeScaledAddress(dest, ScratchRegister); + + // Make sure that ma_sw doesn't clobber ScratchRegister + int32_t offset = dest.offset; + if (!Imm16::IsInSignedRange(offset)) { + ma_li(SecondScratchReg, Imm32(offset)); + as_addu(ScratchRegister, ScratchRegister, SecondScratchReg); + offset = 0; + } + + storeValue(type, reg, Address(ScratchRegister, offset)); +} + +void +MacroAssemblerMIPSCompat::storeValue(ValueOperand val, const Address& dest) +{ + ma_sw(val.payloadReg(), Address(dest.base, dest.offset + PAYLOAD_OFFSET)); + ma_sw(val.typeReg(), Address(dest.base, dest.offset + TAG_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::storeValue(JSValueType type, Register reg, Address dest) +{ + MOZ_ASSERT(dest.base != SecondScratchReg); + + ma_sw(reg, Address(dest.base, dest.offset + PAYLOAD_OFFSET)); + ma_li(SecondScratchReg, ImmTag(JSVAL_TYPE_TO_TAG(type))); + ma_sw(SecondScratchReg, Address(dest.base, dest.offset + TAG_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::storeValue(const Value& val, Address dest) +{ + MOZ_ASSERT(dest.base != SecondScratchReg); + + ma_li(SecondScratchReg, Imm32(getType(val))); + ma_sw(SecondScratchReg, Address(dest.base, dest.offset + TAG_OFFSET)); + moveData(val, SecondScratchReg); + ma_sw(SecondScratchReg, Address(dest.base, dest.offset + PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::storeValue(const Value& val, BaseIndex dest) +{ + computeScaledAddress(dest, ScratchRegister); + + // Make sure that ma_sw doesn't clobber ScratchRegister + int32_t offset = dest.offset; + if (!Imm16::IsInSignedRange(offset)) { + ma_li(SecondScratchReg, Imm32(offset)); + as_addu(ScratchRegister, ScratchRegister, SecondScratchReg); + offset = 0; + } + storeValue(val, Address(ScratchRegister, offset)); +} + +void +MacroAssemblerMIPSCompat::loadValue(const BaseIndex& addr, ValueOperand val) +{ + computeScaledAddress(addr, SecondScratchReg); + loadValue(Address(SecondScratchReg, addr.offset), val); +} + +void +MacroAssemblerMIPSCompat::loadValue(Address src, ValueOperand val) +{ + // Ensure that loading the payload does not erase the pointer to the + // Value in memory. + if (src.base != val.payloadReg()) { + ma_lw(val.payloadReg(), Address(src.base, src.offset + PAYLOAD_OFFSET)); + ma_lw(val.typeReg(), Address(src.base, src.offset + TAG_OFFSET)); + } else { + ma_lw(val.typeReg(), Address(src.base, src.offset + TAG_OFFSET)); + ma_lw(val.payloadReg(), Address(src.base, src.offset + PAYLOAD_OFFSET)); + } +} + +void +MacroAssemblerMIPSCompat::tagValue(JSValueType type, Register payload, ValueOperand dest) +{ + MOZ_ASSERT(payload != dest.typeReg()); + ma_li(dest.typeReg(), ImmType(type)); + if (payload != dest.payloadReg()) + ma_move(dest.payloadReg(), payload); +} + +void +MacroAssemblerMIPSCompat::pushValue(ValueOperand val) +{ + // Allocate stack slots for type and payload. One for each. + asMasm().subPtr(Imm32(sizeof(Value)), StackPointer); + // Store type and payload. + storeValue(val, Address(StackPointer, 0)); +} + +void +MacroAssemblerMIPSCompat::pushValue(const Address& addr) +{ + // Allocate stack slots for type and payload. One for each. + ma_subu(StackPointer, StackPointer, Imm32(sizeof(Value))); + // If address is based on StackPointer its offset needs to be adjusted + // to accommodate for previous stack allocation. + int32_t offset = addr.base != StackPointer ? addr.offset : addr.offset + sizeof(Value); + // Store type and payload. + ma_lw(ScratchRegister, Address(addr.base, offset + TAG_OFFSET)); + ma_sw(ScratchRegister, Address(StackPointer, TAG_OFFSET)); + ma_lw(ScratchRegister, Address(addr.base, offset + PAYLOAD_OFFSET)); + ma_sw(ScratchRegister, Address(StackPointer, PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::popValue(ValueOperand val) +{ + // Load payload and type. + as_lw(val.payloadReg(), StackPointer, PAYLOAD_OFFSET); + as_lw(val.typeReg(), StackPointer, TAG_OFFSET); + // Free stack. + as_addiu(StackPointer, StackPointer, sizeof(Value)); +} + +void +MacroAssemblerMIPSCompat::storePayload(const Value& val, Address dest) +{ + moveData(val, SecondScratchReg); + ma_sw(SecondScratchReg, Address(dest.base, dest.offset + PAYLOAD_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::storePayload(Register src, Address dest) +{ + ma_sw(src, Address(dest.base, dest.offset + PAYLOAD_OFFSET)); + return; +} + +void +MacroAssemblerMIPSCompat::storePayload(const Value& val, const BaseIndex& dest) +{ + MOZ_ASSERT(dest.offset == 0); + + computeScaledAddress(dest, SecondScratchReg); + + moveData(val, ScratchRegister); + + as_sw(ScratchRegister, SecondScratchReg, NUNBOX32_PAYLOAD_OFFSET); +} + +void +MacroAssemblerMIPSCompat::storePayload(Register src, const BaseIndex& dest) +{ + MOZ_ASSERT(dest.offset == 0); + + computeScaledAddress(dest, SecondScratchReg); + as_sw(src, SecondScratchReg, NUNBOX32_PAYLOAD_OFFSET); +} + +void +MacroAssemblerMIPSCompat::storeTypeTag(ImmTag tag, Address dest) +{ + ma_li(SecondScratchReg, tag); + ma_sw(SecondScratchReg, Address(dest.base, dest.offset + TAG_OFFSET)); +} + +void +MacroAssemblerMIPSCompat::storeTypeTag(ImmTag tag, const BaseIndex& dest) +{ + MOZ_ASSERT(dest.offset == 0); + + computeScaledAddress(dest, SecondScratchReg); + ma_li(ScratchRegister, tag); + as_sw(ScratchRegister, SecondScratchReg, TAG_OFFSET); +} + +void +MacroAssemblerMIPSCompat::breakpoint() +{ + as_break(0); +} + +void +MacroAssemblerMIPSCompat::ensureDouble(const ValueOperand& source, FloatRegister dest, + Label* failure) +{ + Label isDouble, done; + asMasm().branchTestDouble(Assembler::Equal, source.typeReg(), &isDouble); + asMasm().branchTestInt32(Assembler::NotEqual, source.typeReg(), failure); + + convertInt32ToDouble(source.payloadReg(), dest); + jump(&done); + + bind(&isDouble); + unboxDouble(source, dest); + + bind(&done); +} + +void +MacroAssemblerMIPSCompat::checkStackAlignment() +{ +#ifdef DEBUG + Label aligned; + as_andi(ScratchRegister, sp, ABIStackAlignment - 1); + ma_b(ScratchRegister, zero, &aligned, Equal, ShortJump); + as_break(BREAK_STACK_UNALIGNED); + bind(&aligned); +#endif +} + +void +MacroAssemblerMIPSCompat::alignStackPointer() +{ + movePtr(StackPointer, SecondScratchReg); + asMasm().subPtr(Imm32(sizeof(uintptr_t)), StackPointer); + asMasm().andPtr(Imm32(~(ABIStackAlignment - 1)), StackPointer); + storePtr(SecondScratchReg, Address(StackPointer, 0)); +} + +void +MacroAssemblerMIPSCompat::restoreStackPointer() +{ + loadPtr(Address(StackPointer, 0), StackPointer); +} + +void +MacroAssemblerMIPSCompat::handleFailureWithHandlerTail(void* handler, Label* profilerExitTail) +{ + // Reserve space for exception information. + int size = (sizeof(ResumeFromException) + ABIStackAlignment) & ~(ABIStackAlignment - 1); + asMasm().subPtr(Imm32(size), StackPointer); + ma_move(a0, StackPointer); // Use a0 since it is a first function argument + + // Call the handler. + asMasm().setupUnalignedABICall(a1); + asMasm().passABIArg(a0); + asMasm().callWithABI(handler, MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame); + + Label entryFrame; + Label catch_; + Label finally; + Label return_; + Label bailout; + Label wasm; + + // Already clobbered a0, so use it... + load32(Address(StackPointer, offsetof(ResumeFromException, kind)), a0); + asMasm().branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), + &entryFrame); + asMasm().branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_CATCH), &catch_); + asMasm().branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_FINALLY), &finally); + asMasm().branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_FORCED_RETURN), + &return_); + asMasm().branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout); + asMasm().branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_WASM), &wasm); + + breakpoint(); // Invalid kind. + + // No exception handler. Load the error value, load the new stack pointer + // and return from the entry frame. + bind(&entryFrame); + asMasm().moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); + loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)), StackPointer); + + // We're going to be returning by the ion calling convention + ma_pop(ra); + as_jr(ra); + as_nop(); + + // If we found a catch handler, this must be a baseline frame. Restore + // state and jump to the catch block. + bind(&catch_); + loadPtr(Address(StackPointer, offsetof(ResumeFromException, target)), a0); + loadPtr(Address(StackPointer, offsetof(ResumeFromException, framePointer)), BaselineFrameReg); + loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)), StackPointer); + jump(a0); + + // If we found a finally block, this must be a baseline frame. Push + // two values expected by JSOP_RETSUB: BooleanValue(true) and the + // exception. + bind(&finally); + ValueOperand exception = ValueOperand(a1, a2); + loadValue(Address(sp, offsetof(ResumeFromException, exception)), exception); + + loadPtr(Address(sp, offsetof(ResumeFromException, target)), a0); + loadPtr(Address(sp, offsetof(ResumeFromException, framePointer)), BaselineFrameReg); + loadPtr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp); + + pushValue(BooleanValue(true)); + pushValue(exception); + jump(a0); + + // Only used in debug mode. Return BaselineFrame->returnValue() to the + // caller. + bind(&return_); + loadPtr(Address(StackPointer, offsetof(ResumeFromException, framePointer)), BaselineFrameReg); + loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)), StackPointer); + loadValue(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfReturnValue()), + JSReturnOperand); + ma_move(StackPointer, BaselineFrameReg); + pop(BaselineFrameReg); + + // If profiling is enabled, then update the lastProfilingFrame to refer to caller + // frame before returning. + { + Label skipProfilingInstrumentation; + // Test if profiler enabled. + AbsoluteAddress addressOfEnabled(GetJitContext()->runtime->geckoProfiler().addressOfEnabled()); + asMasm().branch32(Assembler::Equal, addressOfEnabled, Imm32(0), + &skipProfilingInstrumentation); + jump(profilerExitTail); + bind(&skipProfilingInstrumentation); + } + + ret(); + + // If we are bailing out to baseline to handle an exception, jump to + // the bailout tail stub. + bind(&bailout); + loadPtr(Address(sp, offsetof(ResumeFromException, bailoutInfo)), a2); + ma_li(ReturnReg, Imm32(BAILOUT_RETURN_OK)); + loadPtr(Address(sp, offsetof(ResumeFromException, target)), a1); + jump(a1); + + // If we are throwing and the innermost frame was a wasm frame, reset SP and + // FP; SP is pointing to the unwound return address to the wasm entry, so + // we can just ret(). + bind(&wasm); + loadPtr(Address(StackPointer, offsetof(ResumeFromException, framePointer)), FramePointer); + loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)), StackPointer); + ret(); +} + +CodeOffset +MacroAssemblerMIPSCompat::toggledJump(Label* label) +{ + CodeOffset ret(nextOffset().getOffset()); + ma_b(label); + return ret; +} + +CodeOffset +MacroAssemblerMIPSCompat::toggledCall(JitCode* target, bool enabled) +{ + BufferOffset bo = nextOffset(); + CodeOffset offset(bo.getOffset()); + addPendingJump(bo, ImmPtr(target->raw()), Relocation::JITCODE); + ma_liPatchable(ScratchRegister, ImmPtr(target->raw())); + if (enabled) { + as_jalr(ScratchRegister); + as_nop(); + } else { + as_nop(); + as_nop(); + } + MOZ_ASSERT_IF(!oom(), nextOffset().getOffset() - offset.offset() == ToggledCallSize(nullptr)); + return offset; +} + +void +MacroAssemblerMIPSCompat::profilerEnterFrame(Register framePtr, Register scratch) +{ + asMasm().loadJSContext(scratch); + loadPtr(Address(scratch, offsetof(JSContext, profilingActivation_)), scratch); + storePtr(framePtr, Address(scratch, JitActivation::offsetOfLastProfilingFrame())); + storePtr(ImmPtr(nullptr), Address(scratch, JitActivation::offsetOfLastProfilingCallSite())); +} + +void +MacroAssemblerMIPSCompat::profilerExitFrame() +{ + jump(GetJitContext()->runtime->jitRuntime()->getProfilerExitFrameTail()); +} + +void +MacroAssembler::subFromStackPtr(Imm32 imm32) +{ + if (imm32.value) + asMasm().subPtr(imm32, StackPointer); +} + +//{{{ check_macroassembler_style +// =============================================================== +// Stack manipulation functions. + +void +MacroAssembler::PushRegsInMask(LiveRegisterSet set) +{ + int32_t diffF = set.fpus().getPushSizeInBytes(); + int32_t diffG = set.gprs().size() * sizeof(intptr_t); + + reserveStack(diffG); + for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) { + diffG -= sizeof(intptr_t); + storePtr(*iter, Address(StackPointer, diffG)); + } + MOZ_ASSERT(diffG == 0); + + if (diffF > 0) { + // Double values have to be aligned. We reserve extra space so that we can + // start writing from the first aligned location. + // We reserve a whole extra double so that the buffer has even size. + ma_and(SecondScratchReg, sp, Imm32(~(ABIStackAlignment - 1))); + reserveStack(diffF); + + diffF -= sizeof(double); + + for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); ++iter) { + as_sdc1(*iter, SecondScratchReg, -diffF); + diffF -= sizeof(double); + } + + MOZ_ASSERT(diffF == 0); + } +} + +void +MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore) +{ + int32_t diffG = set.gprs().size() * sizeof(intptr_t); + int32_t diffF = set.fpus().getPushSizeInBytes(); + const int32_t reservedG = diffG; + const int32_t reservedF = diffF; + + if (reservedF > 0) { + // Read the buffer form the first aligned location. + ma_addu(SecondScratchReg, sp, Imm32(reservedF)); + ma_and(SecondScratchReg, SecondScratchReg, Imm32(~(ABIStackAlignment - 1))); + + diffF -= sizeof(double); + + LiveFloatRegisterSet fpignore(ignore.fpus().reduceSetForPush()); + for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); ++iter) { + if (!ignore.has(*iter)) + as_ldc1(*iter, SecondScratchReg, -diffF); + diffF -= sizeof(double); + } + freeStack(reservedF); + MOZ_ASSERT(diffF == 0); + } + + for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) { + diffG -= sizeof(intptr_t); + if (!ignore.has(*iter)) + loadPtr(Address(StackPointer, diffG), *iter); + } + freeStack(reservedG); + MOZ_ASSERT(diffG == 0); +} + +void +MacroAssembler::storeRegsInMask(LiveRegisterSet set, Address dest, Register scratch) +{ + int32_t diffF = set.fpus().getPushSizeInBytes(); + int32_t diffG = set.gprs().size() * sizeof(intptr_t); + + MOZ_ASSERT(dest.offset >= diffG + diffF); + MOZ_ASSERT(dest.base == StackPointer); + + for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) { + diffG -= sizeof(intptr_t); + dest.offset -= sizeof(intptr_t); + storePtr(*iter, dest); + } + MOZ_ASSERT(diffG == 0); + + if (diffF > 0) { + + computeEffectiveAddress(dest, scratch); + ma_and(scratch, scratch, Imm32(~(ABIStackAlignment - 1))); + + diffF -= sizeof(double); + + for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); ++iter) { + as_sdc1(*iter, scratch, -diffF); + diffF -= sizeof(double); + } + MOZ_ASSERT(diffF == 0); + } +} +// =============================================================== +// ABI function calls. + +void +MacroAssembler::setupUnalignedABICall(Register scratch) +{ + MOZ_ASSERT(!IsCompilingWasm(), "wasm should only use aligned ABI calls"); + setupABICall(); + dynamicAlignment_ = true; + + ma_move(scratch, StackPointer); + + // Force sp to be aligned + asMasm().subPtr(Imm32(sizeof(uintptr_t)), StackPointer); + ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1))); + storePtr(scratch, Address(StackPointer, 0)); +} + +void +MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromWasm) +{ + MOZ_ASSERT(inCall_); + uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar(); + + // Reserve place for $ra. + stackForCall += sizeof(intptr_t); + + if (dynamicAlignment_) { + stackForCall += ComputeByteAlignment(stackForCall, ABIStackAlignment); + } else { + uint32_t alignmentAtPrologue = callFromWasm ? sizeof(wasm::Frame) : 0; + stackForCall += ComputeByteAlignment(stackForCall + framePushed() + alignmentAtPrologue, + ABIStackAlignment); + } + + *stackAdjust = stackForCall; + reserveStack(stackForCall); + + // Save $ra because call is going to clobber it. Restore it in + // callWithABIPost. NOTE: This is needed for calls from SharedIC. + // Maybe we can do this differently. + storePtr(ra, Address(StackPointer, stackForCall - sizeof(intptr_t))); + + // Position all arguments. + { + enoughMemory_ = enoughMemory_ && moveResolver_.resolve(); + if (!enoughMemory_) + return; + + MoveEmitter emitter(*this); + emitter.emit(moveResolver_); + emitter.finish(); + } + + assertStackAlignment(ABIStackAlignment); +} + +void +MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result, bool callFromWasm) +{ + // Restore ra value (as stored in callWithABIPre()). + loadPtr(Address(StackPointer, stackAdjust - sizeof(intptr_t)), ra); + + if (dynamicAlignment_) { + // Restore sp value from stack (as stored in setupUnalignedABICall()). + loadPtr(Address(StackPointer, stackAdjust), StackPointer); + // Use adjustFrame instead of freeStack because we already restored sp. + adjustFrame(-stackAdjust); + } else { + freeStack(stackAdjust); + } + +#ifdef DEBUG + MOZ_ASSERT(inCall_); + inCall_ = false; +#endif +} + +void +MacroAssembler::callWithABINoProfiler(Register fun, MoveOp::Type result) +{ + // Load the callee in t9, no instruction between the lw and call + // should clobber it. Note that we can't use fun.base because it may + // be one of the IntArg registers clobbered before the call. + ma_move(t9, fun); + uint32_t stackAdjust; + callWithABIPre(&stackAdjust); + call(t9); + callWithABIPost(stackAdjust, result); +} + +void +MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result) +{ + // Load the callee in t9, as above. + loadPtr(Address(fun.base, fun.offset), t9); + uint32_t stackAdjust; + callWithABIPre(&stackAdjust); + call(t9); + callWithABIPost(stackAdjust, result); +} +// =============================================================== +// Move instructions + +void +MacroAssembler::moveValue(const TypedOrValueRegister& src, const ValueOperand& dest) +{ + if (src.hasValue()) { + moveValue(src.valueReg(), dest); + return; + } + + MIRType type = src.type(); + AnyRegister reg = src.typedReg(); + + if (!IsFloatingPointType(type)) { + mov(ImmWord(MIRTypeToTag(type)), dest.typeReg()); + if (reg.gpr() != dest.payloadReg()) + move32(reg.gpr(), dest.payloadReg()); + return; + } + + ScratchDoubleScope scratch(*this); + FloatRegister freg = reg.fpu(); + if (type == MIRType::Float32) { + convertFloat32ToDouble(freg, scratch); + freg = scratch; + } + boxDouble(freg, dest, scratch); +} + +void +MacroAssembler::moveValue(const ValueOperand& src, const ValueOperand& dest) +{ + Register s0 = src.typeReg(); + Register s1 = src.payloadReg(); + Register d0 = dest.typeReg(); + Register d1 = dest.payloadReg(); + + // Either one or both of the source registers could be the same as a + // destination register. + if (s1 == d0) { + if (s0 == d1) { + // If both are, this is just a swap of two registers. + ScratchRegisterScope scratch(*this); + MOZ_ASSERT(d1 != scratch); + MOZ_ASSERT(d0 != scratch); + move32(d1, scratch); + move32(d0, d1); + move32(scratch, d0); + return; + } + // If only one is, copy that source first. + mozilla::Swap(s0, s1); + mozilla::Swap(d0, d1); + } + + if (s0 != d0) + move32(s0, d0); + if (s1 != d1) + move32(s1, d1); +} + +void +MacroAssembler::moveValue(const Value& src, const ValueOperand& dest) +{ + move32(Imm32(src.toNunboxTag()), dest.typeReg()); + if (src.isGCThing()) + movePtr(ImmGCPtr(src.toGCThing()), dest.payloadReg()); + else + move32(Imm32(src.toNunboxPayload()), dest.payloadReg()); +} + +// =============================================================== +// Branch functions + +void +MacroAssembler::branchValueIsNurseryCell(Condition cond, const Address& address, + Register temp, Label* label) +{ + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + Label done, checkAddress; + + branchTestObject(Assembler::Equal, address, &checkAddress); + branchTestString(Assembler::NotEqual, address, cond == Assembler::Equal ? &done : label); + + bind(&checkAddress); + loadPtr(address, temp); + branchPtrInNurseryChunk(cond, temp, InvalidReg, label); + + bind(&done); +} + +void +MacroAssembler::branchValueIsNurseryCell(Condition cond, ValueOperand value, + Register temp, Label* label) +{ + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + + Label done, checkAddress; + branchTestObject(Assembler::Equal, value, &checkAddress); + branchTestString(Assembler::NotEqual, value, cond == Assembler::Equal ? &done : label); + + bind(&checkAddress); + branchPtrInNurseryChunk(cond, value.payloadReg(), temp, label); + + bind(&done); +} + +void +MacroAssembler::branchValueIsNurseryObject(Condition cond, ValueOperand value, + Register temp, Label* label) +{ + MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); + Label done; + + branchTestObject(Assembler::NotEqual, value, cond == Assembler::Equal ? &done : label); + branchPtrInNurseryChunk(cond, value.payloadReg(), temp, label); + + bind(&done); +} + +void +MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, + const Value& rhs, Label* label) +{ + MOZ_ASSERT(cond == Equal || cond == NotEqual); + ScratchRegisterScope scratch(*this); + moveData(rhs, scratch); + + if (cond == Equal) { + Label done; + ma_b(lhs.payloadReg(), scratch, &done, NotEqual, ShortJump); + { + ma_b(lhs.typeReg(), Imm32(getType(rhs)), label, Equal); + } + bind(&done); + } else { + ma_b(lhs.payloadReg(), scratch, label, NotEqual); + + ma_b(lhs.typeReg(), Imm32(getType(rhs)), label, NotEqual); + } +} + +// ======================================================================== +// Memory access primitives. +template <typename T> +void +MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, + const T& dest, MIRType slotType) +{ + if (valueType == MIRType::Double) { + storeDouble(value.reg().typedReg().fpu(), dest); + return; + } + + // Store the type tag if needed. + if (valueType != slotType) + storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest); + + // Store the payload. + if (value.constant()) + storePayload(value.value(), dest); + else + storePayload(value.reg().typedReg().gpr(), dest); +} + +template void +MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, + const Address& dest, MIRType slotType); +template void +MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, + const BaseIndex& dest, MIRType slotType); + + +void +MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, bool isSaturating, + Label* oolEntry) +{ + Label done; + + as_truncwd(ScratchFloat32Reg, input); + ma_li(ScratchRegister, Imm32(INT32_MAX)); + moveFromFloat32(ScratchFloat32Reg, output); + + // For numbers in -1.[ : ]INT32_MAX range do nothing more + ma_b(output, ScratchRegister, &done, Assembler::Below, ShortJump); + + loadConstantDouble(double(INT32_MAX + 1ULL), ScratchDoubleReg); + ma_li(ScratchRegister, Imm32(INT32_MIN)); + as_subd(ScratchDoubleReg, input, ScratchDoubleReg); + as_truncwd(ScratchFloat32Reg, ScratchDoubleReg); + as_cfc1(SecondScratchReg, Assembler::FCSR); + moveFromFloat32(ScratchFloat32Reg, output); + ma_ext(SecondScratchReg, SecondScratchReg, Assembler::CauseV, 1); + ma_addu(output, ScratchRegister); + + ma_b(SecondScratchReg, Imm32(0), oolEntry, Assembler::NotEqual); + + bind(&done); +} + +void +MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, bool isSaturating, + Label* oolEntry) +{ + Label done; + + as_truncws(ScratchFloat32Reg, input); + ma_li(ScratchRegister, Imm32(INT32_MAX)); + moveFromFloat32(ScratchFloat32Reg, output); + // For numbers in -1.[ : ]INT32_MAX range do nothing more + ma_b(output, ScratchRegister, &done, Assembler::Below, ShortJump); + + loadConstantFloat32(float(INT32_MAX + 1ULL), ScratchFloat32Reg); + ma_li(ScratchRegister, Imm32(INT32_MIN)); + as_subs(ScratchFloat32Reg, input, ScratchFloat32Reg); + as_truncws(ScratchFloat32Reg, ScratchFloat32Reg); + as_cfc1(SecondScratchReg, Assembler::FCSR); + moveFromFloat32(ScratchFloat32Reg, output); + ma_ext(SecondScratchReg, SecondScratchReg, Assembler::CauseV, 1); + ma_addu(output, ScratchRegister); + + // Guard against negative values that result in 0 due the precision loss. + as_sltiu(ScratchRegister, output, 1); + ma_or(SecondScratchReg, ScratchRegister); + + ma_b(SecondScratchReg, Imm32(0), oolEntry, Assembler::NotEqual); + + bind(&done); +} + +void +MacroAssembler::wasmLoadI64(const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr, + Register ptrScratch, Register64 output) +{ + wasmLoadI64Impl(access, memoryBase, ptr, ptrScratch, output, InvalidReg); +} + +void +MacroAssembler::wasmUnalignedLoadI64(const wasm::MemoryAccessDesc& access, Register memoryBase, + Register ptr, Register ptrScratch, Register64 output, + Register tmp) +{ + wasmLoadI64Impl(access, memoryBase, ptr, ptrScratch, output, tmp); +} + +void +MacroAssembler::wasmStoreI64(const wasm::MemoryAccessDesc& access, Register64 value, + Register memoryBase, Register ptr, Register ptrScratch) +{ + wasmStoreI64Impl(access, value, memoryBase, ptr, ptrScratch, InvalidReg); +} + +void +MacroAssembler::wasmUnalignedStoreI64(const wasm::MemoryAccessDesc& access, Register64 value, + Register memoryBase, Register ptr, Register ptrScratch, + Register tmp) +{ + wasmStoreI64Impl(access, value, memoryBase, ptr, ptrScratch, tmp); +} + +void +MacroAssemblerMIPSCompat::wasmLoadI64Impl(const wasm::MemoryAccessDesc& access, Register memoryBase, + Register ptr, Register ptrScratch, Register64 output, + Register tmp) +{ + uint32_t offset = access.offset(); + MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg); + + // Maybe add the offset. + if (offset) { + asMasm().movePtr(ptr, ptrScratch); + asMasm().addPtr(Imm32(offset), ptrScratch); + ptr = ptrScratch; + } + + unsigned byteSize = access.byteSize(); + bool isSigned; + + switch (access.type()) { + case Scalar::Int8: isSigned = true; break; + case Scalar::Uint8: isSigned = false; break; + case Scalar::Int16: isSigned = true; break; + case Scalar::Uint16: isSigned = false; break; + case Scalar::Int32: isSigned = true; break; + case Scalar::Uint32: isSigned = false; break; + case Scalar::Int64: isSigned = true; break; + default: MOZ_CRASH("unexpected array type"); + } + + BaseIndex address(memoryBase, ptr, TimesOne); + MOZ_ASSERT(INT64LOW_OFFSET == 0); + if (IsUnaligned(access)) { + MOZ_ASSERT(tmp != InvalidReg); + if (byteSize <= 4) { + asMasm().ma_load_unaligned(access, output.low, address, tmp, + static_cast<LoadStoreSize>(8 * byteSize), + isSigned ? SignExtend : ZeroExtend); + if (!isSigned) + asMasm().move32(Imm32(0), output.high); + else + asMasm().ma_sra(output.high, output.low, Imm32(31)); + } else { + MOZ_ASSERT(output.low != ptr); + asMasm().ma_load_unaligned(access, output.low, address, tmp, SizeWord, ZeroExtend); + asMasm().ma_load_unaligned(access, output.high, + BaseIndex(HeapReg, ptr, TimesOne, INT64HIGH_OFFSET), + tmp, SizeWord, SignExtend); + } + return; + } + + asMasm().memoryBarrierBefore(access.sync()); + if (byteSize <= 4) { + asMasm().ma_load(output.low, address, static_cast<LoadStoreSize>(8 * byteSize), + isSigned ? SignExtend : ZeroExtend); + asMasm().append(access, asMasm().size() - 4 , asMasm().framePushed()); + if (!isSigned) + asMasm().move32(Imm32(0), output.high); + else + asMasm().ma_sra(output.high, output.low, Imm32(31)); + } else { + MOZ_ASSERT(output.low != ptr); + asMasm().ma_load(output.low, BaseIndex(HeapReg, ptr, TimesOne), SizeWord); + asMasm().append(access, asMasm().size() - 4 , asMasm().framePushed()); + asMasm().ma_load(output.high, BaseIndex(HeapReg, ptr, TimesOne, INT64HIGH_OFFSET), SizeWord); + asMasm().append(access, asMasm().size() - 4 , asMasm().framePushed()); + } + asMasm().memoryBarrierAfter(access.sync()); +} + +void +MacroAssemblerMIPSCompat::wasmStoreI64Impl(const wasm::MemoryAccessDesc& access, Register64 value, + Register memoryBase, Register ptr, Register ptrScratch, + Register tmp) +{ + uint32_t offset = access.offset(); + MOZ_ASSERT(offset < wasm::OffsetGuardLimit); + MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg); + + // Maybe add the offset. + if (offset) { + asMasm().addPtr(Imm32(offset), ptrScratch); + ptr = ptrScratch; + } + + unsigned byteSize = access.byteSize(); + bool isSigned; + switch (access.type()) { + case Scalar::Int8: isSigned = true; break; + case Scalar::Uint8: isSigned = false; break; + case Scalar::Int16: isSigned = true; break; + case Scalar::Uint16: isSigned = false; break; + case Scalar::Int32: isSigned = true; break; + case Scalar::Uint32: isSigned = false; break; + case Scalar::Int64: isSigned = true; break; + default: MOZ_CRASH("unexpected array type"); + } + + MOZ_ASSERT(INT64LOW_OFFSET == 0); + BaseIndex address(memoryBase, ptr, TimesOne); + if (IsUnaligned(access)) { + MOZ_ASSERT(tmp != InvalidReg); + if (byteSize <= 4) { + asMasm().ma_store_unaligned(access, value.low, address, tmp, + static_cast<LoadStoreSize>(8 * byteSize), + isSigned ? SignExtend : ZeroExtend); + } else { + asMasm().ma_store_unaligned(access, value.high, + BaseIndex(HeapReg, ptr, TimesOne, INT64HIGH_OFFSET), + tmp, SizeWord, SignExtend); + asMasm().ma_store_unaligned(access, value.low, address, tmp, SizeWord, ZeroExtend); + } + return; + } + + asMasm().memoryBarrierBefore(access.sync()); + if (byteSize <= 4) { + asMasm().ma_store(value.low, address, static_cast<LoadStoreSize>(8 * byteSize)); + asMasm().append(access, asMasm().size() - 4, asMasm().framePushed()); + } else { + asMasm().ma_store(value.high, BaseIndex(HeapReg, ptr, TimesOne, INT64HIGH_OFFSET), + SizeWord); + asMasm().append(access, asMasm().size() - 4, asMasm().framePushed()); + asMasm().ma_store(value.low, address, SizeWord); + } + asMasm().memoryBarrierAfter(access.sync()); +} + +static void +EnterAtomic64Region(MacroAssembler& masm, Register addr, Register spinlock, Register scratch) +{ + masm.movePtr(wasm::SymbolicAddress::js_jit_gAtomic64Lock, spinlock); + masm.as_lbu(zero, addr, 7); // Force memory trap on invalid access before we enter the spinlock. + + Label tryLock; + + masm.memoryBarrier(MembarFull); + + masm.bind(&tryLock); + + masm.as_ll(scratch, spinlock, 0); + masm.ma_b(scratch, scratch, &tryLock, Assembler::NonZero, ShortJump); + masm.ma_li(scratch, Imm32(1)); + masm.as_sc(scratch, spinlock, 0); + masm.ma_b(scratch, scratch, &tryLock, Assembler::Zero, ShortJump); + + masm.memoryBarrier(MembarFull); +} + +static void +ExitAtomic64Region(MacroAssembler& masm, Register spinlock) +{ + masm.memoryBarrier(MembarFull); + masm.as_sw(zero, spinlock, 0); + masm.memoryBarrier(MembarFull); +} + +template <typename T> +static void +AtomicLoad64(MacroAssembler& masm, const T& mem, Register64 temp, Register64 output) +{ + MOZ_ASSERT(temp.low == InvalidReg && temp.high == InvalidReg); + + masm.computeEffectiveAddress(mem, SecondScratchReg); + + EnterAtomic64Region(masm, /* addr= */ SecondScratchReg, /* spinlock= */ ScratchRegister, + /* scratch= */ output.low); + + masm.load64(Address(SecondScratchReg, 0), output); + + ExitAtomic64Region(masm, /* spinlock= */ ScratchRegister); +} + +void +MacroAssembler::atomicLoad64(const Synchronization&, const Address& mem, Register64 temp, + Register64 output) +{ + AtomicLoad64(*this, mem, temp, output); +} + +void +MacroAssembler::atomicLoad64(const Synchronization&, const BaseIndex& mem, Register64 temp, + Register64 output) +{ + AtomicLoad64(*this, mem, temp, output); +} + +template<typename T> +void +MacroAssemblerMIPSCompat::atomicStore64(const T& mem, Register temp, Register64 value) +{ + computeEffectiveAddress(mem, SecondScratchReg); + + EnterAtomic64Region(asMasm(), /* addr= */ SecondScratchReg, /* spinlock= */ ScratchRegister, + /* scratch= */ temp); + + store64(value, Address(SecondScratchReg, 0)); + + ExitAtomic64Region(asMasm(), /* spinlock= */ ScratchRegister); +} + +template void +MacroAssemblerMIPSCompat::atomicStore64(const Address& mem, Register temp, Register64 value); +template void +MacroAssemblerMIPSCompat::atomicStore64(const BaseIndex& mem, Register temp, Register64 value); + +template <typename T> +static void +CompareExchange64(MacroAssembler& masm, const T& mem, Register64 expect, Register64 replace, + Register64 output) +{ + MOZ_ASSERT(output != expect); + MOZ_ASSERT(output != replace); + + Label exit; + + masm.computeEffectiveAddress(mem, SecondScratchReg); + Address addr(SecondScratchReg, 0); + + EnterAtomic64Region(masm, /* addr= */ SecondScratchReg, /* spinlock= */ ScratchRegister, + /* scratch= */ output.low); + masm.load64(addr, output); + + masm.ma_b(output.low, expect.low, &exit, Assembler::NotEqual, ShortJump); + masm.ma_b(output.high, expect.high, &exit, Assembler::NotEqual, ShortJump); + masm.store64(replace, addr); + masm.bind(&exit); + ExitAtomic64Region(masm, /* spinlock= */ ScratchRegister); +} + + +void +MacroAssembler::compareExchange64(const Synchronization&, const Address& mem, Register64 expect, + Register64 replace, Register64 output) +{ + CompareExchange64(*this, mem, expect, replace, output); +} + +void +MacroAssembler::compareExchange64(const Synchronization&, const BaseIndex& mem, Register64 expect, + Register64 replace, Register64 output) +{ + CompareExchange64(*this, mem, expect, replace, output); +} + + +template <typename T> +static void +AtomicExchange64(MacroAssembler& masm, const T& mem, Register64 src, Register64 output) +{ + masm.computeEffectiveAddress(mem, SecondScratchReg); + Address addr(SecondScratchReg, 0); + + EnterAtomic64Region(masm, /* addr= */ SecondScratchReg, /* spinlock= */ ScratchRegister, + /* scratch= */ output.low); + + masm.load64(addr, output); + masm.store64(src, addr); + + ExitAtomic64Region(masm, /* spinlock= */ ScratchRegister); +} + + +void +MacroAssembler::atomicExchange64(const Synchronization&, const Address& mem, Register64 src, + Register64 output) +{ + AtomicExchange64(*this, mem, src, output); +} + +void +MacroAssembler::atomicExchange64(const Synchronization&, const BaseIndex& mem, Register64 src, + Register64 output) +{ + AtomicExchange64(*this, mem, src, output); +} + +template<typename T> +static void +AtomicFetchOp64(MacroAssembler& masm, AtomicOp op, Register64 value, const T& mem, + Register64 temp, Register64 output) +{ + masm.computeEffectiveAddress(mem, SecondScratchReg); + + EnterAtomic64Region(masm, /* addr= */ SecondScratchReg, /* spinlock= */ ScratchRegister, + /* scratch= */ output.low); + + masm.load64(Address(SecondScratchReg, 0), output); + + switch(op) { + case AtomicFetchAddOp: + masm.as_addu(temp.low, output.low, value.low); + masm.as_sltu(temp.high, temp.low, output.low); + masm.as_addu(temp.high, temp.high, output.high); + masm.as_addu(temp.high, temp.high, value.high); + break; + case AtomicFetchSubOp: + masm.as_sltu(temp.high, output.low, value.low); + masm.as_subu(temp.high, output.high, temp.high); + masm.as_subu(temp.low, output.low, value.low); + masm.as_subu(temp.high, temp.high, value.high); + break; + case AtomicFetchAndOp: + masm.as_and(temp.low, output.low, value.low); + masm.as_and(temp.high, output.high, value.high); + break; + case AtomicFetchOrOp: + masm.as_or(temp.low, output.low, value.low); + masm.as_or(temp.high, output.high, value.high); + break; + case AtomicFetchXorOp: + masm.as_xor(temp.low, output.low, value.low); + masm.as_xor(temp.high, output.high, value.high); + break; + default: + MOZ_CRASH(); + } + + masm.store64(temp, Address(SecondScratchReg, 0)); + + ExitAtomic64Region(masm, /* spinlock= */ ScratchRegister); +} + +void +MacroAssembler::atomicFetchOp64(const Synchronization&, AtomicOp op, Register64 value, + const Address& mem, Register64 temp, Register64 output) +{ + AtomicFetchOp64(*this, op, value, mem, temp, output); +} + +void +MacroAssembler::atomicFetchOp64(const Synchronization&, AtomicOp op, Register64 value, + const BaseIndex& mem, Register64 temp, Register64 output) +{ + AtomicFetchOp64(*this, op, value, mem, temp, output); +} + +// ======================================================================== +// Convert floating point. + +static const double TO_DOUBLE_HIGH_SCALE = 0x100000000; + +bool +MacroAssembler::convertUInt64ToDoubleNeedsTemp() +{ + return false; +} + +void +MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp) +{ + MOZ_ASSERT(temp == Register::Invalid()); + convertUInt32ToDouble(src.high, dest); + loadConstantDouble(TO_DOUBLE_HIGH_SCALE, ScratchDoubleReg); + mulDouble(ScratchDoubleReg, dest); + convertUInt32ToDouble(src.low, ScratchDoubleReg); + addDouble(ScratchDoubleReg, dest); +} + +//}}} check_macroassembler_style |