From 67b54962cfa42c3a977e8db43e66f17d05517bf8 Mon Sep 17 00:00:00 2001 From: Heiher Date: Thu, 10 Sep 2015 19:27:00 +0200 Subject: [PATCH] Bug 1194139 - IonMonkey: MIPS: Split shareable code to mips-shared in Assembler-mips32. r=nbp --HG-- rename : js/src/jit/mips32/Assembler-mips32.cpp => js/src/jit/mips-shared/Assembler-mips-shared.cpp rename : js/src/jit/mips32/Assembler-mips32.h => js/src/jit/mips-shared/Assembler-mips-shared.h --- .../jit/mips-shared/Assembler-mips-shared.cpp | 1230 ++++++++++++++++ .../jit/mips-shared/Assembler-mips-shared.h | 1276 +++++++++++++++++ js/src/jit/mips32/Assembler-mips32.cpp | 1230 +--------------- js/src/jit/mips32/Assembler-mips32.h | 1248 +--------------- js/src/moz.build | 1 + 5 files changed, 2529 insertions(+), 2456 deletions(-) create mode 100644 js/src/jit/mips-shared/Assembler-mips-shared.cpp create mode 100644 js/src/jit/mips-shared/Assembler-mips-shared.h diff --git a/js/src/jit/mips-shared/Assembler-mips-shared.cpp b/js/src/jit/mips-shared/Assembler-mips-shared.cpp new file mode 100644 index 000000000000..52cace504b0a --- /dev/null +++ b/js/src/jit/mips-shared/Assembler-mips-shared.cpp @@ -0,0 +1,1230 @@ +/* -*- 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/mips-shared/Assembler-mips-shared.h" + +#include "mozilla/DebugOnly.h" +#include "mozilla/MathAlgorithms.h" + +#include "jscompartment.h" +#include "jsutil.h" + +#include "gc/Marking.h" +#include "jit/ExecutableAllocator.h" +#include "jit/JitCompartment.h" + +using mozilla::DebugOnly; + +using namespace js; +using namespace js::jit; + +// Encode a standard register when it is being used as rd, the rs, and +// an extra register(rt). These should never be called with an InvalidReg. +uint32_t +js::jit::RS(Register r) +{ + MOZ_ASSERT((r.code() & ~RegMask) == 0); + return r.code() << RSShift; +} + +uint32_t +js::jit::RT(Register r) +{ + MOZ_ASSERT((r.code() & ~RegMask) == 0); + return r.code() << RTShift; +} + +uint32_t +js::jit::RD(Register r) +{ + MOZ_ASSERT((r.code() & ~RegMask) == 0); + return r.code() << RDShift; +} + +uint32_t +js::jit::SA(uint32_t value) +{ + MOZ_ASSERT(value < 32); + return value << SAShift; +} + +Register +js::jit::toRS(Instruction& i) +{ + return Register::FromCode((i.encode() & RSMask ) >> RSShift); +} + +Register +js::jit::toRT(Instruction& i) +{ + return Register::FromCode((i.encode() & RTMask ) >> RTShift); +} + +Register +js::jit::toRD(Instruction& i) +{ + return Register::FromCode((i.encode() & RDMask ) >> RDShift); +} + +Register +js::jit::toR(Instruction& i) +{ + return Register::FromCode(i.encode() & RegMask); +} + +void +InstImm::extractImm16(BOffImm16* dest) +{ + *dest = BOffImm16(*this); +} + +void +AssemblerMIPSShared::finish() +{ + MOZ_ASSERT(!isFinished); + isFinished = true; +} + +uint32_t +AssemblerMIPSShared::actualOffset(uint32_t off_) const +{ + return off_; +} + +uint32_t +AssemblerMIPSShared::actualIndex(uint32_t idx_) const +{ + return idx_; +} + +uint8_t* +AssemblerMIPSShared::PatchableJumpAddress(JitCode* code, uint32_t pe_) +{ + return code->raw() + pe_; +} + +Assembler& +AssemblerMIPSShared::asAsm() +{ + return *static_cast(this); +} + +class RelocationIterator +{ + CompactBufferReader reader_; + // offset in bytes + uint32_t offset_; + + public: + RelocationIterator(CompactBufferReader& reader) + : reader_(reader) + { } + + bool read() { + if (!reader_.more()) + return false; + offset_ = reader_.readUnsigned(); + return true; + } + + uint32_t offset() const { + return offset_; + } +}; + +void +AssemblerMIPSShared::copyJumpRelocationTable(uint8_t* dest) +{ + if (jumpRelocations_.length()) + memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length()); +} + +void +AssemblerMIPSShared::copyDataRelocationTable(uint8_t* dest) +{ + if (dataRelocations_.length()) + memcpy(dest, dataRelocations_.buffer(), dataRelocations_.length()); +} + +void +AssemblerMIPSShared::copyPreBarrierTable(uint8_t* dest) +{ + if (preBarriers_.length()) + memcpy(dest, preBarriers_.buffer(), preBarriers_.length()); +} + +void +AssemblerMIPSShared::processCodeLabels(uint8_t* rawCode) +{ + for (size_t i = 0; i < codeLabels_.length(); i++) { + CodeLabel label = codeLabels_[i]; + asAsm().Bind(rawCode, label.dest(), rawCode + actualOffset(label.src()->offset())); + } +} + +AssemblerMIPSShared::Condition +AssemblerMIPSShared::InvertCondition(Condition cond) +{ + switch (cond) { + case Equal: + return NotEqual; + case NotEqual: + return Equal; + case Zero: + return NonZero; + case NonZero: + return Zero; + case LessThan: + return GreaterThanOrEqual; + case LessThanOrEqual: + return GreaterThan; + case GreaterThan: + return LessThanOrEqual; + case GreaterThanOrEqual: + return LessThan; + case Above: + return BelowOrEqual; + case AboveOrEqual: + return Below; + case Below: + return AboveOrEqual; + case BelowOrEqual: + return Above; + case Signed: + return NotSigned; + case NotSigned: + return Signed; + default: + MOZ_CRASH("unexpected condition"); + } +} + +AssemblerMIPSShared::DoubleCondition +AssemblerMIPSShared::InvertCondition(DoubleCondition cond) +{ + switch (cond) { + case DoubleOrdered: + return DoubleUnordered; + case DoubleEqual: + return DoubleNotEqualOrUnordered; + case DoubleNotEqual: + return DoubleEqualOrUnordered; + case DoubleGreaterThan: + return DoubleLessThanOrEqualOrUnordered; + case DoubleGreaterThanOrEqual: + return DoubleLessThanOrUnordered; + case DoubleLessThan: + return DoubleGreaterThanOrEqualOrUnordered; + case DoubleLessThanOrEqual: + return DoubleGreaterThanOrUnordered; + case DoubleUnordered: + return DoubleOrdered; + case DoubleEqualOrUnordered: + return DoubleNotEqual; + case DoubleNotEqualOrUnordered: + return DoubleEqual; + case DoubleGreaterThanOrUnordered: + return DoubleLessThanOrEqual; + case DoubleGreaterThanOrEqualOrUnordered: + return DoubleLessThan; + case DoubleLessThanOrUnordered: + return DoubleGreaterThanOrEqual; + case DoubleLessThanOrEqualOrUnordered: + return DoubleGreaterThan; + default: + MOZ_CRASH("unexpected condition"); + } +} + +BOffImm16::BOffImm16(InstImm inst) + : data(inst.encode() & Imm16Mask) +{ +} + +bool +AssemblerMIPSShared::oom() const +{ + return AssemblerShared::oom() || + m_buffer.oom() || + jumpRelocations_.oom() || + dataRelocations_.oom() || + preBarriers_.oom(); +} + +void +AssemblerMIPSShared::addCodeLabel(CodeLabel label) +{ + propagateOOM(codeLabels_.append(label)); +} + +// Size of the instruction stream, in bytes. +size_t +AssemblerMIPSShared::size() const +{ + return m_buffer.size(); +} + +// Size of the relocation table, in bytes. +size_t +AssemblerMIPSShared::jumpRelocationTableBytes() const +{ + return jumpRelocations_.length(); +} + +size_t +AssemblerMIPSShared::dataRelocationTableBytes() const +{ + return dataRelocations_.length(); +} + +size_t +AssemblerMIPSShared::preBarrierTableBytes() const +{ + return preBarriers_.length(); +} + +// Size of the data table, in bytes. +size_t +AssemblerMIPSShared::bytesNeeded() const +{ + return size() + + jumpRelocationTableBytes() + + dataRelocationTableBytes() + + preBarrierTableBytes(); +} + +// write a blob of binary into the instruction stream +BufferOffset +AssemblerMIPSShared::writeInst(uint32_t x, uint32_t* dest) +{ + if (dest == nullptr) + return m_buffer.putInt(x); + + WriteInstStatic(x, dest); + return BufferOffset(); +} + +void +AssemblerMIPSShared::WriteInstStatic(uint32_t x, uint32_t* dest) +{ + MOZ_ASSERT(dest != nullptr); + *dest = x; +} + +BufferOffset +AssemblerMIPSShared::haltingAlign(int alignment) +{ + // TODO: Implement a proper halting align. + nopAlign(alignment); +} + +BufferOffset +AssemblerMIPSShared::nopAlign(int alignment) +{ + BufferOffset ret; + MOZ_ASSERT(m_buffer.isAligned(4)); + if (alignment == 8) { + if (!m_buffer.isAligned(alignment)) { + BufferOffset tmp = as_nop(); + if (!ret.assigned()) + ret = tmp; + } + } else { + MOZ_ASSERT((alignment & (alignment - 1)) == 0); + while (size() & (alignment - 1)) { + BufferOffset tmp = as_nop(); + if (!ret.assigned()) + ret = tmp; + } + } + return ret; +} + +BufferOffset +AssemblerMIPSShared::as_nop() +{ + return writeInst(op_special | ff_sll); +} + +// Logical operations. +BufferOffset +AssemblerMIPSShared::as_and(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_and).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_or(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_or).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_xor(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_xor).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_nor(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_nor).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_andi(Register rd, Register rs, int32_t j) +{ + MOZ_ASSERT(Imm16::IsInUnsignedRange(j)); + return writeInst(InstImm(op_andi, rs, rd, Imm16(j)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_ori(Register rd, Register rs, int32_t j) +{ + MOZ_ASSERT(Imm16::IsInUnsignedRange(j)); + return writeInst(InstImm(op_ori, rs, rd, Imm16(j)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_xori(Register rd, Register rs, int32_t j) +{ + MOZ_ASSERT(Imm16::IsInUnsignedRange(j)); + return writeInst(InstImm(op_xori, rs, rd, Imm16(j)).encode()); +} + +// Branch and jump instructions +BufferOffset +AssemblerMIPSShared::as_bal(BOffImm16 off) +{ + BufferOffset bo = writeInst(InstImm(op_regimm, zero, rt_bgezal, off).encode()); + return bo; +} + +BufferOffset +AssemblerMIPSShared::as_b(BOffImm16 off) +{ + BufferOffset bo = writeInst(InstImm(op_beq, zero, zero, off).encode()); + return bo; +} + +InstImm +AssemblerMIPSShared::getBranchCode(JumpOrCall jumpOrCall) +{ + if (jumpOrCall == BranchIsCall) + return InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)); + + return InstImm(op_beq, zero, zero, BOffImm16(0)); +} + +InstImm +AssemblerMIPSShared::getBranchCode(Register s, Register t, Condition c) +{ + MOZ_ASSERT(c == AssemblerMIPSShared::Equal || c == AssemblerMIPSShared::NotEqual); + return InstImm(c == AssemblerMIPSShared::Equal ? op_beq : op_bne, s, t, BOffImm16(0)); +} + +InstImm +AssemblerMIPSShared::getBranchCode(Register s, Condition c) +{ + switch (c) { + case AssemblerMIPSShared::Equal: + case AssemblerMIPSShared::Zero: + case AssemblerMIPSShared::BelowOrEqual: + return InstImm(op_beq, s, zero, BOffImm16(0)); + case AssemblerMIPSShared::NotEqual: + case AssemblerMIPSShared::NonZero: + case AssemblerMIPSShared::Above: + return InstImm(op_bne, s, zero, BOffImm16(0)); + case AssemblerMIPSShared::GreaterThan: + return InstImm(op_bgtz, s, zero, BOffImm16(0)); + case AssemblerMIPSShared::GreaterThanOrEqual: + case AssemblerMIPSShared::NotSigned: + return InstImm(op_regimm, s, rt_bgez, BOffImm16(0)); + case AssemblerMIPSShared::LessThan: + case AssemblerMIPSShared::Signed: + return InstImm(op_regimm, s, rt_bltz, BOffImm16(0)); + case AssemblerMIPSShared::LessThanOrEqual: + return InstImm(op_blez, s, zero, BOffImm16(0)); + default: + MOZ_CRASH("Condition not supported."); + } +} + +InstImm +AssemblerMIPSShared::getBranchCode(FloatTestKind testKind, FPConditionBit fcc) +{ + MOZ_ASSERT(!(fcc && FccMask)); + uint32_t rtField = ((testKind == TestForTrue ? 1 : 0) | (fcc << FccShift)) << RTShift; + + return InstImm(op_cop1, rs_bc1, rtField, BOffImm16(0)); +} + +BufferOffset +AssemblerMIPSShared::as_j(JOffImm26 off) +{ + BufferOffset bo = writeInst(InstJump(op_j, off).encode()); + return bo; +} +BufferOffset +AssemblerMIPSShared::as_jal(JOffImm26 off) +{ + BufferOffset bo = writeInst(InstJump(op_jal, off).encode()); + return bo; +} + +BufferOffset +AssemblerMIPSShared::as_jr(Register rs) +{ + BufferOffset bo = writeInst(InstReg(op_special, rs, zero, zero, ff_jr).encode()); + return bo; +} +BufferOffset +AssemblerMIPSShared::as_jalr(Register rs) +{ + BufferOffset bo = writeInst(InstReg(op_special, rs, zero, ra, ff_jalr).encode()); + return bo; +} + + +// Arithmetic instructions +BufferOffset +AssemblerMIPSShared::as_addu(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_addu).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_addiu(Register rd, Register rs, int32_t j) +{ + MOZ_ASSERT(Imm16::IsInSignedRange(j)); + return writeInst(InstImm(op_addiu, rs, rd, Imm16(j)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_subu(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_subu).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_mult(Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, ff_mult).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_multu(Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, ff_multu).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_div(Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, ff_div).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_divu(Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, ff_divu).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_mul(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special2, rs, rt, rd, ff_mul).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_lui(Register rd, int32_t j) +{ + MOZ_ASSERT(Imm16::IsInUnsignedRange(j)); + return writeInst(InstImm(op_lui, zero, rd, Imm16(j)).encode()); +} + +// Shift instructions +BufferOffset +AssemblerMIPSShared::as_sll(Register rd, Register rt, uint16_t sa) +{ + MOZ_ASSERT(sa < 32); + return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_sll).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_sllv(Register rd, Register rt, Register rs) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_sllv).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_srl(Register rd, Register rt, uint16_t sa) +{ + MOZ_ASSERT(sa < 32); + return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_srl).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_srlv(Register rd, Register rt, Register rs) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_srlv).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_sra(Register rd, Register rt, uint16_t sa) +{ + MOZ_ASSERT(sa < 32); + return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_sra).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_srav(Register rd, Register rt, Register rs) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_srav).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_rotr(Register rd, Register rt, uint16_t sa) +{ + MOZ_ASSERT(sa < 32); + return writeInst(InstReg(op_special, rs_one, rt, rd, sa, ff_srl).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_rotrv(Register rd, Register rt, Register rs) +{ + return writeInst(InstReg(op_special, rs, rt, rd, 1, ff_srlv).encode()); +} + +// Load and store instructions +BufferOffset +AssemblerMIPSShared::as_lb(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_lb, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_lbu(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_lbu, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_lh(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_lh, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_lhu(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_lhu, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_lw(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_lw, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_lwl(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_lwl, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_lwr(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_lwr, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_sb(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_sb, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_sh(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_sh, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_sw(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_sw, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_swl(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_swl, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_swr(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_swr, rs, rd, Imm16(off)).encode()); +} + +// Move from HI/LO register. +BufferOffset +AssemblerMIPSShared::as_mfhi(Register rd) +{ + return writeInst(InstReg(op_special, rd, ff_mfhi).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_mflo(Register rd) +{ + return writeInst(InstReg(op_special, rd, ff_mflo).encode()); +} + +// Set on less than. +BufferOffset +AssemblerMIPSShared::as_slt(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_slt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_sltu(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_sltu).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_slti(Register rd, Register rs, int32_t j) +{ + MOZ_ASSERT(Imm16::IsInSignedRange(j)); + return writeInst(InstImm(op_slti, rs, rd, Imm16(j)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_sltiu(Register rd, Register rs, uint32_t j) +{ + MOZ_ASSERT(Imm16::IsInUnsignedRange(j)); + return writeInst(InstImm(op_sltiu, rs, rd, Imm16(j)).encode()); +} + +// Conditional move. +BufferOffset +AssemblerMIPSShared::as_movz(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_movz).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_movn(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_movn).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_movt(Register rd, Register rs, uint16_t cc) +{ + Register rt; + rt = Register::FromCode((cc & 0x7) << 2 | 1); + return writeInst(InstReg(op_special, rs, rt, rd, ff_movci).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_movf(Register rd, Register rs, uint16_t cc) +{ + Register rt; + rt = Register::FromCode((cc & 0x7) << 2 | 0); + return writeInst(InstReg(op_special, rs, rt, rd, ff_movci).encode()); +} + +// Bit twiddling. +BufferOffset +AssemblerMIPSShared::as_clz(Register rd, Register rs) +{ + return writeInst(InstReg(op_special2, rs, rd, rd, ff_clz).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_ins(Register rt, Register rs, uint16_t pos, uint16_t size) +{ + MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 32); + Register rd; + rd = Register::FromCode(pos + size - 1); + return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ins).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_ext(Register rt, Register rs, uint16_t pos, uint16_t size) +{ + MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 32); + Register rd; + rd = Register::FromCode(size - 1); + return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ext).encode()); +} + +// FP instructions +BufferOffset +AssemblerMIPSShared::as_ld(FloatRegister fd, Register base, int32_t off) +{ + MOZ_ASSERT(Imm16::IsInSignedRange(off)); + return writeInst(InstImm(op_ldc1, base, fd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_sd(FloatRegister fd, Register base, int32_t off) +{ + MOZ_ASSERT(Imm16::IsInSignedRange(off)); + return writeInst(InstImm(op_sdc1, base, fd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_ls(FloatRegister fd, Register base, int32_t off) +{ + MOZ_ASSERT(Imm16::IsInSignedRange(off)); + return writeInst(InstImm(op_lwc1, base, fd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_ss(FloatRegister fd, Register base, int32_t off) +{ + MOZ_ASSERT(Imm16::IsInSignedRange(off)); + return writeInst(InstImm(op_swc1, base, fd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_movs(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_mov_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_movd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_mov_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_mtc1(Register rt, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_mtc1, rt, fs).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_mfc1(Register rt, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_mfc1, rt, fs).encode()); +} + +// FP convert instructions +BufferOffset +AssemblerMIPSShared::as_ceilws(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_ceil_w_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_floorws(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_floor_w_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_roundws(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_round_w_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_truncws(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_trunc_w_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_ceilwd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_ceil_w_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_floorwd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_floor_w_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_roundwd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_round_w_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_truncwd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_trunc_w_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_cvtds(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_cvt_d_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_cvtdw(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_w, zero, fs, fd, ff_cvt_d_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_cvtsd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_cvt_s_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_cvtsw(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_w, zero, fs, fd, ff_cvt_s_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_cvtwd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_cvt_w_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_cvtws(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_cvt_w_fmt).encode()); +} + +// FP arithmetic instructions +BufferOffset +AssemblerMIPSShared::as_adds(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_add_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_addd(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_add_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_subs(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_sub_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_subd(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_sub_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_abss(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_abs_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_absd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_abs_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_negs(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_neg_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_negd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_neg_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_muls(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_mul_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_muld(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_mul_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_divs(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_div_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_divd(FloatRegister fd, FloatRegister fs, FloatRegister ft) +{ + return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_div_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_sqrts(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_sqrt_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_sqrtd(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_sqrt_fmt).encode()); +} + +// FP compare instructions +BufferOffset +AssemblerMIPSShared::as_cf(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_f_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_cun(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_un_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_ceq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_eq_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_cueq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ueq_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_colt(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_olt_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_cult(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ult_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_cole(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ole_fmt).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_cule(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) +{ + RSField rs = fmt == DoubleFloat ? rs_d : rs_s; + return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ule_fmt).encode()); +} + + +void +AssemblerMIPSShared::bind(Label* label, BufferOffset boff) +{ + // If our caller didn't give us an explicit target to bind to + // then we want to bind to the location of the next instruction + BufferOffset dest = boff.assigned() ? boff : nextOffset(); + if (label->used()) { + int32_t next; + + // A used label holds a link to branch that uses it. + BufferOffset b(label); + do { + Instruction* inst = editSrc(b); + + // Second word holds a pointer to the next branch in label's chain. + next = inst[1].encode(); + asAsm().bind(reinterpret_cast(inst), b.getOffset(), dest.getOffset()); + + b = BufferOffset(next); + } while (next != LabelBase::INVALID_OFFSET); + } + label->bind(dest.getOffset()); +} + +void +AssemblerMIPSShared::retarget(Label* label, Label* target) +{ + if (label->used()) { + if (target->bound()) { + bind(label, BufferOffset(target)); + } else if (target->used()) { + // The target is not bound but used. Prepend label's branch list + // onto target's. + int32_t next; + BufferOffset labelBranchOffset(label); + + // Find the head of the use chain for label. + do { + Instruction* inst = editSrc(labelBranchOffset); + + // Second word holds a pointer to the next branch in chain. + next = inst[1].encode(); + labelBranchOffset = BufferOffset(next); + } while (next != LabelBase::INVALID_OFFSET); + + // Then patch the head of label's use chain to the tail of + // target's use chain, prepending the entire use chain of target. + Instruction* inst = editSrc(labelBranchOffset); + int32_t prev = target->use(label->offset()); + inst[1].setData(prev); + } else { + // The target is unbound and unused. We can just take the head of + // the list hanging off of label, and dump that into target. + DebugOnly prev = target->use(label->offset()); + MOZ_ASSERT((int32_t)prev == Label::INVALID_OFFSET); + } + } + label->reset(); +} + +void dbg_break() {} +void +AssemblerMIPSShared::as_break(uint32_t code) +{ + MOZ_ASSERT(code <= MAX_BREAK_CODE); + writeInst(op_special | code << FunctionBits | ff_break); +} + +void +AssemblerMIPSShared::PatchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, ImmPtr expectedValue) +{ + Assembler::PatchDataWithValueCheck(label, PatchedImmPtr(newValue.value), + PatchedImmPtr(expectedValue.value)); +} + +// This just stomps over memory with 32 bits of raw data. Its purpose is to +// overwrite the call of JITed code with 32 bits worth of an offset. This will +// is only meant to function on code that has been invalidated, so it should +// be totally safe. Since that instruction will never be executed again, a +// ICache flush should not be necessary +void +AssemblerMIPSShared::PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm) +{ + // Raw is going to be the return address. + uint32_t* raw = (uint32_t*)label.raw(); + // Overwrite the 4 bytes before the return address, which will + // end up being the call instruction. + *(raw - 1) = imm.value; +} + +uint8_t* +AssemblerMIPSShared::NextInstruction(uint8_t* inst_, uint32_t* count) +{ + Instruction* inst = reinterpret_cast(inst_); + if (count != nullptr) + *count += sizeof(Instruction); + return reinterpret_cast(inst->next()); +} + +// Since there are no pools in MIPS implementation, this should be simple. +Instruction* +Instruction::next() +{ + return this + 1; +} + +InstImm AssemblerMIPSShared::invertBranch(InstImm branch, BOffImm16 skipOffset) +{ + uint32_t rt = 0; + Opcode op = (Opcode) (branch.extractOpcode() << OpcodeShift); + switch(op) { + case op_beq: + branch.setBOffImm16(skipOffset); + branch.setOpcode(op_bne); + return branch; + case op_bne: + branch.setBOffImm16(skipOffset); + branch.setOpcode(op_beq); + return branch; + case op_bgtz: + branch.setBOffImm16(skipOffset); + branch.setOpcode(op_blez); + return branch; + case op_blez: + branch.setBOffImm16(skipOffset); + branch.setOpcode(op_bgtz); + return branch; + case op_regimm: + branch.setBOffImm16(skipOffset); + rt = branch.extractRT(); + if (rt == (rt_bltz >> RTShift)) { + branch.setRT(rt_bgez); + return branch; + } + if (rt == (rt_bgez >> RTShift)) { + branch.setRT(rt_bltz); + return branch; + } + + MOZ_CRASH("Error creating long branch."); + + case op_cop1: + MOZ_ASSERT(branch.extractRS() == rs_bc1 >> RSShift); + + branch.setBOffImm16(skipOffset); + rt = branch.extractRT(); + if (rt & 0x1) + branch.setRT((RTField) ((rt & ~0x1) << RTShift)); + else + branch.setRT((RTField) ((rt | 0x1) << RTShift)); + return branch; + default: + MOZ_CRASH("Error creating long branch."); + } +} + +void +AssemblerMIPSShared::ToggleToJmp(CodeLocationLabel inst_) +{ + InstImm * inst = (InstImm*)inst_.raw(); + + MOZ_ASSERT(inst->extractOpcode() == ((uint32_t)op_andi >> OpcodeShift)); + // We converted beq to andi, so now we restore it. + inst->setOpcode(op_beq); + + AutoFlushICache::flush(uintptr_t(inst), 4); +} + +void +AssemblerMIPSShared::ToggleToCmp(CodeLocationLabel inst_) +{ + InstImm * inst = (InstImm*)inst_.raw(); + + // toggledJump is allways used for short jumps. + MOZ_ASSERT(inst->extractOpcode() == ((uint32_t)op_beq >> OpcodeShift)); + // Replace "beq $zero, $zero, offset" with "andi $zero, $zero, offset" + inst->setOpcode(op_andi); + + AutoFlushICache::flush(uintptr_t(inst), 4); +} + diff --git a/js/src/jit/mips-shared/Assembler-mips-shared.h b/js/src/jit/mips-shared/Assembler-mips-shared.h new file mode 100644 index 000000000000..17b7c8de0c38 --- /dev/null +++ b/js/src/jit/mips-shared/Assembler-mips-shared.h @@ -0,0 +1,1276 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_shared_Assembler_mips_shared_h +#define jit_mips_shared_Assembler_mips_shared_h + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Attributes.h" +#include "mozilla/MathAlgorithms.h" + +#include "jit/CompactBuffer.h" +#include "jit/IonCode.h" +#include "jit/JitCompartment.h" +#include "jit/JitSpewer.h" +#include "jit/mips-shared/Architecture-mips-shared.h" +#include "jit/shared/Assembler-shared.h" +#include "jit/shared/IonAssemblerBuffer.h" + +namespace js { +namespace jit { + +static MOZ_CONSTEXPR_VAR Register zero = { Registers::zero }; +static MOZ_CONSTEXPR_VAR Register at = { Registers::at }; +static MOZ_CONSTEXPR_VAR Register v0 = { Registers::v0 }; +static MOZ_CONSTEXPR_VAR Register v1 = { Registers::v1 }; +static MOZ_CONSTEXPR_VAR Register a0 = { Registers::a0 }; +static MOZ_CONSTEXPR_VAR Register a1 = { Registers::a1 }; +static MOZ_CONSTEXPR_VAR Register a2 = { Registers::a2 }; +static MOZ_CONSTEXPR_VAR Register a3 = { Registers::a3 }; +static MOZ_CONSTEXPR_VAR Register t0 = { Registers::t0 }; +static MOZ_CONSTEXPR_VAR Register t1 = { Registers::t1 }; +static MOZ_CONSTEXPR_VAR Register t2 = { Registers::t2 }; +static MOZ_CONSTEXPR_VAR Register t3 = { Registers::t3 }; +static MOZ_CONSTEXPR_VAR Register t4 = { Registers::t4 }; +static MOZ_CONSTEXPR_VAR Register t5 = { Registers::t5 }; +static MOZ_CONSTEXPR_VAR Register t6 = { Registers::t6 }; +static MOZ_CONSTEXPR_VAR Register t7 = { Registers::t7 }; +static MOZ_CONSTEXPR_VAR Register s0 = { Registers::s0 }; +static MOZ_CONSTEXPR_VAR Register s1 = { Registers::s1 }; +static MOZ_CONSTEXPR_VAR Register s2 = { Registers::s2 }; +static MOZ_CONSTEXPR_VAR Register s3 = { Registers::s3 }; +static MOZ_CONSTEXPR_VAR Register s4 = { Registers::s4 }; +static MOZ_CONSTEXPR_VAR Register s5 = { Registers::s5 }; +static MOZ_CONSTEXPR_VAR Register s6 = { Registers::s6 }; +static MOZ_CONSTEXPR_VAR Register s7 = { Registers::s7 }; +static MOZ_CONSTEXPR_VAR Register t8 = { Registers::t8 }; +static MOZ_CONSTEXPR_VAR Register t9 = { Registers::t9 }; +static MOZ_CONSTEXPR_VAR Register k0 = { Registers::k0 }; +static MOZ_CONSTEXPR_VAR Register k1 = { Registers::k1 }; +static MOZ_CONSTEXPR_VAR Register gp = { Registers::gp }; +static MOZ_CONSTEXPR_VAR Register sp = { Registers::sp }; +static MOZ_CONSTEXPR_VAR Register fp = { Registers::fp }; +static MOZ_CONSTEXPR_VAR Register ra = { Registers::ra }; + +static MOZ_CONSTEXPR_VAR Register ScratchRegister = at; +static MOZ_CONSTEXPR_VAR Register SecondScratchReg = t8; + +// Helper classes for ScratchRegister usage. Asserts that only one piece +// of code thinks it has exclusive ownership of each scratch register. +struct ScratchRegisterScope : public AutoRegisterScope +{ + explicit ScratchRegisterScope(MacroAssembler& masm) + : AutoRegisterScope(masm, ScratchRegister) + { } +}; +struct SecondScratchRegisterScope : public AutoRegisterScope +{ + explicit SecondScratchRegisterScope(MacroAssembler& masm) + : AutoRegisterScope(masm, SecondScratchReg) + { } +}; + +// Use arg reg from EnterJIT function as OsrFrameReg. +static MOZ_CONSTEXPR_VAR Register OsrFrameReg = a3; +static MOZ_CONSTEXPR_VAR Register ArgumentsRectifierReg = s3; +static MOZ_CONSTEXPR_VAR Register CallTempReg0 = t0; +static MOZ_CONSTEXPR_VAR Register CallTempReg1 = t1; +static MOZ_CONSTEXPR_VAR Register CallTempReg2 = t2; +static MOZ_CONSTEXPR_VAR Register CallTempReg3 = t3; + +static MOZ_CONSTEXPR_VAR Register IntArgReg0 = a0; +static MOZ_CONSTEXPR_VAR Register IntArgReg1 = a1; +static MOZ_CONSTEXPR_VAR Register IntArgReg2 = a2; +static MOZ_CONSTEXPR_VAR Register IntArgReg3 = a3; +static MOZ_CONSTEXPR_VAR Register GlobalReg = s6; // used by Odin +static MOZ_CONSTEXPR_VAR Register HeapReg = s7; // used by Odin +static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = CALL_TEMP_NON_ARG_REGS; +static const uint32_t NumCallTempNonArgRegs = mozilla::ArrayLength(CallTempNonArgRegs); + +static MOZ_CONSTEXPR_VAR Register PreBarrierReg = a1; + +static MOZ_CONSTEXPR_VAR Register InvalidReg = { Registers::invalid_reg }; +static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg; + +static MOZ_CONSTEXPR_VAR Register StackPointer = sp; +static MOZ_CONSTEXPR_VAR Register FramePointer = InvalidReg; +static MOZ_CONSTEXPR_VAR Register ReturnReg = v0; +static MOZ_CONSTEXPR_VAR FloatRegister ReturnInt32x4Reg = InvalidFloatReg; +static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32x4Reg = InvalidFloatReg; +static MOZ_CONSTEXPR_VAR FloatRegister ScratchSimdReg = InvalidFloatReg; + +// A bias applied to the GlobalReg to allow the use of instructions with small +// negative immediate offsets which doubles the range of global data that can be +// accessed with a single instruction. +static const int32_t AsmJSGlobalRegBias = 32768; + +// Registers used in the GenerateFFIIonExit Enable Activation block. +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = t0; +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = a0; +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = a1; +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = a2; +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = a3; + +// Registers used in the GenerateFFIIonExit Disable Activation block. +// None of these may be the second scratch register (t8). +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = a0; +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = a1; +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = t0; + +static MOZ_CONSTEXPR_VAR uint32_t CodeAlignment = 4; + +// This boolean indicates whether we support SIMD instructions flavoured for +// this architecture or not. Rather than a method in the LIRGenerator, it is +// here such that it is accessible from the entire codebase. Once full support +// for SIMD is reached on all tier-1 platforms, this constant can be deleted. +static MOZ_CONSTEXPR_VAR bool SupportsSimd = false; + +// MIPS instruction types +// +---------------------------------------------------------------+ +// | 6 | 5 | 5 | 5 | 5 | 6 | +// +---------------------------------------------------------------+ +// Register type | Opcode | Rs | Rt | Rd | Sa | Function | +// +---------------------------------------------------------------+ +// | 6 | 5 | 5 | 16 | +// +---------------------------------------------------------------+ +// Immediate type | Opcode | Rs | Rt | 2's complement constant | +// +---------------------------------------------------------------+ +// | 6 | 26 | +// +---------------------------------------------------------------+ +// Jump type | Opcode | jump_target | +// +---------------------------------------------------------------+ +// 31 bit bit 0 + +// MIPS instruction encoding constants. +static const uint32_t OpcodeShift = 26; +static const uint32_t OpcodeBits = 6; +static const uint32_t RSShift = 21; +static const uint32_t RSBits = 5; +static const uint32_t RTShift = 16; +static const uint32_t RTBits = 5; +static const uint32_t RDShift = 11; +static const uint32_t RDBits = 5; +static const uint32_t SAShift = 6; +static const uint32_t SABits = 5; +static const uint32_t FunctionShift = 0; +static const uint32_t FunctionBits = 6; +static const uint32_t Imm16Shift = 0; +static const uint32_t Imm16Bits = 16; +static const uint32_t Imm26Shift = 0; +static const uint32_t Imm26Bits = 26; +static const uint32_t Imm28Shift = 0; +static const uint32_t Imm28Bits = 28; +static const uint32_t ImmFieldShift = 2; +static const uint32_t FRBits = 5; +static const uint32_t FRShift = 21; +static const uint32_t FSShift = 11; +static const uint32_t FSBits = 5; +static const uint32_t FTShift = 16; +static const uint32_t FTBits = 5; +static const uint32_t FDShift = 6; +static const uint32_t FDBits = 5; +static const uint32_t FCccShift = 8; +static const uint32_t FCccBits = 3; +static const uint32_t FBccShift = 18; +static const uint32_t FBccBits = 3; +static const uint32_t FBtrueShift = 16; +static const uint32_t FBtrueBits = 1; +static const uint32_t FccMask = 0x7; +static const uint32_t FccShift = 2; + + +// MIPS instruction field bit masks. +static const uint32_t OpcodeMask = ((1 << OpcodeBits) - 1) << OpcodeShift; +static const uint32_t Imm16Mask = ((1 << Imm16Bits) - 1) << Imm16Shift; +static const uint32_t Imm26Mask = ((1 << Imm26Bits) - 1) << Imm26Shift; +static const uint32_t Imm28Mask = ((1 << Imm28Bits) - 1) << Imm28Shift; +static const uint32_t RSMask = ((1 << RSBits) - 1) << RSShift; +static const uint32_t RTMask = ((1 << RTBits) - 1) << RTShift; +static const uint32_t RDMask = ((1 << RDBits) - 1) << RDShift; +static const uint32_t SAMask = ((1 << SABits) - 1) << SAShift; +static const uint32_t FunctionMask = ((1 << FunctionBits) - 1) << FunctionShift; +static const uint32_t RegMask = Registers::Total - 1; + +static const uint32_t BREAK_STACK_UNALIGNED = 1; +static const uint32_t MAX_BREAK_CODE = 1024 - 1; + +class Instruction; +class InstReg; +class InstImm; +class InstJump; + +uint32_t RS(Register r); +uint32_t RT(Register r); +uint32_t RT(uint32_t regCode); +uint32_t RT(FloatRegister r); +uint32_t RD(Register r); +uint32_t RD(FloatRegister r); +uint32_t RD(uint32_t regCode); +uint32_t SA(uint32_t value); +uint32_t SA(FloatRegister r); + +Register toRS (Instruction& i); +Register toRT (Instruction& i); +Register toRD (Instruction& i); +Register toR (Instruction& i); + +// MIPS enums for instruction fields +enum Opcode { + op_special = 0 << OpcodeShift, + op_regimm = 1 << OpcodeShift, + + op_j = 2 << OpcodeShift, + op_jal = 3 << OpcodeShift, + op_beq = 4 << OpcodeShift, + op_bne = 5 << OpcodeShift, + op_blez = 6 << OpcodeShift, + op_bgtz = 7 << OpcodeShift, + + op_addi = 8 << OpcodeShift, + op_addiu = 9 << OpcodeShift, + op_slti = 10 << OpcodeShift, + op_sltiu = 11 << OpcodeShift, + op_andi = 12 << OpcodeShift, + op_ori = 13 << OpcodeShift, + op_xori = 14 << OpcodeShift, + op_lui = 15 << OpcodeShift, + + op_cop1 = 17 << OpcodeShift, + op_cop1x = 19 << OpcodeShift, + + op_beql = 20 << OpcodeShift, + op_bnel = 21 << OpcodeShift, + op_blezl = 22 << OpcodeShift, + op_bgtzl = 23 << OpcodeShift, + + op_special2 = 28 << OpcodeShift, + op_special3 = 31 << OpcodeShift, + + op_lb = 32 << OpcodeShift, + op_lh = 33 << OpcodeShift, + op_lwl = 34 << OpcodeShift, + op_lw = 35 << OpcodeShift, + op_lbu = 36 << OpcodeShift, + op_lhu = 37 << OpcodeShift, + op_lwr = 38 << OpcodeShift, + op_sb = 40 << OpcodeShift, + op_sh = 41 << OpcodeShift, + op_swl = 42 << OpcodeShift, + op_sw = 43 << OpcodeShift, + op_swr = 46 << OpcodeShift, + + op_lwc1 = 49 << OpcodeShift, + op_ldc1 = 53 << OpcodeShift, + + op_swc1 = 57 << OpcodeShift, + op_sdc1 = 61 << OpcodeShift +}; + +enum RSField { + rs_zero = 0 << RSShift, + // cop1 encoding of RS field. + rs_mfc1 = 0 << RSShift, + rs_one = 1 << RSShift, + rs_cfc1 = 2 << RSShift, + rs_mfhc1 = 3 << RSShift, + rs_mtc1 = 4 << RSShift, + rs_ctc1 = 6 << RSShift, + rs_mthc1 = 7 << RSShift, + rs_bc1 = 8 << RSShift, + rs_s = 16 << RSShift, + rs_d = 17 << RSShift, + rs_w = 20 << RSShift, + rs_l = 21 << RSShift, + rs_ps = 22 << RSShift +}; + +enum RTField { + rt_zero = 0 << RTShift, + // regimm encoding of RT field. + rt_bltz = 0 << RTShift, + rt_bgez = 1 << RTShift, + rt_bltzal = 16 << RTShift, + rt_bgezal = 17 << RTShift +}; + +enum FunctionField { + // special encoding of function field. + ff_sll = 0, + ff_movci = 1, + ff_srl = 2, + ff_sra = 3, + ff_sllv = 4, + ff_srlv = 6, + ff_srav = 7, + + ff_jr = 8, + ff_jalr = 9, + ff_movz = 10, + ff_movn = 11, + ff_break = 13, + + ff_mfhi = 16, + ff_mflo = 18, + + ff_mult = 24, + ff_multu = 25, + ff_div = 26, + ff_divu = 27, + + ff_add = 32, + ff_addu = 33, + ff_sub = 34, + ff_subu = 35, + ff_and = 36, + ff_or = 37, + ff_xor = 38, + ff_nor = 39, + + ff_slt = 42, + ff_sltu = 43, + + ff_tge = 48, + ff_tgeu = 49, + ff_tlt = 50, + ff_tltu = 51, + ff_teq = 52, + ff_tne = 54, + + // special2 encoding of function field. + ff_mul = 2, + ff_clz = 32, + ff_clo = 33, + + // special3 encoding of function field. + ff_ext = 0, + ff_ins = 4, + + // cop1 encoding of function field. + ff_add_fmt = 0, + ff_sub_fmt = 1, + ff_mul_fmt = 2, + ff_div_fmt = 3, + ff_sqrt_fmt = 4, + ff_abs_fmt = 5, + ff_mov_fmt = 6, + ff_neg_fmt = 7, + + ff_round_l_fmt = 8, + ff_trunc_l_fmt = 9, + ff_ceil_l_fmt = 10, + ff_floor_l_fmt = 11, + + ff_round_w_fmt = 12, + ff_trunc_w_fmt = 13, + ff_ceil_w_fmt = 14, + ff_floor_w_fmt = 15, + + ff_cvt_s_fmt = 32, + ff_cvt_d_fmt = 33, + ff_cvt_w_fmt = 36, + ff_cvt_l_fmt = 37, + ff_cvt_ps_s = 38, + + ff_c_f_fmt = 48, + ff_c_un_fmt = 49, + ff_c_eq_fmt = 50, + ff_c_ueq_fmt = 51, + ff_c_olt_fmt = 52, + ff_c_ult_fmt = 53, + ff_c_ole_fmt = 54, + ff_c_ule_fmt = 55, + + ff_madd_s = 32, + ff_madd_d = 33, + + ff_null = 0 +}; + +class Operand; + +// A BOffImm16 is a 16 bit immediate that is used for branches. +class BOffImm16 +{ + uint32_t data; + + public: + uint32_t encode() { + MOZ_ASSERT(!isInvalid()); + return data; + } + int32_t decode() { + MOZ_ASSERT(!isInvalid()); + return (int32_t(data << 18) >> 16) + 4; + } + + explicit BOffImm16(int offset) + : data ((offset - 4) >> 2 & Imm16Mask) + { + MOZ_ASSERT((offset & 0x3) == 0); + MOZ_ASSERT(IsInRange(offset)); + } + static bool IsInRange(int offset) { + if ((offset - 4) < (INT16_MIN << 2)) + return false; + if ((offset - 4) > (INT16_MAX << 2)) + return false; + return true; + } + static const uint32_t INVALID = 0x00020000; + BOffImm16() + : data(INVALID) + { } + + bool isInvalid() { + return data == INVALID; + } + Instruction* getDest(Instruction* src); + + BOffImm16(InstImm inst); +}; + +// A JOffImm26 is a 26 bit immediate that is used for unconditional jumps. +class JOffImm26 +{ + uint32_t data; + + public: + uint32_t encode() { + MOZ_ASSERT(!isInvalid()); + return data; + } + int32_t decode() { + MOZ_ASSERT(!isInvalid()); + return (int32_t(data << 8) >> 6) + 4; + } + + explicit JOffImm26(int offset) + : data ((offset - 4) >> 2 & Imm26Mask) + { + MOZ_ASSERT((offset & 0x3) == 0); + MOZ_ASSERT(IsInRange(offset)); + } + static bool IsInRange(int offset) { + if ((offset - 4) < -536870912) + return false; + if ((offset - 4) > 536870908) + return false; + return true; + } + static const uint32_t INVALID = 0x20000000; + JOffImm26() + : data(INVALID) + { } + + bool isInvalid() { + return data == INVALID; + } + Instruction* getDest(Instruction* src); + +}; + +class Imm16 +{ + uint16_t value; + + public: + Imm16(); + Imm16(uint32_t imm) + : value(imm) + { } + uint32_t encode() { + return value; + } + int32_t decodeSigned() { + return value; + } + uint32_t decodeUnsigned() { + return value; + } + static bool IsInSignedRange(int32_t imm) { + return imm >= INT16_MIN && imm <= INT16_MAX; + } + static bool IsInUnsignedRange(uint32_t imm) { + return imm <= UINT16_MAX ; + } + static Imm16 Lower (Imm32 imm) { + return Imm16(imm.value & 0xffff); + } + static Imm16 Upper (Imm32 imm) { + return Imm16((imm.value >> 16) & 0xffff); + } +}; + +class Operand +{ + public: + enum Tag { + REG, + FREG, + MEM + }; + + private: + Tag tag : 3; + uint32_t reg : 5; + int32_t offset; + + public: + Operand (Register reg_) + : tag(REG), reg(reg_.code()) + { } + + Operand (FloatRegister freg) + : tag(FREG), reg(freg.code()) + { } + + Operand (Register base, Imm32 off) + : tag(MEM), reg(base.code()), offset(off.value) + { } + + Operand (Register base, int32_t off) + : tag(MEM), reg(base.code()), offset(off) + { } + + Operand (const Address& addr) + : tag(MEM), reg(addr.base.code()), offset(addr.offset) + { } + + Tag getTag() const { + return tag; + } + + Register toReg() const { + MOZ_ASSERT(tag == REG); + return Register::FromCode(reg); + } + + FloatRegister toFReg() const { + MOZ_ASSERT(tag == FREG); + return FloatRegister::FromCode(reg); + } + + void toAddr(Register* r, Imm32* dest) const { + MOZ_ASSERT(tag == MEM); + *r = Register::FromCode(reg); + *dest = Imm32(offset); + } + Address toAddress() const { + MOZ_ASSERT(tag == MEM); + return Address(Register::FromCode(reg), offset); + } + int32_t disp() const { + MOZ_ASSERT(tag == MEM); + return offset; + } + + int32_t base() const { + MOZ_ASSERT(tag == MEM); + return reg; + } + Register baseReg() const { + MOZ_ASSERT(tag == MEM); + return Register::FromCode(reg); + } +}; + +void +PatchJump(CodeLocationJump& jump_, CodeLocationLabel label, + ReprotectCode reprotect = DontReprotect); + +void +PatchBackedge(CodeLocationJump& jump_, CodeLocationLabel label, JitRuntime::BackedgeTarget target); + +typedef js::jit::AssemblerBuffer<1024, Instruction> MIPSBuffer; + +class MIPSBufferWithExecutableCopy : public MIPSBuffer +{ + public: + void executableCopy(uint8_t* buffer) { + if (this->oom()) + return; + + for (Slice* cur = head; cur != nullptr; cur = cur->getNext()) { + memcpy(buffer, &cur->instructions, cur->length()); + buffer += cur->length(); + } + } +}; + +class Assembler; + +class AssemblerMIPSShared : public AssemblerShared +{ + public: + + enum Condition { + Equal, + NotEqual, + Above, + AboveOrEqual, + Below, + BelowOrEqual, + GreaterThan, + GreaterThanOrEqual, + LessThan, + LessThanOrEqual, + Overflow, + Signed, + NotSigned, + Zero, + NonZero, + Always, + }; + + enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. + DoubleOrdered, + DoubleEqual, + DoubleNotEqual, + DoubleGreaterThan, + DoubleGreaterThanOrEqual, + DoubleLessThan, + DoubleLessThanOrEqual, + // If either operand is NaN, these conditions always evaluate to true. + DoubleUnordered, + DoubleEqualOrUnordered, + DoubleNotEqualOrUnordered, + DoubleGreaterThanOrUnordered, + DoubleGreaterThanOrEqualOrUnordered, + DoubleLessThanOrUnordered, + DoubleLessThanOrEqualOrUnordered + }; + + enum FPConditionBit { + FCC0 = 0, + FCC1, + FCC2, + FCC3, + FCC4, + FCC5, + FCC6, + FCC7 + }; + + enum FloatFormat { + SingleFloat, + DoubleFloat + }; + + enum JumpOrCall { + BranchIsJump, + BranchIsCall + }; + + enum FloatTestKind { + TestForTrue, + TestForFalse + }; + + // :( this should be protected, but since CodeGenerator + // wants to use it, It needs to go out here :( + + BufferOffset nextOffset() { + return m_buffer.nextOffset(); + } + + protected: + Instruction * editSrc (BufferOffset bo) { + return m_buffer.getInst(bo); + } + public: + uint32_t actualOffset(uint32_t) const; + uint32_t actualIndex(uint32_t) const; + static uint8_t* PatchableJumpAddress(JitCode* code, uint32_t index); + protected: + Assembler& asAsm(); + + // structure for fixing up pc-relative loads/jumps when a the machine code + // gets moved (executable copy, gc, etc.) + struct RelativePatch + { + // the offset within the code buffer where the value is loaded that + // we want to fix-up + BufferOffset offset; + void* target; + Relocation::Kind kind; + + RelativePatch(BufferOffset offset, void* target, Relocation::Kind kind) + : offset(offset), + target(target), + kind(kind) + { } + }; + + js::Vector codeLabels_; + js::Vector jumps_; + js::Vector longJumps_; + + CompactBufferWriter jumpRelocations_; + CompactBufferWriter dataRelocations_; + CompactBufferWriter preBarriers_; + + MIPSBufferWithExecutableCopy m_buffer; + + public: + AssemblerMIPSShared() + : m_buffer(), + isFinished(false) + { } + + static Condition InvertCondition(Condition cond); + static DoubleCondition InvertCondition(DoubleCondition cond); + + void writeRelocation(BufferOffset src) { + jumpRelocations_.writeUnsigned(src.getOffset()); + } + + // As opposed to x86/x64 version, the data relocation has to be executed + // before to recover the pointer, and not after. + void writeDataRelocation(ImmGCPtr ptr) { + if (ptr.value) { + if (gc::IsInsideNursery(ptr.value)) + embedsNurseryPointers_ = true; + dataRelocations_.writeUnsigned(nextOffset().getOffset()); + } + } + void writePrebarrierOffset(CodeOffsetLabel label) { + preBarriers_.writeUnsigned(label.offset()); + } + + public: + bool oom() const; + + void setPrinter(Sprinter* sp) { + } + + static const Register getStackPointer() { + return StackPointer; + } + + protected: + bool isFinished; + public: + void finish(); + void executableCopy(void* buffer); + void copyJumpRelocationTable(uint8_t* dest); + void copyDataRelocationTable(uint8_t* dest); + void copyPreBarrierTable(uint8_t* dest); + + void addCodeLabel(CodeLabel label); + size_t numCodeLabels() const { + return codeLabels_.length(); + } + CodeLabel codeLabel(size_t i) { + return codeLabels_[i]; + } + + // Size of the instruction stream, in bytes. + size_t size() const; + // Size of the jump relocation table, in bytes. + size_t jumpRelocationTableBytes() const; + size_t dataRelocationTableBytes() const; + size_t preBarrierTableBytes() const; + + // Size of the data table, in bytes. + size_t bytesNeeded() const; + + // Write a blob of binary into the instruction stream *OR* + // into a destination address. If dest is nullptr (the default), then the + // instruction gets written into the instruction stream. If dest is not null + // it is interpreted as a pointer to the location that we want the + // instruction to be written. + BufferOffset writeInst(uint32_t x, uint32_t* dest = nullptr); + // A static variant for the cases where we don't want to have an assembler + // object at all. Normally, you would use the dummy (nullptr) object. + static void WriteInstStatic(uint32_t x, uint32_t* dest); + + public: + BufferOffset haltingAlign(int alignment); + BufferOffset nopAlign(int alignment); + BufferOffset as_nop(); + + // Branch and jump instructions + BufferOffset as_bal(BOffImm16 off); + BufferOffset as_b(BOffImm16 off); + + InstImm getBranchCode(JumpOrCall jumpOrCall); + InstImm getBranchCode(Register s, Register t, Condition c); + InstImm getBranchCode(Register s, Condition c); + InstImm getBranchCode(FloatTestKind testKind, FPConditionBit fcc); + + BufferOffset as_j(JOffImm26 off); + BufferOffset as_jal(JOffImm26 off); + + BufferOffset as_jr(Register rs); + BufferOffset as_jalr(Register rs); + + // Arithmetic instructions + BufferOffset as_addu(Register rd, Register rs, Register rt); + BufferOffset as_addiu(Register rd, Register rs, int32_t j); + BufferOffset as_subu(Register rd, Register rs, Register rt); + BufferOffset as_mult(Register rs, Register rt); + BufferOffset as_multu(Register rs, Register rt); + BufferOffset as_div(Register rs, Register rt); + BufferOffset as_divu(Register rs, Register rt); + BufferOffset as_mul(Register rd, Register rs, Register rt); + + // Logical instructions + BufferOffset as_and(Register rd, Register rs, Register rt); + BufferOffset as_or(Register rd, Register rs, Register rt); + BufferOffset as_xor(Register rd, Register rs, Register rt); + BufferOffset as_nor(Register rd, Register rs, Register rt); + + BufferOffset as_andi(Register rd, Register rs, int32_t j); + BufferOffset as_ori(Register rd, Register rs, int32_t j); + BufferOffset as_xori(Register rd, Register rs, int32_t j); + BufferOffset as_lui(Register rd, int32_t j); + + // Shift instructions + // as_sll(zero, zero, x) instructions are reserved as nop + BufferOffset as_sll(Register rd, Register rt, uint16_t sa); + BufferOffset as_sllv(Register rd, Register rt, Register rs); + BufferOffset as_srl(Register rd, Register rt, uint16_t sa); + BufferOffset as_srlv(Register rd, Register rt, Register rs); + BufferOffset as_sra(Register rd, Register rt, uint16_t sa); + BufferOffset as_srav(Register rd, Register rt, Register rs); + BufferOffset as_rotr(Register rd, Register rt, uint16_t sa); + BufferOffset as_rotrv(Register rd, Register rt, Register rs); + + // Load and store instructions + BufferOffset as_lb(Register rd, Register rs, int16_t off); + BufferOffset as_lbu(Register rd, Register rs, int16_t off); + BufferOffset as_lh(Register rd, Register rs, int16_t off); + BufferOffset as_lhu(Register rd, Register rs, int16_t off); + BufferOffset as_lw(Register rd, Register rs, int16_t off); + BufferOffset as_lwl(Register rd, Register rs, int16_t off); + BufferOffset as_lwr(Register rd, Register rs, int16_t off); + BufferOffset as_sb(Register rd, Register rs, int16_t off); + BufferOffset as_sh(Register rd, Register rs, int16_t off); + BufferOffset as_sw(Register rd, Register rs, int16_t off); + BufferOffset as_swl(Register rd, Register rs, int16_t off); + BufferOffset as_swr(Register rd, Register rs, int16_t off); + + // Move from HI/LO register. + BufferOffset as_mfhi(Register rd); + BufferOffset as_mflo(Register rd); + + // Set on less than. + BufferOffset as_slt(Register rd, Register rs, Register rt); + BufferOffset as_sltu(Register rd, Register rs, Register rt); + BufferOffset as_slti(Register rd, Register rs, int32_t j); + BufferOffset as_sltiu(Register rd, Register rs, uint32_t j); + + // Conditional move. + BufferOffset as_movz(Register rd, Register rs, Register rt); + BufferOffset as_movn(Register rd, Register rs, Register rt); + BufferOffset as_movt(Register rd, Register rs, uint16_t cc = 0); + BufferOffset as_movf(Register rd, Register rs, uint16_t cc = 0); + + // Bit twiddling. + BufferOffset as_clz(Register rd, Register rs); + BufferOffset as_ins(Register rt, Register rs, uint16_t pos, uint16_t size); + BufferOffset as_ext(Register rt, Register rs, uint16_t pos, uint16_t size); + + // FP instructions + + // Use these two functions only when you are sure address is aligned. + // Otherwise, use ma_ld and ma_sd. + BufferOffset as_ld(FloatRegister fd, Register base, int32_t off); + BufferOffset as_sd(FloatRegister fd, Register base, int32_t off); + + BufferOffset as_ls(FloatRegister fd, Register base, int32_t off); + BufferOffset as_ss(FloatRegister fd, Register base, int32_t off); + + BufferOffset as_movs(FloatRegister fd, FloatRegister fs); + BufferOffset as_movd(FloatRegister fd, FloatRegister fs); + + BufferOffset as_mtc1(Register rt, FloatRegister fs); + BufferOffset as_mfc1(Register rt, FloatRegister fs); + + + public: + // FP convert instructions + BufferOffset as_ceilws(FloatRegister fd, FloatRegister fs); + BufferOffset as_floorws(FloatRegister fd, FloatRegister fs); + BufferOffset as_roundws(FloatRegister fd, FloatRegister fs); + BufferOffset as_truncws(FloatRegister fd, FloatRegister fs); + + BufferOffset as_ceilwd(FloatRegister fd, FloatRegister fs); + BufferOffset as_floorwd(FloatRegister fd, FloatRegister fs); + BufferOffset as_roundwd(FloatRegister fd, FloatRegister fs); + BufferOffset as_truncwd(FloatRegister fd, FloatRegister fs); + + BufferOffset as_cvtdl(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtds(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtdw(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtld(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtls(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtsd(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtsl(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtsw(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtwd(FloatRegister fd, FloatRegister fs); + BufferOffset as_cvtws(FloatRegister fd, FloatRegister fs); + + // FP arithmetic instructions + BufferOffset as_adds(FloatRegister fd, FloatRegister fs, FloatRegister ft); + BufferOffset as_addd(FloatRegister fd, FloatRegister fs, FloatRegister ft); + BufferOffset as_subs(FloatRegister fd, FloatRegister fs, FloatRegister ft); + BufferOffset as_subd(FloatRegister fd, FloatRegister fs, FloatRegister ft); + + BufferOffset as_abss(FloatRegister fd, FloatRegister fs); + BufferOffset as_absd(FloatRegister fd, FloatRegister fs); + BufferOffset as_negs(FloatRegister fd, FloatRegister fs); + BufferOffset as_negd(FloatRegister fd, FloatRegister fs); + + BufferOffset as_muls(FloatRegister fd, FloatRegister fs, FloatRegister ft); + BufferOffset as_muld(FloatRegister fd, FloatRegister fs, FloatRegister ft); + BufferOffset as_divs(FloatRegister fd, FloatRegister fs, FloatRegister ft); + BufferOffset as_divd(FloatRegister fd, FloatRegister fs, FloatRegister ft); + BufferOffset as_sqrts(FloatRegister fd, FloatRegister fs); + BufferOffset as_sqrtd(FloatRegister fd, FloatRegister fs); + + // FP compare instructions + BufferOffset as_cf(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + BufferOffset as_cun(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + BufferOffset as_ceq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + BufferOffset as_cueq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + BufferOffset as_colt(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + BufferOffset as_cult(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + BufferOffset as_cole(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + BufferOffset as_cule(FloatFormat fmt, FloatRegister fs, FloatRegister ft, + FPConditionBit fcc = FCC0); + + // label operations + void bind(Label* label, BufferOffset boff = BufferOffset()); + uint32_t currentOffset() { + return nextOffset().getOffset(); + } + void retarget(Label* label, Label* target); + + // See Bind + size_t labelOffsetToPatchOffset(size_t offset) { + return actualOffset(offset); + } + + void call(Label* label); + void call(void* target); + + void as_break(uint32_t code); + + public: + static bool SupportsFloatingPoint() { +#if (defined(__mips_hard_float) && !defined(__mips_single_float)) || defined(JS_SIMULATOR_MIPS32) + return true; +#else + return false; +#endif + } + static bool SupportsSimd() { + return js::jit::SupportsSimd; + } + + protected: + InstImm invertBranch(InstImm branch, BOffImm16 skipOffset); + void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) { + enoughMemory_ &= jumps_.append(RelativePatch(src, target.value, kind)); + if (kind == Relocation::JITCODE) + writeRelocation(src); + } + + void addLongJump(BufferOffset src) { + enoughMemory_ &= longJumps_.append(src.getOffset()); + } + + public: + size_t numLongJumps() const { + return longJumps_.length(); + } + uint32_t longJump(size_t i) { + return longJumps_[i]; + } + + void flushBuffer() { + } + + static uint32_t NopSize() { return 4; } + + static void PatchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, + ImmPtr expectedValue); + static void PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm); + + static uint32_t AlignDoubleArg(uint32_t offset) { + return (offset + 1U) &~ 1U; + } + + static uint8_t* NextInstruction(uint8_t* instruction, uint32_t* count = nullptr); + + static void ToggleToJmp(CodeLocationLabel inst_); + static void ToggleToCmp(CodeLocationLabel inst_); + + void processCodeLabels(uint8_t* rawCode); + + bool bailed() { + return m_buffer.bail(); + } + + void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, + const Disassembler::HeapAccess& heapAccess) + { + // Implement this if we implement a disassembler. + } +}; // AssemblerMIPSShared + +// sll zero, zero, 0 +const uint32_t NopInst = 0x00000000; + +// An Instruction is a structure for both encoding and decoding any and all +// MIPS instructions. +class Instruction +{ + protected: + uint32_t data; + + // Standard constructor + Instruction (uint32_t data_) : data(data_) { } + + // You should never create an instruction directly. You should create a + // more specific instruction which will eventually call one of these + // constructors for you. + public: + uint32_t encode() const { + return data; + } + + void makeNop() { + data = NopInst; + } + + void setData(uint32_t data) { + this->data = data; + } + + const Instruction & operator=(const Instruction& src) { + data = src.data; + return *this; + } + + // Extract the one particular bit. + uint32_t extractBit(uint32_t bit) { + return (encode() >> bit) & 1; + } + // Extract a bit field out of the instruction + uint32_t extractBitField(uint32_t hi, uint32_t lo) { + return (encode() >> lo) & ((2 << (hi - lo)) - 1); + } + // Since all MIPS instructions have opcode, the opcode + // extractor resides in the base class. + uint32_t extractOpcode() { + return extractBitField(OpcodeShift + OpcodeBits - 1, OpcodeShift); + } + // Return the fields at their original place in the instruction encoding. + Opcode OpcodeFieldRaw() const { + return static_cast(encode() & OpcodeMask); + } + + // Get the next instruction in the instruction stream. + // This does neat things like ignoreconstant pools and their guards. + Instruction* next(); + + // Sometimes, an api wants a uint32_t (or a pointer to it) rather than + // an instruction. raw() just coerces this into a pointer to a uint32_t + const uint32_t* raw() const { return &data; } + uint32_t size() const { return 4; } +}; // Instruction + +// make sure that it is the right size +static_assert(sizeof(Instruction) == 4, "Size of Instruction class has to be 4 bytes."); + +class InstNOP : public Instruction +{ + public: + InstNOP() + : Instruction(NopInst) + { } + +}; + +// Class for register type instructions. +class InstReg : public Instruction +{ + public: + InstReg(Opcode op, Register rd, FunctionField ff) + : Instruction(op | RD(rd) | ff) + { } + InstReg(Opcode op, Register rs, Register rt, FunctionField ff) + : Instruction(op | RS(rs) | RT(rt) | ff) + { } + InstReg(Opcode op, Register rs, Register rt, Register rd, FunctionField ff) + : Instruction(op | RS(rs) | RT(rt) | RD(rd) | ff) + { } + InstReg(Opcode op, Register rs, Register rt, Register rd, uint32_t sa, FunctionField ff) + : Instruction(op | RS(rs) | RT(rt) | RD(rd) | SA(sa) | ff) + { } + InstReg(Opcode op, RSField rs, Register rt, Register rd, uint32_t sa, FunctionField ff) + : Instruction(op | rs | RT(rt) | RD(rd) | SA(sa) | ff) + { } + InstReg(Opcode op, Register rs, RTField rt, Register rd, uint32_t sa, FunctionField ff) + : Instruction(op | RS(rs) | rt | RD(rd) | SA(sa) | ff) + { } + InstReg(Opcode op, Register rs, uint32_t cc, Register rd, uint32_t sa, FunctionField ff) + : Instruction(op | RS(rs) | cc | RD(rd) | SA(sa) | ff) + { } + InstReg(Opcode op, uint32_t code, FunctionField ff) + : Instruction(op | code | ff) + { } + // for float point + InstReg(Opcode op, RSField rs, Register rt, FloatRegister rd) + : Instruction(op | rs | RT(rt) | RD(rd)) + { } + InstReg(Opcode op, RSField rs, Register rt, FloatRegister rd, uint32_t sa, FunctionField ff) + : Instruction(op | rs | RT(rt) | RD(rd) | SA(sa) | ff) + { } + InstReg(Opcode op, RSField rs, Register rt, FloatRegister fs, FloatRegister fd, FunctionField ff) + : Instruction(op | rs | RT(rt) | RD(fs) | SA(fd) | ff) + { } + InstReg(Opcode op, RSField rs, FloatRegister ft, FloatRegister fs, FloatRegister fd, FunctionField ff) + : Instruction(op | rs | RT(ft) | RD(fs) | SA(fd) | ff) + { } + InstReg(Opcode op, RSField rs, FloatRegister ft, FloatRegister fd, uint32_t sa, FunctionField ff) + : Instruction(op | rs | RT(ft) | RD(fd) | SA(sa) | ff) + { } + + uint32_t extractRS () { + return extractBitField(RSShift + RSBits - 1, RSShift); + } + uint32_t extractRT () { + return extractBitField(RTShift + RTBits - 1, RTShift); + } + uint32_t extractRD () { + return extractBitField(RDShift + RDBits - 1, RDShift); + } + uint32_t extractSA () { + return extractBitField(SAShift + SABits - 1, SAShift); + } + uint32_t extractFunctionField () { + return extractBitField(FunctionShift + FunctionBits - 1, FunctionShift); + } +}; + +// Class for branch, load and store instructions with immediate offset. +class InstImm : public Instruction +{ + public: + void extractImm16(BOffImm16* dest); + + InstImm(Opcode op, Register rs, Register rt, BOffImm16 off) + : Instruction(op | RS(rs) | RT(rt) | off.encode()) + { } + InstImm(Opcode op, Register rs, RTField rt, BOffImm16 off) + : Instruction(op | RS(rs) | rt | off.encode()) + { } + InstImm(Opcode op, RSField rs, uint32_t cc, BOffImm16 off) + : Instruction(op | rs | cc | off.encode()) + { } + InstImm(Opcode op, Register rs, Register rt, Imm16 off) + : Instruction(op | RS(rs) | RT(rt) | off.encode()) + { } + InstImm(uint32_t raw) + : Instruction(raw) + { } + // For floating-point loads and stores. + InstImm(Opcode op, Register rs, FloatRegister rt, Imm16 off) + : Instruction(op | RS(rs) | RT(rt) | off.encode()) + { } + + uint32_t extractOpcode() { + return extractBitField(OpcodeShift + OpcodeBits - 1, OpcodeShift); + } + void setOpcode(Opcode op) { + data = (data & ~OpcodeMask) | op; + } + uint32_t extractRS() { + return extractBitField(RSShift + RSBits - 1, RSShift); + } + uint32_t extractRT() { + return extractBitField(RTShift + RTBits - 1, RTShift); + } + void setRT(RTField rt) { + data = (data & ~RTMask) | rt; + } + uint32_t extractImm16Value() { + return extractBitField(Imm16Shift + Imm16Bits - 1, Imm16Shift); + } + void setBOffImm16(BOffImm16 off) { + // Reset immediate field and replace it + data = (data & ~Imm16Mask) | off.encode(); + } + void setImm16(Imm16 off) { + // Reset immediate field and replace it + data = (data & ~Imm16Mask) | off.encode(); + } +}; + +// Class for Jump type instructions. +class InstJump : public Instruction +{ + public: + InstJump(Opcode op, JOffImm26 off) + : Instruction(op | off.encode()) + { } + + uint32_t extractImm26Value() { + return extractBitField(Imm26Shift + Imm26Bits - 1, Imm26Shift); + } +}; + +static const uint32_t NumIntArgRegs = NUM_INT_ARG_REGS; + +static inline bool +GetIntArgReg(uint32_t usedArgSlots, Register* out) +{ + if (usedArgSlots < NumIntArgRegs) { + *out = Register::FromCode(a0.code() + usedArgSlots); + return true; + } + return false; +} + +// Get a register in which we plan to put a quantity that will be used as an +// integer argument. This differs from GetIntArgReg in that if we have no more +// actual argument registers to use we will fall back on using whatever +// CallTempReg* don't overlap the argument registers, and only fail once those +// run out too. +static inline bool +GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register* out) +{ + // NOTE: We can't properly determine which regs are used if there are + // float arguments. If this is needed, we will have to guess. + MOZ_ASSERT(usedFloatArgs == 0); + + if (GetIntArgReg(usedIntArgs, out)) + return true; + // Unfortunately, we have to assume things about the point at which + // GetIntArgReg returns false, because we need to know how many registers it + // can allocate. + usedIntArgs -= NumIntArgRegs; + if (usedIntArgs >= NumCallTempNonArgRegs) + return false; + *out = CallTempNonArgRegs[usedIntArgs]; + return true; +} + +} // namespace jit +} // namespace js + +#endif /* jit_mips_shared_Assembler_mips_shared_h */ diff --git a/js/src/jit/mips32/Assembler-mips32.cpp b/js/src/jit/mips32/Assembler-mips32.cpp index cff988a8206a..e5031c66d935 100644 --- a/js/src/jit/mips32/Assembler-mips32.cpp +++ b/js/src/jit/mips32/Assembler-mips32.cpp @@ -6,18 +6,6 @@ #include "jit/mips32/Assembler-mips32.h" -#include "mozilla/DebugOnly.h" -#include "mozilla/MathAlgorithms.h" - -#include "jscompartment.h" -#include "jsutil.h" - -#include "gc/Marking.h" -#include "jit/ExecutableAllocator.h" -#include "jit/JitCompartment.h" - -using mozilla::DebugOnly; - using namespace js; using namespace js::jit; @@ -87,22 +75,6 @@ const Register ABIArgGenerator::NonArg_VolatileReg = v0; const Register ABIArgGenerator::NonReturn_VolatileReg0 = a0; const Register ABIArgGenerator::NonReturn_VolatileReg1 = a1; -// Encode a standard register when it is being used as rd, the rs, and -// an extra register(rt). These should never be called with an InvalidReg. -uint32_t -js::jit::RS(Register r) -{ - MOZ_ASSERT((r.code() & ~RegMask) == 0); - return r.code() << RSShift; -} - -uint32_t -js::jit::RT(Register r) -{ - MOZ_ASSERT((r.code() & ~RegMask) == 0); - return r.code() << RTShift; -} - uint32_t js::jit::RT(FloatRegister r) { @@ -110,13 +82,6 @@ js::jit::RT(FloatRegister r) return r.id() << RTShift; } -uint32_t -js::jit::RD(Register r) -{ - MOZ_ASSERT((r.code() & ~RegMask) == 0); - return r.code() << RDShift; -} - uint32_t js::jit::RD(FloatRegister r) { @@ -124,13 +89,6 @@ js::jit::RD(FloatRegister r) return r.id() << RDShift; } -uint32_t -js::jit::SA(uint32_t value) -{ - MOZ_ASSERT(value < 32); - return value << SAShift; -} - uint32_t js::jit::SA(FloatRegister r) { @@ -138,36 +96,6 @@ js::jit::SA(FloatRegister r) return r.id() << SAShift; } -Register -js::jit::toRS(Instruction& i) -{ - return Register::FromCode((i.encode() & RSMask ) >> RSShift); -} - -Register -js::jit::toRT(Instruction& i) -{ - return Register::FromCode((i.encode() & RTMask ) >> RTShift); -} - -Register -js::jit::toRD(Instruction& i) -{ - return Register::FromCode((i.encode() & RDMask ) >> RDShift); -} - -Register -js::jit::toR(Instruction& i) -{ - return Register::FromCode(i.encode() & RegMask); -} - -void -InstImm::extractImm16(BOffImm16* dest) -{ - *dest = BOffImm16(*this); -} - // Used to patch jumps created by MacroAssemblerMIPSCompat::jumpWithPatch. void jit::PatchJump(CodeLocationJump& jump_, CodeLocationLabel label, ReprotectCode reprotect) @@ -209,13 +137,6 @@ jit::PatchBackedge(CodeLocationJump& jump, CodeLocationLabel label, } } -void -Assembler::finish() -{ - MOZ_ASSERT(!isFinished); - isFinished = true; -} - void Assembler::executableCopy(uint8_t* buffer) { @@ -226,54 +147,13 @@ Assembler::executableCopy(uint8_t* buffer) for (size_t i = 0; i < longJumps_.length(); i++) { Instruction* inst1 = (Instruction*) ((uint32_t)buffer + longJumps_[i]); - uint32_t value = ExtractLuiOriValue(inst1, inst1->next()); - UpdateLuiOriValue(inst1, inst1->next(), (uint32_t)buffer + value); + uint32_t value = Assembler::ExtractLuiOriValue(inst1, inst1->next()); + Assembler::UpdateLuiOriValue(inst1, inst1->next(), (uint32_t)buffer + value); } AutoFlushICache::setRange(uintptr_t(buffer), m_buffer.size()); } -uint32_t -Assembler::actualOffset(uint32_t off_) const -{ - return off_; -} - -uint32_t -Assembler::actualIndex(uint32_t idx_) const -{ - return idx_; -} - -uint8_t* -Assembler::PatchableJumpAddress(JitCode* code, uint32_t pe_) -{ - return code->raw() + pe_; -} - -class RelocationIterator -{ - CompactBufferReader reader_; - // offset in bytes - uint32_t offset_; - - public: - RelocationIterator(CompactBufferReader& reader) - : reader_(reader) - { } - - bool read() { - if (!reader_.more()) - return false; - offset_ = reader_.readUnsigned(); - return true; - } - - uint32_t offset() const { - return offset_; - } -}; - uintptr_t Assembler::GetPointer(uint8_t* instPtr) { @@ -339,27 +219,6 @@ Assembler::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReade ::TraceDataRelocations(trc, code->raw(), reader); } -void -Assembler::copyJumpRelocationTable(uint8_t* dest) -{ - if (jumpRelocations_.length()) - memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length()); -} - -void -Assembler::copyDataRelocationTable(uint8_t* dest) -{ - if (dataRelocations_.length()) - memcpy(dest, dataRelocations_.buffer(), dataRelocations_.length()); -} - -void -Assembler::copyPreBarrierTable(uint8_t* dest) -{ - if (preBarriers_.length()) - memcpy(dest, preBarriers_.buffer(), preBarriers_.length()); -} - void Assembler::trace(JSTracer* trc) { @@ -377,15 +236,6 @@ Assembler::trace(JSTracer* trc) } } -void -Assembler::processCodeLabels(uint8_t* rawCode) -{ - for (size_t i = 0; i < codeLabels_.length(); i++) { - CodeLabel label = codeLabels_[i]; - Bind(rawCode, label.dest(), rawCode + actualOffset(label.src()->offset())); - } -} - int32_t Assembler::ExtractCodeLabelOffset(uint8_t* code) { InstImm* inst = (InstImm*)code; @@ -407,912 +257,6 @@ Assembler::Bind(uint8_t* rawCode, AbsoluteLabel* label, const void* address) label->bind(); } -Assembler::Condition -Assembler::InvertCondition(Condition cond) -{ - switch (cond) { - case Equal: - return NotEqual; - case NotEqual: - return Equal; - case Zero: - return NonZero; - case NonZero: - return Zero; - case LessThan: - return GreaterThanOrEqual; - case LessThanOrEqual: - return GreaterThan; - case GreaterThan: - return LessThanOrEqual; - case GreaterThanOrEqual: - return LessThan; - case Above: - return BelowOrEqual; - case AboveOrEqual: - return Below; - case Below: - return AboveOrEqual; - case BelowOrEqual: - return Above; - case Signed: - return NotSigned; - case NotSigned: - return Signed; - default: - MOZ_CRASH("unexpected condition"); - } -} - -Assembler::DoubleCondition -Assembler::InvertCondition(DoubleCondition cond) -{ - switch (cond) { - case DoubleOrdered: - return DoubleUnordered; - case DoubleEqual: - return DoubleNotEqualOrUnordered; - case DoubleNotEqual: - return DoubleEqualOrUnordered; - case DoubleGreaterThan: - return DoubleLessThanOrEqualOrUnordered; - case DoubleGreaterThanOrEqual: - return DoubleLessThanOrUnordered; - case DoubleLessThan: - return DoubleGreaterThanOrEqualOrUnordered; - case DoubleLessThanOrEqual: - return DoubleGreaterThanOrUnordered; - case DoubleUnordered: - return DoubleOrdered; - case DoubleEqualOrUnordered: - return DoubleNotEqual; - case DoubleNotEqualOrUnordered: - return DoubleEqual; - case DoubleGreaterThanOrUnordered: - return DoubleLessThanOrEqual; - case DoubleGreaterThanOrEqualOrUnordered: - return DoubleLessThan; - case DoubleLessThanOrUnordered: - return DoubleGreaterThanOrEqual; - case DoubleLessThanOrEqualOrUnordered: - return DoubleGreaterThan; - default: - MOZ_CRASH("unexpected condition"); - } -} - -BOffImm16::BOffImm16(InstImm inst) - : data(inst.encode() & Imm16Mask) -{ -} - -bool -Assembler::oom() const -{ - return AssemblerShared::oom() || - m_buffer.oom() || - jumpRelocations_.oom() || - dataRelocations_.oom() || - preBarriers_.oom(); -} - -void -Assembler::addCodeLabel(CodeLabel label) -{ - propagateOOM(codeLabels_.append(label)); -} - -// Size of the instruction stream, in bytes. -size_t -Assembler::size() const -{ - return m_buffer.size(); -} - -// Size of the relocation table, in bytes. -size_t -Assembler::jumpRelocationTableBytes() const -{ - return jumpRelocations_.length(); -} - -size_t -Assembler::dataRelocationTableBytes() const -{ - return dataRelocations_.length(); -} - -size_t -Assembler::preBarrierTableBytes() const -{ - return preBarriers_.length(); -} - -// Size of the data table, in bytes. -size_t -Assembler::bytesNeeded() const -{ - return size() + - jumpRelocationTableBytes() + - dataRelocationTableBytes() + - preBarrierTableBytes(); -} - -// write a blob of binary into the instruction stream -BufferOffset -Assembler::writeInst(uint32_t x, uint32_t* dest) -{ - if (dest == nullptr) - return m_buffer.putInt(x); - - WriteInstStatic(x, dest); - return BufferOffset(); -} - -void -Assembler::WriteInstStatic(uint32_t x, uint32_t* dest) -{ - MOZ_ASSERT(dest != nullptr); - *dest = x; -} - -BufferOffset -Assembler::haltingAlign(int alignment) -{ - // TODO: Implement a proper halting align. - nopAlign(alignment); -} - -BufferOffset -Assembler::nopAlign(int alignment) -{ - BufferOffset ret; - MOZ_ASSERT(m_buffer.isAligned(4)); - if (alignment == 8) { - if (!m_buffer.isAligned(alignment)) { - BufferOffset tmp = as_nop(); - if (!ret.assigned()) - ret = tmp; - } - } else { - MOZ_ASSERT((alignment & (alignment - 1)) == 0); - while (size() & (alignment - 1)) { - BufferOffset tmp = as_nop(); - if (!ret.assigned()) - ret = tmp; - } - } - return ret; -} - -BufferOffset -Assembler::as_nop() -{ - return writeInst(op_special | ff_sll); -} - -// Logical operations. -BufferOffset -Assembler::as_and(Register rd, Register rs, Register rt) -{ - return writeInst(InstReg(op_special, rs, rt, rd, ff_and).encode()); -} - -BufferOffset -Assembler::as_or(Register rd, Register rs, Register rt) -{ - return writeInst(InstReg(op_special, rs, rt, rd, ff_or).encode()); -} - -BufferOffset -Assembler::as_xor(Register rd, Register rs, Register rt) -{ - return writeInst(InstReg(op_special, rs, rt, rd, ff_xor).encode()); -} - -BufferOffset -Assembler::as_nor(Register rd, Register rs, Register rt) -{ - return writeInst(InstReg(op_special, rs, rt, rd, ff_nor).encode()); -} - -BufferOffset -Assembler::as_andi(Register rd, Register rs, int32_t j) -{ - MOZ_ASSERT(Imm16::IsInUnsignedRange(j)); - return writeInst(InstImm(op_andi, rs, rd, Imm16(j)).encode()); -} - -BufferOffset -Assembler::as_ori(Register rd, Register rs, int32_t j) -{ - MOZ_ASSERT(Imm16::IsInUnsignedRange(j)); - return writeInst(InstImm(op_ori, rs, rd, Imm16(j)).encode()); -} - -BufferOffset -Assembler::as_xori(Register rd, Register rs, int32_t j) -{ - MOZ_ASSERT(Imm16::IsInUnsignedRange(j)); - return writeInst(InstImm(op_xori, rs, rd, Imm16(j)).encode()); -} - -// Branch and jump instructions -BufferOffset -Assembler::as_bal(BOffImm16 off) -{ - BufferOffset bo = writeInst(InstImm(op_regimm, zero, rt_bgezal, off).encode()); - return bo; -} - -BufferOffset -Assembler::as_b(BOffImm16 off) -{ - BufferOffset bo = writeInst(InstImm(op_beq, zero, zero, off).encode()); - return bo; -} - -InstImm -Assembler::getBranchCode(JumpOrCall jumpOrCall) -{ - if (jumpOrCall == BranchIsCall) - return InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)); - - return InstImm(op_beq, zero, zero, BOffImm16(0)); -} - -InstImm -Assembler::getBranchCode(Register s, Register t, Condition c) -{ - MOZ_ASSERT(c == Assembler::Equal || c == Assembler::NotEqual); - return InstImm(c == Assembler::Equal ? op_beq : op_bne, s, t, BOffImm16(0)); -} - -InstImm -Assembler::getBranchCode(Register s, Condition c) -{ - switch (c) { - case Assembler::Equal: - case Assembler::Zero: - case Assembler::BelowOrEqual: - return InstImm(op_beq, s, zero, BOffImm16(0)); - case Assembler::NotEqual: - case Assembler::NonZero: - case Assembler::Above: - return InstImm(op_bne, s, zero, BOffImm16(0)); - case Assembler::GreaterThan: - return InstImm(op_bgtz, s, zero, BOffImm16(0)); - case Assembler::GreaterThanOrEqual: - case Assembler::NotSigned: - return InstImm(op_regimm, s, rt_bgez, BOffImm16(0)); - case Assembler::LessThan: - case Assembler::Signed: - return InstImm(op_regimm, s, rt_bltz, BOffImm16(0)); - case Assembler::LessThanOrEqual: - return InstImm(op_blez, s, zero, BOffImm16(0)); - default: - MOZ_CRASH("Condition not supported."); - } -} - -InstImm -Assembler::getBranchCode(FloatTestKind testKind, FPConditionBit fcc) -{ - MOZ_ASSERT(!(fcc && FccMask)); - uint32_t rtField = ((testKind == TestForTrue ? 1 : 0) | (fcc << FccShift)) << RTShift; - - return InstImm(op_cop1, rs_bc1, rtField, BOffImm16(0)); -} - -BufferOffset -Assembler::as_j(JOffImm26 off) -{ - BufferOffset bo = writeInst(InstJump(op_j, off).encode()); - return bo; -} -BufferOffset -Assembler::as_jal(JOffImm26 off) -{ - BufferOffset bo = writeInst(InstJump(op_jal, off).encode()); - return bo; -} - -BufferOffset -Assembler::as_jr(Register rs) -{ - BufferOffset bo = writeInst(InstReg(op_special, rs, zero, zero, ff_jr).encode()); - return bo; -} -BufferOffset -Assembler::as_jalr(Register rs) -{ - BufferOffset bo = writeInst(InstReg(op_special, rs, zero, ra, ff_jalr).encode()); - return bo; -} - - -// Arithmetic instructions -BufferOffset -Assembler::as_addu(Register rd, Register rs, Register rt) -{ - return writeInst(InstReg(op_special, rs, rt, rd, ff_addu).encode()); -} - -BufferOffset -Assembler::as_addiu(Register rd, Register rs, int32_t j) -{ - MOZ_ASSERT(Imm16::IsInSignedRange(j)); - return writeInst(InstImm(op_addiu, rs, rd, Imm16(j)).encode()); -} - -BufferOffset -Assembler::as_subu(Register rd, Register rs, Register rt) -{ - return writeInst(InstReg(op_special, rs, rt, rd, ff_subu).encode()); -} - -BufferOffset -Assembler::as_mult(Register rs, Register rt) -{ - return writeInst(InstReg(op_special, rs, rt, ff_mult).encode()); -} - -BufferOffset -Assembler::as_multu(Register rs, Register rt) -{ - return writeInst(InstReg(op_special, rs, rt, ff_multu).encode()); -} - -BufferOffset -Assembler::as_div(Register rs, Register rt) -{ - return writeInst(InstReg(op_special, rs, rt, ff_div).encode()); -} - -BufferOffset -Assembler::as_divu(Register rs, Register rt) -{ - return writeInst(InstReg(op_special, rs, rt, ff_divu).encode()); -} - -BufferOffset -Assembler::as_mul(Register rd, Register rs, Register rt) -{ - return writeInst(InstReg(op_special2, rs, rt, rd, ff_mul).encode()); -} - -BufferOffset -Assembler::as_lui(Register rd, int32_t j) -{ - MOZ_ASSERT(Imm16::IsInUnsignedRange(j)); - return writeInst(InstImm(op_lui, zero, rd, Imm16(j)).encode()); -} - -// Shift instructions -BufferOffset -Assembler::as_sll(Register rd, Register rt, uint16_t sa) -{ - MOZ_ASSERT(sa < 32); - return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_sll).encode()); -} - -BufferOffset -Assembler::as_sllv(Register rd, Register rt, Register rs) -{ - return writeInst(InstReg(op_special, rs, rt, rd, ff_sllv).encode()); -} - -BufferOffset -Assembler::as_srl(Register rd, Register rt, uint16_t sa) -{ - MOZ_ASSERT(sa < 32); - return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_srl).encode()); -} - -BufferOffset -Assembler::as_srlv(Register rd, Register rt, Register rs) -{ - return writeInst(InstReg(op_special, rs, rt, rd, ff_srlv).encode()); -} - -BufferOffset -Assembler::as_sra(Register rd, Register rt, uint16_t sa) -{ - MOZ_ASSERT(sa < 32); - return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_sra).encode()); -} - -BufferOffset -Assembler::as_srav(Register rd, Register rt, Register rs) -{ - return writeInst(InstReg(op_special, rs, rt, rd, ff_srav).encode()); -} - -BufferOffset -Assembler::as_rotr(Register rd, Register rt, uint16_t sa) -{ - MOZ_ASSERT(sa < 32); - return writeInst(InstReg(op_special, rs_one, rt, rd, sa, ff_srl).encode()); -} - -BufferOffset -Assembler::as_rotrv(Register rd, Register rt, Register rs) -{ - return writeInst(InstReg(op_special, rs, rt, rd, 1, ff_srlv).encode()); -} - -// Load and store instructions -BufferOffset -Assembler::as_lb(Register rd, Register rs, int16_t off) -{ - return writeInst(InstImm(op_lb, rs, rd, Imm16(off)).encode()); -} - -BufferOffset -Assembler::as_lbu(Register rd, Register rs, int16_t off) -{ - return writeInst(InstImm(op_lbu, rs, rd, Imm16(off)).encode()); -} - -BufferOffset -Assembler::as_lh(Register rd, Register rs, int16_t off) -{ - return writeInst(InstImm(op_lh, rs, rd, Imm16(off)).encode()); -} - -BufferOffset -Assembler::as_lhu(Register rd, Register rs, int16_t off) -{ - return writeInst(InstImm(op_lhu, rs, rd, Imm16(off)).encode()); -} - -BufferOffset -Assembler::as_lw(Register rd, Register rs, int16_t off) -{ - return writeInst(InstImm(op_lw, rs, rd, Imm16(off)).encode()); -} - -BufferOffset -Assembler::as_lwl(Register rd, Register rs, int16_t off) -{ - return writeInst(InstImm(op_lwl, rs, rd, Imm16(off)).encode()); -} - -BufferOffset -Assembler::as_lwr(Register rd, Register rs, int16_t off) -{ - return writeInst(InstImm(op_lwr, rs, rd, Imm16(off)).encode()); -} - -BufferOffset -Assembler::as_sb(Register rd, Register rs, int16_t off) -{ - return writeInst(InstImm(op_sb, rs, rd, Imm16(off)).encode()); -} - -BufferOffset -Assembler::as_sh(Register rd, Register rs, int16_t off) -{ - return writeInst(InstImm(op_sh, rs, rd, Imm16(off)).encode()); -} - -BufferOffset -Assembler::as_sw(Register rd, Register rs, int16_t off) -{ - return writeInst(InstImm(op_sw, rs, rd, Imm16(off)).encode()); -} - -BufferOffset -Assembler::as_swl(Register rd, Register rs, int16_t off) -{ - return writeInst(InstImm(op_swl, rs, rd, Imm16(off)).encode()); -} - -BufferOffset -Assembler::as_swr(Register rd, Register rs, int16_t off) -{ - return writeInst(InstImm(op_swr, rs, rd, Imm16(off)).encode()); -} - -// Move from HI/LO register. -BufferOffset -Assembler::as_mfhi(Register rd) -{ - return writeInst(InstReg(op_special, rd, ff_mfhi).encode()); -} - -BufferOffset -Assembler::as_mflo(Register rd) -{ - return writeInst(InstReg(op_special, rd, ff_mflo).encode()); -} - -// Set on less than. -BufferOffset -Assembler::as_slt(Register rd, Register rs, Register rt) -{ - return writeInst(InstReg(op_special, rs, rt, rd, ff_slt).encode()); -} - -BufferOffset -Assembler::as_sltu(Register rd, Register rs, Register rt) -{ - return writeInst(InstReg(op_special, rs, rt, rd, ff_sltu).encode()); -} - -BufferOffset -Assembler::as_slti(Register rd, Register rs, int32_t j) -{ - MOZ_ASSERT(Imm16::IsInSignedRange(j)); - return writeInst(InstImm(op_slti, rs, rd, Imm16(j)).encode()); -} - -BufferOffset -Assembler::as_sltiu(Register rd, Register rs, uint32_t j) -{ - MOZ_ASSERT(Imm16::IsInUnsignedRange(j)); - return writeInst(InstImm(op_sltiu, rs, rd, Imm16(j)).encode()); -} - -// Conditional move. -BufferOffset -Assembler::as_movz(Register rd, Register rs, Register rt) -{ - return writeInst(InstReg(op_special, rs, rt, rd, ff_movz).encode()); -} - -BufferOffset -Assembler::as_movn(Register rd, Register rs, Register rt) -{ - return writeInst(InstReg(op_special, rs, rt, rd, ff_movn).encode()); -} - -BufferOffset -Assembler::as_movt(Register rd, Register rs, uint16_t cc) -{ - Register rt; - rt = Register::FromCode((cc & 0x7) << 2 | 1); - return writeInst(InstReg(op_special, rs, rt, rd, ff_movci).encode()); -} - -BufferOffset -Assembler::as_movf(Register rd, Register rs, uint16_t cc) -{ - Register rt; - rt = Register::FromCode((cc & 0x7) << 2 | 0); - return writeInst(InstReg(op_special, rs, rt, rd, ff_movci).encode()); -} - -// Bit twiddling. -BufferOffset -Assembler::as_clz(Register rd, Register rs) -{ - return writeInst(InstReg(op_special2, rs, rd, rd, ff_clz).encode()); -} - -BufferOffset -Assembler::as_ins(Register rt, Register rs, uint16_t pos, uint16_t size) -{ - MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 32); - Register rd; - rd = Register::FromCode(pos + size - 1); - return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ins).encode()); -} - -BufferOffset -Assembler::as_ext(Register rt, Register rs, uint16_t pos, uint16_t size) -{ - MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 32); - Register rd; - rd = Register::FromCode(size - 1); - return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ext).encode()); -} - -// FP instructions -BufferOffset -Assembler::as_ld(FloatRegister fd, Register base, int32_t off) -{ - MOZ_ASSERT(Imm16::IsInSignedRange(off)); - return writeInst(InstImm(op_ldc1, base, fd, Imm16(off)).encode()); -} - -BufferOffset -Assembler::as_sd(FloatRegister fd, Register base, int32_t off) -{ - MOZ_ASSERT(Imm16::IsInSignedRange(off)); - return writeInst(InstImm(op_sdc1, base, fd, Imm16(off)).encode()); -} - -BufferOffset -Assembler::as_ls(FloatRegister fd, Register base, int32_t off) -{ - MOZ_ASSERT(Imm16::IsInSignedRange(off)); - return writeInst(InstImm(op_lwc1, base, fd, Imm16(off)).encode()); -} - -BufferOffset -Assembler::as_ss(FloatRegister fd, Register base, int32_t off) -{ - MOZ_ASSERT(Imm16::IsInSignedRange(off)); - return writeInst(InstImm(op_swc1, base, fd, Imm16(off)).encode()); -} - -BufferOffset -Assembler::as_movs(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_mov_fmt).encode()); -} - -BufferOffset -Assembler::as_movd(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_mov_fmt).encode()); -} - -BufferOffset -Assembler::as_mtc1(Register rt, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_mtc1, rt, fs).encode()); -} - -BufferOffset -Assembler::as_mfc1(Register rt, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_mfc1, rt, fs).encode()); -} - -// FP convert instructions -BufferOffset -Assembler::as_ceilws(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_ceil_w_fmt).encode()); -} - -BufferOffset -Assembler::as_floorws(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_floor_w_fmt).encode()); -} - -BufferOffset -Assembler::as_roundws(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_round_w_fmt).encode()); -} - -BufferOffset -Assembler::as_truncws(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_trunc_w_fmt).encode()); -} - -BufferOffset -Assembler::as_ceilwd(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_ceil_w_fmt).encode()); -} - -BufferOffset -Assembler::as_floorwd(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_floor_w_fmt).encode()); -} - -BufferOffset -Assembler::as_roundwd(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_round_w_fmt).encode()); -} - -BufferOffset -Assembler::as_truncwd(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_trunc_w_fmt).encode()); -} - -BufferOffset -Assembler::as_cvtds(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_cvt_d_fmt).encode()); -} - -BufferOffset -Assembler::as_cvtdw(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_w, zero, fs, fd, ff_cvt_d_fmt).encode()); -} - -BufferOffset -Assembler::as_cvtsd(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_cvt_s_fmt).encode()); -} - -BufferOffset -Assembler::as_cvtsw(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_w, zero, fs, fd, ff_cvt_s_fmt).encode()); -} - -BufferOffset -Assembler::as_cvtwd(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_cvt_w_fmt).encode()); -} - -BufferOffset -Assembler::as_cvtws(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_cvt_w_fmt).encode()); -} - -// FP arithmetic instructions -BufferOffset -Assembler::as_adds(FloatRegister fd, FloatRegister fs, FloatRegister ft) -{ - return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_add_fmt).encode()); -} - -BufferOffset -Assembler::as_addd(FloatRegister fd, FloatRegister fs, FloatRegister ft) -{ - return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_add_fmt).encode()); -} - -BufferOffset -Assembler::as_subs(FloatRegister fd, FloatRegister fs, FloatRegister ft) -{ - return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_sub_fmt).encode()); -} - -BufferOffset -Assembler::as_subd(FloatRegister fd, FloatRegister fs, FloatRegister ft) -{ - return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_sub_fmt).encode()); -} - -BufferOffset -Assembler::as_abss(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_abs_fmt).encode()); -} - -BufferOffset -Assembler::as_absd(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_abs_fmt).encode()); -} - -BufferOffset -Assembler::as_negs(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_neg_fmt).encode()); -} - -BufferOffset -Assembler::as_negd(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_neg_fmt).encode()); -} - -BufferOffset -Assembler::as_muls(FloatRegister fd, FloatRegister fs, FloatRegister ft) -{ - return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_mul_fmt).encode()); -} - -BufferOffset -Assembler::as_muld(FloatRegister fd, FloatRegister fs, FloatRegister ft) -{ - return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_mul_fmt).encode()); -} - -BufferOffset -Assembler::as_divs(FloatRegister fd, FloatRegister fs, FloatRegister ft) -{ - return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_div_fmt).encode()); -} - -BufferOffset -Assembler::as_divd(FloatRegister fd, FloatRegister fs, FloatRegister ft) -{ - return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_div_fmt).encode()); -} - -BufferOffset -Assembler::as_sqrts(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_sqrt_fmt).encode()); -} - -BufferOffset -Assembler::as_sqrtd(FloatRegister fd, FloatRegister fs) -{ - return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_sqrt_fmt).encode()); -} - -// FP compare instructions -BufferOffset -Assembler::as_cf(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) -{ - RSField rs = fmt == DoubleFloat ? rs_d : rs_s; - return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_f_fmt).encode()); -} - -BufferOffset -Assembler::as_cun(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) -{ - RSField rs = fmt == DoubleFloat ? rs_d : rs_s; - return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_un_fmt).encode()); -} - -BufferOffset -Assembler::as_ceq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) -{ - RSField rs = fmt == DoubleFloat ? rs_d : rs_s; - return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_eq_fmt).encode()); -} - -BufferOffset -Assembler::as_cueq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) -{ - RSField rs = fmt == DoubleFloat ? rs_d : rs_s; - return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ueq_fmt).encode()); -} - -BufferOffset -Assembler::as_colt(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) -{ - RSField rs = fmt == DoubleFloat ? rs_d : rs_s; - return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_olt_fmt).encode()); -} - -BufferOffset -Assembler::as_cult(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) -{ - RSField rs = fmt == DoubleFloat ? rs_d : rs_s; - return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ult_fmt).encode()); -} - -BufferOffset -Assembler::as_cole(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) -{ - RSField rs = fmt == DoubleFloat ? rs_d : rs_s; - return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ole_fmt).encode()); -} - -BufferOffset -Assembler::as_cule(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) -{ - RSField rs = fmt == DoubleFloat ? rs_d : rs_s; - return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ule_fmt).encode()); -} - - -void -Assembler::bind(Label* label, BufferOffset boff) -{ - // If our caller didn't give us an explicit target to bind to - // then we want to bind to the location of the next instruction - BufferOffset dest = boff.assigned() ? boff : nextOffset(); - if (label->used()) { - int32_t next; - - // A used label holds a link to branch that uses it. - BufferOffset b(label); - do { - Instruction* inst = editSrc(b); - - // Second word holds a pointer to the next branch in label's chain. - next = inst[1].encode(); - bind(reinterpret_cast(inst), b.getOffset(), dest.getOffset()); - - b = BufferOffset(next); - } while (next != LabelBase::INVALID_OFFSET); - } - label->bind(dest.getOffset()); -} - void Assembler::bind(InstImm* inst, uint32_t branch, uint32_t target) { @@ -1332,7 +276,7 @@ Assembler::bind(InstImm* inst, uint32_t branch, uint32_t target) // address after the reserved block. if (inst[0].encode() == inst_bgezal.encode()) { addLongJump(BufferOffset(branch)); - WriteLuiOriInstructions(inst, &inst[1], ScratchRegister, target); + Assembler::WriteLuiOriInstructions(inst, &inst[1], ScratchRegister, target); inst[2] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr).encode(); // There is 1 nop after this. return; @@ -1356,7 +300,7 @@ Assembler::bind(InstImm* inst, uint32_t branch, uint32_t target) if (inst[0].encode() == inst_beq.encode()) { // Handle long unconditional jump. addLongJump(BufferOffset(branch)); - WriteLuiOriInstructions(inst, &inst[1], ScratchRegister, target); + Assembler::WriteLuiOriInstructions(inst, &inst[1], ScratchRegister, target); inst[2] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr).encode(); // There is 1 nop after this. } else { @@ -1364,7 +308,7 @@ Assembler::bind(InstImm* inst, uint32_t branch, uint32_t target) inst[0] = invertBranch(inst[0], BOffImm16(5 * sizeof(void*))); // No need for a "nop" here because we can clobber scratch. addLongJump(BufferOffset(branch + sizeof(void*))); - WriteLuiOriInstructions(&inst[1], &inst[2], ScratchRegister, target); + Assembler::WriteLuiOriInstructions(&inst[1], &inst[2], ScratchRegister, target); inst[3] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr).encode(); // There is 1 nop after this. } @@ -1388,57 +332,13 @@ Assembler::bind(RepatchLabel* label) MOZ_ASSERT(BOffImm16::IsInRange(offset)); inst1->setBOffImm16(BOffImm16(offset)); } else { - UpdateLuiOriValue(inst1, inst1->next(), dest.getOffset()); + Assembler::UpdateLuiOriValue(inst1, inst1->next(), dest.getOffset()); } } label->bind(dest.getOffset()); } -void -Assembler::retarget(Label* label, Label* target) -{ - if (label->used()) { - if (target->bound()) { - bind(label, BufferOffset(target)); - } else if (target->used()) { - // The target is not bound but used. Prepend label's branch list - // onto target's. - int32_t next; - BufferOffset labelBranchOffset(label); - - // Find the head of the use chain for label. - do { - Instruction* inst = editSrc(labelBranchOffset); - - // Second word holds a pointer to the next branch in chain. - next = inst[1].encode(); - labelBranchOffset = BufferOffset(next); - } while (next != LabelBase::INVALID_OFFSET); - - // Then patch the head of label's use chain to the tail of - // target's use chain, prepending the entire use chain of target. - Instruction* inst = editSrc(labelBranchOffset); - int32_t prev = target->use(label->offset()); - inst[1].setData(prev); - } else { - // The target is unbound and unused. We can just take the head of - // the list hanging off of label, and dump that into target. - DebugOnly prev = target->use(label->offset()); - MOZ_ASSERT((int32_t)prev == Label::INVALID_OFFSET); - } - } - label->reset(); -} - -void dbg_break() {} -void -Assembler::as_break(uint32_t code) -{ - MOZ_ASSERT(code <= MAX_BREAK_CODE); - writeInst(op_special | code << FunctionBits | ff_break); -} - uint32_t Assembler::PatchWrite_NearCallSize() { @@ -1456,7 +356,7 @@ Assembler::PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall // - Jump has to be the same size because of PatchWrite_NearCallSize. // - Return address has to be at the end of replaced block. // Short jump wouldn't be more efficient. - WriteLuiOriInstructions(inst, &inst[1], ScratchRegister, (uint32_t)dest); + Assembler::WriteLuiOriInstructions(inst, &inst[1], ScratchRegister, (uint32_t)dest); inst[2] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr); inst[3] = InstNOP(); @@ -1511,28 +411,6 @@ Assembler::PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newVal AutoFlushICache::flush(uintptr_t(inst), 8); } -void -Assembler::PatchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, ImmPtr expectedValue) -{ - PatchDataWithValueCheck(label, PatchedImmPtr(newValue.value), - PatchedImmPtr(expectedValue.value)); -} - -// This just stomps over memory with 32 bits of raw data. Its purpose is to -// overwrite the call of JITed code with 32 bits worth of an offset. This will -// is only meant to function on code that has been invalidated, so it should -// be totally safe. Since that instruction will never be executed again, a -// ICache flush should not be necessary -void -Assembler::PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm) -{ - // Raw is going to be the return address. - uint32_t* raw = (uint32_t*)label.raw(); - // Overwrite the 4 bytes before the return address, which will - // end up being the call instruction. - *(raw - 1) = imm.value; -} - void Assembler::PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm) { @@ -1540,97 +418,6 @@ Assembler::PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm) Assembler::UpdateLuiOriValue(inst, inst->next(), (uint32_t)imm.value); } -uint8_t* -Assembler::NextInstruction(uint8_t* inst_, uint32_t* count) -{ - Instruction* inst = reinterpret_cast(inst_); - if (count != nullptr) - *count += sizeof(Instruction); - return reinterpret_cast(inst->next()); -} - -// Since there are no pools in MIPS implementation, this should be simple. -Instruction* -Instruction::next() -{ - return this + 1; -} - -InstImm Assembler::invertBranch(InstImm branch, BOffImm16 skipOffset) -{ - uint32_t rt = 0; - Opcode op = (Opcode) (branch.extractOpcode() << OpcodeShift); - switch(op) { - case op_beq: - branch.setBOffImm16(skipOffset); - branch.setOpcode(op_bne); - return branch; - case op_bne: - branch.setBOffImm16(skipOffset); - branch.setOpcode(op_beq); - return branch; - case op_bgtz: - branch.setBOffImm16(skipOffset); - branch.setOpcode(op_blez); - return branch; - case op_blez: - branch.setBOffImm16(skipOffset); - branch.setOpcode(op_bgtz); - return branch; - case op_regimm: - branch.setBOffImm16(skipOffset); - rt = branch.extractRT(); - if (rt == (rt_bltz >> RTShift)) { - branch.setRT(rt_bgez); - return branch; - } - if (rt == (rt_bgez >> RTShift)) { - branch.setRT(rt_bltz); - return branch; - } - - MOZ_CRASH("Error creating long branch."); - - case op_cop1: - MOZ_ASSERT(branch.extractRS() == rs_bc1 >> RSShift); - - branch.setBOffImm16(skipOffset); - rt = branch.extractRT(); - if (rt & 0x1) - branch.setRT((RTField) ((rt & ~0x1) << RTShift)); - else - branch.setRT((RTField) ((rt | 0x1) << RTShift)); - return branch; - default: - MOZ_CRASH("Error creating long branch."); - } -} - -void -Assembler::ToggleToJmp(CodeLocationLabel inst_) -{ - InstImm * inst = (InstImm*)inst_.raw(); - - MOZ_ASSERT(inst->extractOpcode() == ((uint32_t)op_andi >> OpcodeShift)); - // We converted beq to andi, so now we restore it. - inst->setOpcode(op_beq); - - AutoFlushICache::flush(uintptr_t(inst), 4); -} - -void -Assembler::ToggleToCmp(CodeLocationLabel inst_) -{ - InstImm * inst = (InstImm*)inst_.raw(); - - // toggledJump is allways used for short jumps. - MOZ_ASSERT(inst->extractOpcode() == ((uint32_t)op_beq >> OpcodeShift)); - // Replace "beq $zero, $zero, offset" with "andi $zero, $zero, offset" - inst->setOpcode(op_andi); - - AutoFlushICache::flush(uintptr_t(inst), 4); -} - void Assembler::ToggleCall(CodeLocationLabel inst_, bool enabled) { @@ -1653,7 +440,8 @@ Assembler::ToggleCall(CodeLocationLabel inst_, bool enabled) AutoFlushICache::flush(uintptr_t(i2), 4); } -void Assembler::UpdateBoundsCheck(uint32_t heapSize, Instruction* inst) +void +Assembler::UpdateBoundsCheck(uint32_t heapSize, Instruction* inst) { InstImm* i0 = (InstImm*) inst; InstImm* i1 = (InstImm*) i0->next(); diff --git a/js/src/jit/mips32/Assembler-mips32.h b/js/src/jit/mips32/Assembler-mips32.h index 814c40ec0c3d..d9a9200eaee7 100644 --- a/js/src/jit/mips32/Assembler-mips32.h +++ b/js/src/jit/mips32/Assembler-mips32.h @@ -7,91 +7,23 @@ #ifndef jit_mips32_Assembler_mips32_h #define jit_mips32_Assembler_mips32_h -#include "mozilla/ArrayUtils.h" -#include "mozilla/Attributes.h" -#include "mozilla/MathAlgorithms.h" - -#include "jit/CompactBuffer.h" -#include "jit/IonCode.h" -#include "jit/JitCompartment.h" -#include "jit/JitSpewer.h" #include "jit/mips32/Architecture-mips32.h" -#include "jit/shared/Assembler-shared.h" -#include "jit/shared/IonAssemblerBuffer.h" + +// NOTE: Don't use these macros directly +// CallTempNonArgRegs +#define CALL_TEMP_NON_ARG_REGS \ + { t0, t1, t2, t3, t4 }; +// NumIntArgRegs +#define NUM_INT_ARG_REGS 4; + +#include "jit/mips-shared/Assembler-mips-shared.h" namespace js { namespace jit { -static MOZ_CONSTEXPR_VAR Register zero = { Registers::zero }; -static MOZ_CONSTEXPR_VAR Register at = { Registers::at }; -static MOZ_CONSTEXPR_VAR Register v0 = { Registers::v0 }; -static MOZ_CONSTEXPR_VAR Register v1 = { Registers::v1 }; -static MOZ_CONSTEXPR_VAR Register a0 = { Registers::a0 }; -static MOZ_CONSTEXPR_VAR Register a1 = { Registers::a1 }; -static MOZ_CONSTEXPR_VAR Register a2 = { Registers::a2 }; -static MOZ_CONSTEXPR_VAR Register a3 = { Registers::a3 }; -static MOZ_CONSTEXPR_VAR Register t0 = { Registers::t0 }; -static MOZ_CONSTEXPR_VAR Register t1 = { Registers::t1 }; -static MOZ_CONSTEXPR_VAR Register t2 = { Registers::t2 }; -static MOZ_CONSTEXPR_VAR Register t3 = { Registers::t3 }; -static MOZ_CONSTEXPR_VAR Register t4 = { Registers::t4 }; -static MOZ_CONSTEXPR_VAR Register t5 = { Registers::t5 }; -static MOZ_CONSTEXPR_VAR Register t6 = { Registers::t6 }; -static MOZ_CONSTEXPR_VAR Register t7 = { Registers::t7 }; -static MOZ_CONSTEXPR_VAR Register s0 = { Registers::s0 }; -static MOZ_CONSTEXPR_VAR Register s1 = { Registers::s1 }; -static MOZ_CONSTEXPR_VAR Register s2 = { Registers::s2 }; -static MOZ_CONSTEXPR_VAR Register s3 = { Registers::s3 }; -static MOZ_CONSTEXPR_VAR Register s4 = { Registers::s4 }; -static MOZ_CONSTEXPR_VAR Register s5 = { Registers::s5 }; -static MOZ_CONSTEXPR_VAR Register s6 = { Registers::s6 }; -static MOZ_CONSTEXPR_VAR Register s7 = { Registers::s7 }; -static MOZ_CONSTEXPR_VAR Register t8 = { Registers::t8 }; -static MOZ_CONSTEXPR_VAR Register t9 = { Registers::t9 }; -static MOZ_CONSTEXPR_VAR Register k0 = { Registers::k0 }; -static MOZ_CONSTEXPR_VAR Register k1 = { Registers::k1 }; -static MOZ_CONSTEXPR_VAR Register gp = { Registers::gp }; -static MOZ_CONSTEXPR_VAR Register sp = { Registers::sp }; -static MOZ_CONSTEXPR_VAR Register fp = { Registers::fp }; -static MOZ_CONSTEXPR_VAR Register ra = { Registers::ra }; - -static MOZ_CONSTEXPR_VAR Register ScratchRegister = at; -static MOZ_CONSTEXPR_VAR Register SecondScratchReg = t8; - -// Helper classes for ScratchRegister usage. Asserts that only one piece -// of code thinks it has exclusive ownership of each scratch register. -struct ScratchRegisterScope : public AutoRegisterScope -{ - explicit ScratchRegisterScope(MacroAssembler& masm) - : AutoRegisterScope(masm, ScratchRegister) - { } -}; -struct SecondScratchRegisterScope : public AutoRegisterScope -{ - explicit SecondScratchRegisterScope(MacroAssembler& masm) - : AutoRegisterScope(masm, SecondScratchReg) - { } -}; - -// Use arg reg from EnterJIT function as OsrFrameReg. -static MOZ_CONSTEXPR_VAR Register OsrFrameReg = a3; -static MOZ_CONSTEXPR_VAR Register ArgumentsRectifierReg = s3; -static MOZ_CONSTEXPR_VAR Register CallTempReg0 = t0; -static MOZ_CONSTEXPR_VAR Register CallTempReg1 = t1; -static MOZ_CONSTEXPR_VAR Register CallTempReg2 = t2; -static MOZ_CONSTEXPR_VAR Register CallTempReg3 = t3; static MOZ_CONSTEXPR_VAR Register CallTempReg4 = t4; static MOZ_CONSTEXPR_VAR Register CallTempReg5 = t5; -static MOZ_CONSTEXPR_VAR Register IntArgReg0 = a0; -static MOZ_CONSTEXPR_VAR Register IntArgReg1 = a1; -static MOZ_CONSTEXPR_VAR Register IntArgReg2 = a2; -static MOZ_CONSTEXPR_VAR Register IntArgReg3 = a3; -static MOZ_CONSTEXPR_VAR Register GlobalReg = s6; // used by Odin -static MOZ_CONSTEXPR_VAR Register HeapReg = s7; // used by Odin -static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = { t0, t1, t2, t3, t4 }; -static const uint32_t NumCallTempNonArgRegs = mozilla::ArrayLength(CallTempNonArgRegs); - class ABIArgGenerator { unsigned usedArgSlots_; @@ -124,45 +56,19 @@ class ABIArgGenerator static const Register NonReturn_VolatileReg1; }; -static MOZ_CONSTEXPR_VAR Register PreBarrierReg = a1; - -static MOZ_CONSTEXPR_VAR Register InvalidReg = { Registers::invalid_reg }; -static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg; - static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = a3; static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = a2; -static MOZ_CONSTEXPR_VAR Register StackPointer = sp; -static MOZ_CONSTEXPR_VAR Register FramePointer = InvalidReg; -static MOZ_CONSTEXPR_VAR Register ReturnReg = v0; static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32Reg = { FloatRegisters::f0, FloatRegister::Single }; static MOZ_CONSTEXPR_VAR FloatRegister ReturnDoubleReg = { FloatRegisters::f0, FloatRegister::Double }; -static MOZ_CONSTEXPR_VAR FloatRegister ReturnInt32x4Reg = InvalidFloatReg; -static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32x4Reg = InvalidFloatReg; static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloat32Reg = { FloatRegisters::f18, FloatRegister::Single }; static MOZ_CONSTEXPR_VAR FloatRegister ScratchDoubleReg = { FloatRegisters::f18, FloatRegister::Double }; -static MOZ_CONSTEXPR_VAR FloatRegister ScratchSimdReg = InvalidFloatReg; static MOZ_CONSTEXPR_VAR FloatRegister SecondScratchFloat32Reg = { FloatRegisters::f16, FloatRegister::Single }; static MOZ_CONSTEXPR_VAR FloatRegister SecondScratchDoubleReg = { FloatRegisters::f16, FloatRegister::Double }; -// A bias applied to the GlobalReg to allow the use of instructions with small -// negative immediate offsets which doubles the range of global data that can be -// accessed with a single instruction. -static const int32_t AsmJSGlobalRegBias = 32768; - -// Registers used in the GenerateFFIIonExit Enable Activation block. -static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = t0; -static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = a0; -static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = a1; -static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = a2; -static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = a3; - // Registers used in the GenerateFFIIonExit Disable Activation block. // None of these may be the second scratch register (t8). static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = JSReturnReg_Data; static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = JSReturnReg_Type; -static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = a0; -static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = a1; -static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = t0; static MOZ_CONSTEXPR_VAR FloatRegister f0 = { FloatRegisters::f0, FloatRegister::Double }; static MOZ_CONSTEXPR_VAR FloatRegister f2 = { FloatRegisters::f2, FloatRegister::Double }; @@ -184,792 +90,32 @@ static MOZ_CONSTEXPR_VAR FloatRegister f30 = { FloatRegisters::f30, FloatRegiste // MIPS CPUs can only load multibyte data that is "naturally" // four-byte-aligned, sp register should be eight-byte-aligned. static MOZ_CONSTEXPR_VAR uint32_t ABIStackAlignment = 8; -static MOZ_CONSTEXPR_VAR uint32_t CodeAlignment = 4; static MOZ_CONSTEXPR_VAR uint32_t JitStackAlignment = 8; static MOZ_CONSTEXPR_VAR uint32_t JitStackValueAlignment = JitStackAlignment / sizeof(Value); static_assert(JitStackAlignment % sizeof(Value) == 0 && JitStackValueAlignment >= 1, "Stack alignment should be a non-zero multiple of sizeof(Value)"); -// This boolean indicates whether we support SIMD instructions flavoured for -// this architecture or not. Rather than a method in the LIRGenerator, it is -// here such that it is accessible from the entire codebase. Once full support -// for SIMD is reached on all tier-1 platforms, this constant can be deleted. -static MOZ_CONSTEXPR_VAR bool SupportsSimd = false; // TODO this is just a filler to prevent a build failure. The MIPS SIMD // alignment requirements still need to be explored. // TODO Copy the static_asserts from x64/x86 assembler files. static MOZ_CONSTEXPR_VAR uint32_t SimdMemoryAlignment = 8; - static MOZ_CONSTEXPR_VAR uint32_t AsmJSStackAlignment = SimdMemoryAlignment; static MOZ_CONSTEXPR_VAR Scale ScalePointer = TimesFour; -// MIPS instruction types -// +---------------------------------------------------------------+ -// | 6 | 5 | 5 | 5 | 5 | 6 | -// +---------------------------------------------------------------+ -// Register type | Opcode | Rs | Rt | Rd | Sa | Function | -// +---------------------------------------------------------------+ -// | 6 | 5 | 5 | 16 | -// +---------------------------------------------------------------+ -// Immediate type | Opcode | Rs | Rt | 2's complement constant | -// +---------------------------------------------------------------+ -// | 6 | 26 | -// +---------------------------------------------------------------+ -// Jump type | Opcode | jump_target | -// +---------------------------------------------------------------+ -// 31 bit bit 0 - -// MIPS instruction encoding constants. -static const uint32_t OpcodeShift = 26; -static const uint32_t OpcodeBits = 6; -static const uint32_t RSShift = 21; -static const uint32_t RSBits = 5; -static const uint32_t RTShift = 16; -static const uint32_t RTBits = 5; -static const uint32_t RDShift = 11; -static const uint32_t RDBits = 5; -static const uint32_t SAShift = 6; -static const uint32_t SABits = 5; -static const uint32_t FunctionShift = 0; -static const uint32_t FunctionBits = 6; -static const uint32_t Imm16Shift = 0; -static const uint32_t Imm16Bits = 16; -static const uint32_t Imm26Shift = 0; -static const uint32_t Imm26Bits = 26; -static const uint32_t Imm28Shift = 0; -static const uint32_t Imm28Bits = 28; -static const uint32_t ImmFieldShift = 2; -static const uint32_t FRBits = 5; -static const uint32_t FRShift = 21; -static const uint32_t FSShift = 11; -static const uint32_t FSBits = 5; -static const uint32_t FTShift = 16; -static const uint32_t FTBits = 5; -static const uint32_t FDShift = 6; -static const uint32_t FDBits = 5; -static const uint32_t FCccShift = 8; -static const uint32_t FCccBits = 3; -static const uint32_t FBccShift = 18; -static const uint32_t FBccBits = 3; -static const uint32_t FBtrueShift = 16; -static const uint32_t FBtrueBits = 1; -static const uint32_t FccMask = 0x7; -static const uint32_t FccShift = 2; - - -// MIPS instruction field bit masks. -static const uint32_t OpcodeMask = ((1 << OpcodeBits) - 1) << OpcodeShift; -static const uint32_t Imm16Mask = ((1 << Imm16Bits) - 1) << Imm16Shift; -static const uint32_t Imm26Mask = ((1 << Imm26Bits) - 1) << Imm26Shift; -static const uint32_t Imm28Mask = ((1 << Imm28Bits) - 1) << Imm28Shift; -static const uint32_t RSMask = ((1 << RSBits) - 1) << RSShift; -static const uint32_t RTMask = ((1 << RTBits) - 1) << RTShift; -static const uint32_t RDMask = ((1 << RDBits) - 1) << RDShift; -static const uint32_t SAMask = ((1 << SABits) - 1) << SAShift; -static const uint32_t FunctionMask = ((1 << FunctionBits) - 1) << FunctionShift; -static const uint32_t RegMask = Registers::Total - 1; - -static const uint32_t BREAK_STACK_UNALIGNED = 1; -static const uint32_t MAX_BREAK_CODE = 1024 - 1; - -class Instruction; -class InstReg; -class InstImm; -class InstJump; - -uint32_t RS(Register r); -uint32_t RT(Register r); -uint32_t RT(uint32_t regCode); -uint32_t RT(FloatRegister r); -uint32_t RD(Register r); -uint32_t RD(FloatRegister r); -uint32_t RD(uint32_t regCode); -uint32_t SA(uint32_t value); -uint32_t SA(FloatRegister r); - -Register toRS (Instruction& i); -Register toRT (Instruction& i); -Register toRD (Instruction& i); -Register toR (Instruction& i); - -// MIPS enums for instruction fields -enum Opcode { - op_special = 0 << OpcodeShift, - op_regimm = 1 << OpcodeShift, - - op_j = 2 << OpcodeShift, - op_jal = 3 << OpcodeShift, - op_beq = 4 << OpcodeShift, - op_bne = 5 << OpcodeShift, - op_blez = 6 << OpcodeShift, - op_bgtz = 7 << OpcodeShift, - - op_addi = 8 << OpcodeShift, - op_addiu = 9 << OpcodeShift, - op_slti = 10 << OpcodeShift, - op_sltiu = 11 << OpcodeShift, - op_andi = 12 << OpcodeShift, - op_ori = 13 << OpcodeShift, - op_xori = 14 << OpcodeShift, - op_lui = 15 << OpcodeShift, - - op_cop1 = 17 << OpcodeShift, - op_cop1x = 19 << OpcodeShift, - - op_beql = 20 << OpcodeShift, - op_bnel = 21 << OpcodeShift, - op_blezl = 22 << OpcodeShift, - op_bgtzl = 23 << OpcodeShift, - - op_special2 = 28 << OpcodeShift, - op_special3 = 31 << OpcodeShift, - - op_lb = 32 << OpcodeShift, - op_lh = 33 << OpcodeShift, - op_lwl = 34 << OpcodeShift, - op_lw = 35 << OpcodeShift, - op_lbu = 36 << OpcodeShift, - op_lhu = 37 << OpcodeShift, - op_lwr = 38 << OpcodeShift, - op_sb = 40 << OpcodeShift, - op_sh = 41 << OpcodeShift, - op_swl = 42 << OpcodeShift, - op_sw = 43 << OpcodeShift, - op_swr = 46 << OpcodeShift, - - op_lwc1 = 49 << OpcodeShift, - op_ldc1 = 53 << OpcodeShift, - - op_swc1 = 57 << OpcodeShift, - op_sdc1 = 61 << OpcodeShift -}; - -enum RSField { - rs_zero = 0 << RSShift, - // cop1 encoding of RS field. - rs_mfc1 = 0 << RSShift, - rs_one = 1 << RSShift, - rs_cfc1 = 2 << RSShift, - rs_mfhc1 = 3 << RSShift, - rs_mtc1 = 4 << RSShift, - rs_ctc1 = 6 << RSShift, - rs_mthc1 = 7 << RSShift, - rs_bc1 = 8 << RSShift, - rs_s = 16 << RSShift, - rs_d = 17 << RSShift, - rs_w = 20 << RSShift, - rs_l = 21 << RSShift, - rs_ps = 22 << RSShift -}; - -enum RTField { - rt_zero = 0 << RTShift, - // regimm encoding of RT field. - rt_bltz = 0 << RTShift, - rt_bgez = 1 << RTShift, - rt_bltzal = 16 << RTShift, - rt_bgezal = 17 << RTShift -}; - -enum FunctionField { - // special encoding of function field. - ff_sll = 0, - ff_movci = 1, - ff_srl = 2, - ff_sra = 3, - ff_sllv = 4, - ff_srlv = 6, - ff_srav = 7, - - ff_jr = 8, - ff_jalr = 9, - ff_movz = 10, - ff_movn = 11, - ff_break = 13, - - ff_mfhi = 16, - ff_mflo = 18, - - ff_mult = 24, - ff_multu = 25, - ff_div = 26, - ff_divu = 27, - - ff_add = 32, - ff_addu = 33, - ff_sub = 34, - ff_subu = 35, - ff_and = 36, - ff_or = 37, - ff_xor = 38, - ff_nor = 39, - - ff_slt = 42, - ff_sltu = 43, - - ff_tge = 48, - ff_tgeu = 49, - ff_tlt = 50, - ff_tltu = 51, - ff_teq = 52, - ff_tne = 54, - - // special2 encoding of function field. - ff_mul = 2, - ff_clz = 32, - ff_clo = 33, - - // special3 encoding of function field. - ff_ext = 0, - ff_ins = 4, - - // cop1 encoding of function field. - ff_add_fmt = 0, - ff_sub_fmt = 1, - ff_mul_fmt = 2, - ff_div_fmt = 3, - ff_sqrt_fmt = 4, - ff_abs_fmt = 5, - ff_mov_fmt = 6, - ff_neg_fmt = 7, - - ff_round_l_fmt = 8, - ff_trunc_l_fmt = 9, - ff_ceil_l_fmt = 10, - ff_floor_l_fmt = 11, - - ff_round_w_fmt = 12, - ff_trunc_w_fmt = 13, - ff_ceil_w_fmt = 14, - ff_floor_w_fmt = 15, - - ff_cvt_s_fmt = 32, - ff_cvt_d_fmt = 33, - ff_cvt_w_fmt = 36, - ff_cvt_l_fmt = 37, - ff_cvt_ps_s = 38, - - ff_c_f_fmt = 48, - ff_c_un_fmt = 49, - ff_c_eq_fmt = 50, - ff_c_ueq_fmt = 51, - ff_c_olt_fmt = 52, - ff_c_ult_fmt = 53, - ff_c_ole_fmt = 54, - ff_c_ule_fmt = 55, - - ff_madd_s = 32, - ff_madd_d = 33, - - ff_null = 0 -}; - -class Operand; - -// A BOffImm16 is a 16 bit immediate that is used for branches. -class BOffImm16 +class Assembler : public AssemblerMIPSShared { - uint32_t data; - - public: - uint32_t encode() { - MOZ_ASSERT(!isInvalid()); - return data; - } - int32_t decode() { - MOZ_ASSERT(!isInvalid()); - return (int32_t(data << 18) >> 16) + 4; - } - - explicit BOffImm16(int offset) - : data ((offset - 4) >> 2 & Imm16Mask) - { - MOZ_ASSERT((offset & 0x3) == 0); - MOZ_ASSERT(IsInRange(offset)); - } - static bool IsInRange(int offset) { - if ((offset - 4) < (INT16_MIN << 2)) - return false; - if ((offset - 4) > (INT16_MAX << 2)) - return false; - return true; - } - static const uint32_t INVALID = 0x00020000; - BOffImm16() - : data(INVALID) - { } - - bool isInvalid() { - return data == INVALID; - } - Instruction* getDest(Instruction* src); - - BOffImm16(InstImm inst); -}; - -// A JOffImm26 is a 26 bit immediate that is used for unconditional jumps. -class JOffImm26 -{ - uint32_t data; - - public: - uint32_t encode() { - MOZ_ASSERT(!isInvalid()); - return data; - } - int32_t decode() { - MOZ_ASSERT(!isInvalid()); - return (int32_t(data << 8) >> 6) + 4; - } - - explicit JOffImm26(int offset) - : data ((offset - 4) >> 2 & Imm26Mask) - { - MOZ_ASSERT((offset & 0x3) == 0); - MOZ_ASSERT(IsInRange(offset)); - } - static bool IsInRange(int offset) { - if ((offset - 4) < -536870912) - return false; - if ((offset - 4) > 536870908) - return false; - return true; - } - static const uint32_t INVALID = 0x20000000; - JOffImm26() - : data(INVALID) - { } - - bool isInvalid() { - return data == INVALID; - } - Instruction* getDest(Instruction* src); - -}; - -class Imm16 -{ - uint16_t value; - - public: - Imm16(); - Imm16(uint32_t imm) - : value(imm) - { } - uint32_t encode() { - return value; - } - int32_t decodeSigned() { - return value; - } - uint32_t decodeUnsigned() { - return value; - } - static bool IsInSignedRange(int32_t imm) { - return imm >= INT16_MIN && imm <= INT16_MAX; - } - static bool IsInUnsignedRange(uint32_t imm) { - return imm <= UINT16_MAX ; - } - static Imm16 Lower (Imm32 imm) { - return Imm16(imm.value & 0xffff); - } - static Imm16 Upper (Imm32 imm) { - return Imm16((imm.value >> 16) & 0xffff); - } -}; - -class Operand -{ - public: - enum Tag { - REG, - FREG, - MEM - }; - - private: - Tag tag : 3; - uint32_t reg : 5; - int32_t offset; - - public: - Operand (Register reg_) - : tag(REG), reg(reg_.code()) - { } - - Operand (FloatRegister freg) - : tag(FREG), reg(freg.code()) - { } - - Operand (Register base, Imm32 off) - : tag(MEM), reg(base.code()), offset(off.value) - { } - - Operand (Register base, int32_t off) - : tag(MEM), reg(base.code()), offset(off) - { } - - Operand (const Address& addr) - : tag(MEM), reg(addr.base.code()), offset(addr.offset) - { } - - Tag getTag() const { - return tag; - } - - Register toReg() const { - MOZ_ASSERT(tag == REG); - return Register::FromCode(reg); - } - - FloatRegister toFReg() const { - MOZ_ASSERT(tag == FREG); - return FloatRegister::FromCode(reg); - } - - void toAddr(Register* r, Imm32* dest) const { - MOZ_ASSERT(tag == MEM); - *r = Register::FromCode(reg); - *dest = Imm32(offset); - } - Address toAddress() const { - MOZ_ASSERT(tag == MEM); - return Address(Register::FromCode(reg), offset); - } - int32_t disp() const { - MOZ_ASSERT(tag == MEM); - return offset; - } - - int32_t base() const { - MOZ_ASSERT(tag == MEM); - return reg; - } - Register baseReg() const { - MOZ_ASSERT(tag == MEM); - return Register::FromCode(reg); - } -}; - -void -PatchJump(CodeLocationJump& jump_, CodeLocationLabel label, - ReprotectCode reprotect = DontReprotect); - -void -PatchBackedge(CodeLocationJump& jump_, CodeLocationLabel label, JitRuntime::BackedgeTarget target); - -class Assembler; -typedef js::jit::AssemblerBuffer<1024, Instruction> MIPSBuffer; - -class MIPSBufferWithExecutableCopy : public MIPSBuffer -{ - public: - void executableCopy(uint8_t* buffer) { - if (this->oom()) - return; - - for (Slice* cur = head; cur != nullptr; cur = cur->getNext()) { - memcpy(buffer, &cur->instructions, cur->length()); - buffer += cur->length(); - } - } -}; - -class Assembler : public AssemblerShared -{ - public: - - enum Condition { - Equal, - NotEqual, - Above, - AboveOrEqual, - Below, - BelowOrEqual, - GreaterThan, - GreaterThanOrEqual, - LessThan, - LessThanOrEqual, - Overflow, - Signed, - NotSigned, - Zero, - NonZero, - Always, - }; - - enum DoubleCondition { - // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. - DoubleOrdered, - DoubleEqual, - DoubleNotEqual, - DoubleGreaterThan, - DoubleGreaterThanOrEqual, - DoubleLessThan, - DoubleLessThanOrEqual, - // If either operand is NaN, these conditions always evaluate to true. - DoubleUnordered, - DoubleEqualOrUnordered, - DoubleNotEqualOrUnordered, - DoubleGreaterThanOrUnordered, - DoubleGreaterThanOrEqualOrUnordered, - DoubleLessThanOrUnordered, - DoubleLessThanOrEqualOrUnordered - }; - - enum FPConditionBit { - FCC0 = 0, - FCC1, - FCC2, - FCC3, - FCC4, - FCC5, - FCC6, - FCC7 - }; - - enum FloatFormat { - SingleFloat, - DoubleFloat - }; - - enum JumpOrCall { - BranchIsJump, - BranchIsCall - }; - - enum FloatTestKind { - TestForTrue, - TestForFalse - }; - - // :( this should be protected, but since CodeGenerator - // wants to use it, It needs to go out here :( - - BufferOffset nextOffset() { - return m_buffer.nextOffset(); - } - - protected: - Instruction * editSrc (BufferOffset bo) { - return m_buffer.getInst(bo); - } - public: - uint32_t actualOffset(uint32_t) const; - uint32_t actualIndex(uint32_t) const; - static uint8_t* PatchableJumpAddress(JitCode* code, uint32_t index); - protected: - - // structure for fixing up pc-relative loads/jumps when a the machine code - // gets moved (executable copy, gc, etc.) - struct RelativePatch - { - // the offset within the code buffer where the value is loaded that - // we want to fix-up - BufferOffset offset; - void* target; - Relocation::Kind kind; - - RelativePatch(BufferOffset offset, void* target, Relocation::Kind kind) - : offset(offset), - target(target), - kind(kind) - { } - }; - - js::Vector codeLabels_; - js::Vector jumps_; - js::Vector longJumps_; - - CompactBufferWriter jumpRelocations_; - CompactBufferWriter dataRelocations_; - CompactBufferWriter preBarriers_; - - MIPSBufferWithExecutableCopy m_buffer; - public: Assembler() - : m_buffer(), - isFinished(false) + : AssemblerMIPSShared() { } - static Condition InvertCondition(Condition cond); - static DoubleCondition InvertCondition(DoubleCondition cond); - // MacroAssemblers hold onto gcthings, so they are traced by the GC. void trace(JSTracer* trc); - void writeRelocation(BufferOffset src) { - jumpRelocations_.writeUnsigned(src.getOffset()); - } - // As opposed to x86/x64 version, the data relocation has to be executed - // before to recover the pointer, and not after. - void writeDataRelocation(ImmGCPtr ptr) { - if (ptr.value) { - if (gc::IsInsideNursery(ptr.value)) - embedsNurseryPointers_ = true; - dataRelocations_.writeUnsigned(nextOffset().getOffset()); - } - } - void writePrebarrierOffset(CodeOffsetLabel label) { - preBarriers_.writeUnsigned(label.offset()); - } - - public: static uintptr_t GetPointer(uint8_t*); - bool oom() const; - - void setPrinter(Sprinter* sp) { - } - - static const Register getStackPointer() { - return StackPointer; - } - - private: - bool isFinished; - public: - void finish(); - void executableCopy(void* buffer); - void copyJumpRelocationTable(uint8_t* dest); - void copyDataRelocationTable(uint8_t* dest); - void copyPreBarrierTable(uint8_t* dest); - - void addCodeLabel(CodeLabel label); - size_t numCodeLabels() const { - return codeLabels_.length(); - } - CodeLabel codeLabel(size_t i) { - return codeLabels_[i]; - } - - // Size of the instruction stream, in bytes. - size_t size() const; - // Size of the jump relocation table, in bytes. - size_t jumpRelocationTableBytes() const; - size_t dataRelocationTableBytes() const; - size_t preBarrierTableBytes() const; - - // Size of the data table, in bytes. - size_t bytesNeeded() const; - - // Write a blob of binary into the instruction stream *OR* - // into a destination address. If dest is nullptr (the default), then the - // instruction gets written into the instruction stream. If dest is not null - // it is interpreted as a pointer to the location that we want the - // instruction to be written. - BufferOffset writeInst(uint32_t x, uint32_t* dest = nullptr); - // A static variant for the cases where we don't want to have an assembler - // object at all. Normally, you would use the dummy (nullptr) object. - static void WriteInstStatic(uint32_t x, uint32_t* dest); - - public: - BufferOffset haltingAlign(int alignment); - BufferOffset nopAlign(int alignment); - BufferOffset as_nop(); - - // Branch and jump instructions - BufferOffset as_bal(BOffImm16 off); - BufferOffset as_b(BOffImm16 off); - - InstImm getBranchCode(JumpOrCall jumpOrCall); - InstImm getBranchCode(Register s, Register t, Condition c); - InstImm getBranchCode(Register s, Condition c); - InstImm getBranchCode(FloatTestKind testKind, FPConditionBit fcc); - - BufferOffset as_j(JOffImm26 off); - BufferOffset as_jal(JOffImm26 off); - - BufferOffset as_jr(Register rs); - BufferOffset as_jalr(Register rs); - - // Arithmetic instructions - BufferOffset as_addu(Register rd, Register rs, Register rt); - BufferOffset as_addiu(Register rd, Register rs, int32_t j); - BufferOffset as_subu(Register rd, Register rs, Register rt); - BufferOffset as_mult(Register rs, Register rt); - BufferOffset as_multu(Register rs, Register rt); - BufferOffset as_div(Register rs, Register rt); - BufferOffset as_divu(Register rs, Register rt); - BufferOffset as_mul(Register rd, Register rs, Register rt); - - // Logical instructions - BufferOffset as_and(Register rd, Register rs, Register rt); - BufferOffset as_or(Register rd, Register rs, Register rt); - BufferOffset as_xor(Register rd, Register rs, Register rt); - BufferOffset as_nor(Register rd, Register rs, Register rt); - - BufferOffset as_andi(Register rd, Register rs, int32_t j); - BufferOffset as_ori(Register rd, Register rs, int32_t j); - BufferOffset as_xori(Register rd, Register rs, int32_t j); - BufferOffset as_lui(Register rd, int32_t j); - - // Shift instructions - // as_sll(zero, zero, x) instructions are reserved as nop - BufferOffset as_sll(Register rd, Register rt, uint16_t sa); - BufferOffset as_sllv(Register rd, Register rt, Register rs); - BufferOffset as_srl(Register rd, Register rt, uint16_t sa); - BufferOffset as_srlv(Register rd, Register rt, Register rs); - BufferOffset as_sra(Register rd, Register rt, uint16_t sa); - BufferOffset as_srav(Register rd, Register rt, Register rs); - BufferOffset as_rotr(Register rd, Register rt, uint16_t sa); - BufferOffset as_rotrv(Register rd, Register rt, Register rs); - - // Load and store instructions - BufferOffset as_lb(Register rd, Register rs, int16_t off); - BufferOffset as_lbu(Register rd, Register rs, int16_t off); - BufferOffset as_lh(Register rd, Register rs, int16_t off); - BufferOffset as_lhu(Register rd, Register rs, int16_t off); - BufferOffset as_lw(Register rd, Register rs, int16_t off); - BufferOffset as_lwl(Register rd, Register rs, int16_t off); - BufferOffset as_lwr(Register rd, Register rs, int16_t off); - BufferOffset as_sb(Register rd, Register rs, int16_t off); - BufferOffset as_sh(Register rd, Register rs, int16_t off); - BufferOffset as_sw(Register rd, Register rs, int16_t off); - BufferOffset as_swl(Register rd, Register rs, int16_t off); - BufferOffset as_swr(Register rd, Register rs, int16_t off); - - // Move from HI/LO register. - BufferOffset as_mfhi(Register rd); - BufferOffset as_mflo(Register rd); - - // Set on less than. - BufferOffset as_slt(Register rd, Register rs, Register rt); - BufferOffset as_sltu(Register rd, Register rs, Register rt); - BufferOffset as_slti(Register rd, Register rs, int32_t j); - BufferOffset as_sltiu(Register rd, Register rs, uint32_t j); - - // Conditional move. - BufferOffset as_movz(Register rd, Register rs, Register rt); - BufferOffset as_movn(Register rd, Register rs, Register rt); - BufferOffset as_movt(Register rd, Register rs, uint16_t cc = 0); - BufferOffset as_movf(Register rd, Register rs, uint16_t cc = 0); - - // Bit twiddling. - BufferOffset as_clz(Register rd, Register rs); - BufferOffset as_ins(Register rt, Register rs, uint16_t pos, uint16_t size); - BufferOffset as_ext(Register rt, Register rs, uint16_t pos, uint16_t size); - - // FP instructions - - // Use these two functions only when you are sure address is aligned. - // Otherwise, use ma_ld and ma_sd. - BufferOffset as_ld(FloatRegister fd, Register base, int32_t off); - BufferOffset as_sd(FloatRegister fd, Register base, int32_t off); - - BufferOffset as_ls(FloatRegister fd, Register base, int32_t off); - BufferOffset as_ss(FloatRegister fd, Register base, int32_t off); - - BufferOffset as_movs(FloatRegister fd, FloatRegister fs); - BufferOffset as_movd(FloatRegister fd, FloatRegister fs); - - BufferOffset as_mtc1(Register rt, FloatRegister fs); - BufferOffset as_mfc1(Register rt, FloatRegister fs); - protected: // This is used to access the odd register form the pair of single // precision registers that make one double register. @@ -979,128 +125,22 @@ class Assembler : public AssemblerShared } public: - // FP convert instructions - BufferOffset as_ceilws(FloatRegister fd, FloatRegister fs); - BufferOffset as_floorws(FloatRegister fd, FloatRegister fs); - BufferOffset as_roundws(FloatRegister fd, FloatRegister fs); - BufferOffset as_truncws(FloatRegister fd, FloatRegister fs); + using AssemblerMIPSShared::bind; + using AssemblerMIPSShared::PatchDataWithValueCheck; - BufferOffset as_ceilwd(FloatRegister fd, FloatRegister fs); - BufferOffset as_floorwd(FloatRegister fd, FloatRegister fs); - BufferOffset as_roundwd(FloatRegister fd, FloatRegister fs); - BufferOffset as_truncwd(FloatRegister fd, FloatRegister fs); - - BufferOffset as_cvtdl(FloatRegister fd, FloatRegister fs); - BufferOffset as_cvtds(FloatRegister fd, FloatRegister fs); - BufferOffset as_cvtdw(FloatRegister fd, FloatRegister fs); - BufferOffset as_cvtld(FloatRegister fd, FloatRegister fs); - BufferOffset as_cvtls(FloatRegister fd, FloatRegister fs); - BufferOffset as_cvtsd(FloatRegister fd, FloatRegister fs); - BufferOffset as_cvtsl(FloatRegister fd, FloatRegister fs); - BufferOffset as_cvtsw(FloatRegister fd, FloatRegister fs); - BufferOffset as_cvtwd(FloatRegister fd, FloatRegister fs); - BufferOffset as_cvtws(FloatRegister fd, FloatRegister fs); - - // FP arithmetic instructions - BufferOffset as_adds(FloatRegister fd, FloatRegister fs, FloatRegister ft); - BufferOffset as_addd(FloatRegister fd, FloatRegister fs, FloatRegister ft); - BufferOffset as_subs(FloatRegister fd, FloatRegister fs, FloatRegister ft); - BufferOffset as_subd(FloatRegister fd, FloatRegister fs, FloatRegister ft); - - BufferOffset as_abss(FloatRegister fd, FloatRegister fs); - BufferOffset as_absd(FloatRegister fd, FloatRegister fs); - BufferOffset as_negs(FloatRegister fd, FloatRegister fs); - BufferOffset as_negd(FloatRegister fd, FloatRegister fs); - - BufferOffset as_muls(FloatRegister fd, FloatRegister fs, FloatRegister ft); - BufferOffset as_muld(FloatRegister fd, FloatRegister fs, FloatRegister ft); - BufferOffset as_divs(FloatRegister fd, FloatRegister fs, FloatRegister ft); - BufferOffset as_divd(FloatRegister fd, FloatRegister fs, FloatRegister ft); - BufferOffset as_sqrts(FloatRegister fd, FloatRegister fs); - BufferOffset as_sqrtd(FloatRegister fd, FloatRegister fs); - - // FP compare instructions - BufferOffset as_cf(FloatFormat fmt, FloatRegister fs, FloatRegister ft, - FPConditionBit fcc = FCC0); - BufferOffset as_cun(FloatFormat fmt, FloatRegister fs, FloatRegister ft, - FPConditionBit fcc = FCC0); - BufferOffset as_ceq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, - FPConditionBit fcc = FCC0); - BufferOffset as_cueq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, - FPConditionBit fcc = FCC0); - BufferOffset as_colt(FloatFormat fmt, FloatRegister fs, FloatRegister ft, - FPConditionBit fcc = FCC0); - BufferOffset as_cult(FloatFormat fmt, FloatRegister fs, FloatRegister ft, - FPConditionBit fcc = FCC0); - BufferOffset as_cole(FloatFormat fmt, FloatRegister fs, FloatRegister ft, - FPConditionBit fcc = FCC0); - BufferOffset as_cule(FloatFormat fmt, FloatRegister fs, FloatRegister ft, - FPConditionBit fcc = FCC0); - - // label operations - void bind(Label* label, BufferOffset boff = BufferOffset()); void bind(RepatchLabel* label); - uint32_t currentOffset() { - return nextOffset().getOffset(); - } - void retarget(Label* label, Label* target); void Bind(uint8_t* rawCode, AbsoluteLabel* label, const void* address); - // See Bind - size_t labelOffsetToPatchOffset(size_t offset) { - return actualOffset(offset); - } - - void call(Label* label); - void call(void* target); - - void as_break(uint32_t code); - - public: static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader); static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader); - static bool SupportsFloatingPoint() { -#if (defined(__mips_hard_float) && !defined(__mips_single_float)) || defined(JS_SIMULATOR_MIPS32) - return true; -#else - return false; -#endif - } - static bool SupportsSimd() { - return js::jit::SupportsSimd; - } - - protected: - InstImm invertBranch(InstImm branch, BOffImm16 skipOffset); void bind(InstImm* inst, uint32_t branch, uint32_t target); - void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) { - enoughMemory_ &= jumps_.append(RelativePatch(src, target.value, kind)); - if (kind == Relocation::JITCODE) - writeRelocation(src); - } - - void addLongJump(BufferOffset src) { - enoughMemory_ &= longJumps_.append(src.getOffset()); - } - - public: - size_t numLongJumps() const { - return longJumps_.length(); - } - uint32_t longJump(size_t i) { - return longJumps_[i]; - } // Copy the assembly code to the given buffer, and perform any pending // relocations relying on the target address. void executableCopy(uint8_t* buffer); - void flushBuffer() { - } - static uint32_t PatchWrite_NearCallSize(); - static uint32_t NopSize() { return 4; } static uint32_t ExtractLuiOriValue(Instruction* inst0, Instruction* inst1); static void UpdateLuiOriValue(Instruction* inst0, Instruction* inst1, uint32_t value); @@ -1110,277 +150,15 @@ class Assembler : public AssemblerShared static void PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall); static void PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue, PatchedImmPtr expectedValue); - static void PatchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, - ImmPtr expectedValue); - static void PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm); static void PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm); - static uint32_t AlignDoubleArg(uint32_t offset) { - return (offset + 1U) &~ 1U; - } - - static uint8_t* NextInstruction(uint8_t* instruction, uint32_t* count = nullptr); - - static void ToggleToJmp(CodeLocationLabel inst_); - static void ToggleToCmp(CodeLocationLabel inst_); - static void ToggleCall(CodeLocationLabel inst_, bool enabled); static void UpdateBoundsCheck(uint32_t logHeapSize, Instruction* inst); - void processCodeLabels(uint8_t* rawCode); static int32_t ExtractCodeLabelOffset(uint8_t* code); - - bool bailed() { - return m_buffer.bail(); - } - - void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, - const Disassembler::HeapAccess& heapAccess) - { - // Implement this if we implement a disassembler. - } }; // Assembler -// sll zero, zero, 0 -const uint32_t NopInst = 0x00000000; - -// An Instruction is a structure for both encoding and decoding any and all -// MIPS instructions. -class Instruction -{ - protected: - uint32_t data; - - // Standard constructor - Instruction (uint32_t data_) : data(data_) { } - - // You should never create an instruction directly. You should create a - // more specific instruction which will eventually call one of these - // constructors for you. - public: - uint32_t encode() const { - return data; - } - - void makeNop() { - data = NopInst; - } - - void setData(uint32_t data) { - this->data = data; - } - - const Instruction & operator=(const Instruction& src) { - data = src.data; - return *this; - } - - // Extract the one particular bit. - uint32_t extractBit(uint32_t bit) { - return (encode() >> bit) & 1; - } - // Extract a bit field out of the instruction - uint32_t extractBitField(uint32_t hi, uint32_t lo) { - return (encode() >> lo) & ((2 << (hi - lo)) - 1); - } - // Since all MIPS instructions have opcode, the opcode - // extractor resides in the base class. - uint32_t extractOpcode() { - return extractBitField(OpcodeShift + OpcodeBits - 1, OpcodeShift); - } - // Return the fields at their original place in the instruction encoding. - Opcode OpcodeFieldRaw() const { - return static_cast(encode() & OpcodeMask); - } - - // Get the next instruction in the instruction stream. - // This does neat things like ignoreconstant pools and their guards. - Instruction* next(); - - // Sometimes, an api wants a uint32_t (or a pointer to it) rather than - // an instruction. raw() just coerces this into a pointer to a uint32_t - const uint32_t* raw() const { return &data; } - uint32_t size() const { return 4; } -}; // Instruction - -// make sure that it is the right size -static_assert(sizeof(Instruction) == 4, "Size of Instruction class has to be 4 bytes."); - -class InstNOP : public Instruction -{ - public: - InstNOP() - : Instruction(NopInst) - { } - -}; - -// Class for register type instructions. -class InstReg : public Instruction -{ - public: - InstReg(Opcode op, Register rd, FunctionField ff) - : Instruction(op | RD(rd) | ff) - { } - InstReg(Opcode op, Register rs, Register rt, FunctionField ff) - : Instruction(op | RS(rs) | RT(rt) | ff) - { } - InstReg(Opcode op, Register rs, Register rt, Register rd, FunctionField ff) - : Instruction(op | RS(rs) | RT(rt) | RD(rd) | ff) - { } - InstReg(Opcode op, Register rs, Register rt, Register rd, uint32_t sa, FunctionField ff) - : Instruction(op | RS(rs) | RT(rt) | RD(rd) | SA(sa) | ff) - { } - InstReg(Opcode op, RSField rs, Register rt, Register rd, uint32_t sa, FunctionField ff) - : Instruction(op | rs | RT(rt) | RD(rd) | SA(sa) | ff) - { } - InstReg(Opcode op, Register rs, RTField rt, Register rd, uint32_t sa, FunctionField ff) - : Instruction(op | RS(rs) | rt | RD(rd) | SA(sa) | ff) - { } - InstReg(Opcode op, Register rs, uint32_t cc, Register rd, uint32_t sa, FunctionField ff) - : Instruction(op | RS(rs) | cc | RD(rd) | SA(sa) | ff) - { } - InstReg(Opcode op, uint32_t code, FunctionField ff) - : Instruction(op | code | ff) - { } - // for float point - InstReg(Opcode op, RSField rs, Register rt, FloatRegister rd) - : Instruction(op | rs | RT(rt) | RD(rd)) - { } - InstReg(Opcode op, RSField rs, Register rt, FloatRegister rd, uint32_t sa, FunctionField ff) - : Instruction(op | rs | RT(rt) | RD(rd) | SA(sa) | ff) - { } - InstReg(Opcode op, RSField rs, Register rt, FloatRegister fs, FloatRegister fd, FunctionField ff) - : Instruction(op | rs | RT(rt) | RD(fs) | SA(fd) | ff) - { } - InstReg(Opcode op, RSField rs, FloatRegister ft, FloatRegister fs, FloatRegister fd, FunctionField ff) - : Instruction(op | rs | RT(ft) | RD(fs) | SA(fd) | ff) - { } - InstReg(Opcode op, RSField rs, FloatRegister ft, FloatRegister fd, uint32_t sa, FunctionField ff) - : Instruction(op | rs | RT(ft) | RD(fd) | SA(sa) | ff) - { } - - uint32_t extractRS () { - return extractBitField(RSShift + RSBits - 1, RSShift); - } - uint32_t extractRT () { - return extractBitField(RTShift + RTBits - 1, RTShift); - } - uint32_t extractRD () { - return extractBitField(RDShift + RDBits - 1, RDShift); - } - uint32_t extractSA () { - return extractBitField(SAShift + SABits - 1, SAShift); - } - uint32_t extractFunctionField () { - return extractBitField(FunctionShift + FunctionBits - 1, FunctionShift); - } -}; - -// Class for branch, load and store instructions with immediate offset. -class InstImm : public Instruction -{ - public: - void extractImm16(BOffImm16* dest); - - InstImm(Opcode op, Register rs, Register rt, BOffImm16 off) - : Instruction(op | RS(rs) | RT(rt) | off.encode()) - { } - InstImm(Opcode op, Register rs, RTField rt, BOffImm16 off) - : Instruction(op | RS(rs) | rt | off.encode()) - { } - InstImm(Opcode op, RSField rs, uint32_t cc, BOffImm16 off) - : Instruction(op | rs | cc | off.encode()) - { } - InstImm(Opcode op, Register rs, Register rt, Imm16 off) - : Instruction(op | RS(rs) | RT(rt) | off.encode()) - { } - InstImm(uint32_t raw) - : Instruction(raw) - { } - // For floating-point loads and stores. - InstImm(Opcode op, Register rs, FloatRegister rt, Imm16 off) - : Instruction(op | RS(rs) | RT(rt) | off.encode()) - { } - - uint32_t extractOpcode() { - return extractBitField(OpcodeShift + OpcodeBits - 1, OpcodeShift); - } - void setOpcode(Opcode op) { - data = (data & ~OpcodeMask) | op; - } - uint32_t extractRS() { - return extractBitField(RSShift + RSBits - 1, RSShift); - } - uint32_t extractRT() { - return extractBitField(RTShift + RTBits - 1, RTShift); - } - void setRT(RTField rt) { - data = (data & ~RTMask) | rt; - } - uint32_t extractImm16Value() { - return extractBitField(Imm16Shift + Imm16Bits - 1, Imm16Shift); - } - void setBOffImm16(BOffImm16 off) { - // Reset immediate field and replace it - data = (data & ~Imm16Mask) | off.encode(); - } - void setImm16(Imm16 off) { - // Reset immediate field and replace it - data = (data & ~Imm16Mask) | off.encode(); - } -}; - -// Class for Jump type instructions. -class InstJump : public Instruction -{ - public: - InstJump(Opcode op, JOffImm26 off) - : Instruction(op | off.encode()) - { } - - uint32_t extractImm26Value() { - return extractBitField(Imm26Shift + Imm26Bits - 1, Imm26Shift); - } -}; - -static const uint32_t NumIntArgRegs = 4; - -static inline bool -GetIntArgReg(uint32_t usedArgSlots, Register* out) -{ - if (usedArgSlots < NumIntArgRegs) { - *out = Register::FromCode(a0.code() + usedArgSlots); - return true; - } - return false; -} - -// Get a register in which we plan to put a quantity that will be used as an -// integer argument. This differs from GetIntArgReg in that if we have no more -// actual argument registers to use we will fall back on using whatever -// CallTempReg* don't overlap the argument registers, and only fail once those -// run out too. -static inline bool -GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register* out) -{ - // NOTE: We can't properly determine which regs are used if there are - // float arguments. If this is needed, we will have to guess. - MOZ_ASSERT(usedFloatArgs == 0); - - if (GetIntArgReg(usedIntArgs, out)) - return true; - // Unfortunately, we have to assume things about the point at which - // GetIntArgReg returns false, because we need to know how many registers it - // can allocate. - usedIntArgs -= NumIntArgRegs; - if (usedIntArgs >= NumCallTempNonArgRegs) - return false; - *out = CallTempNonArgRegs[usedIntArgs]; - return true; -} - static inline uint32_t GetArgStackDisp(uint32_t usedArgSlots) { diff --git a/js/src/moz.build b/js/src/moz.build index 0a48aa9edf58..09ec6d54be22 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -467,6 +467,7 @@ elif CONFIG['JS_CODEGEN_ARM64']: elif CONFIG['JS_CODEGEN_MIPS32']: UNIFIED_SOURCES += [ 'jit/mips-shared/Architecture-mips-shared.cpp', + 'jit/mips-shared/Assembler-mips-shared.cpp', ] if CONFIG['JS_CODEGEN_MIPS32']: UNIFIED_SOURCES += [