michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jit/mips/Assembler-mips.h" michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: #include "mozilla/MathAlgorithms.h" michael@0: michael@0: #include "jscompartment.h" michael@0: #include "jsutil.h" michael@0: michael@0: #include "assembler/jit/ExecutableAllocator.h" michael@0: #include "gc/Marking.h" michael@0: #include "jit/JitCompartment.h" michael@0: michael@0: using mozilla::DebugOnly; michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: ABIArgGenerator::ABIArgGenerator() michael@0: : usedArgSlots_(0), michael@0: firstArgFloat(false), michael@0: current_() michael@0: {} michael@0: michael@0: ABIArg michael@0: ABIArgGenerator::next(MIRType type) michael@0: { michael@0: MOZ_ASSUME_UNREACHABLE("NYI"); michael@0: return ABIArg(); michael@0: } michael@0: const Register ABIArgGenerator::NonArgReturnVolatileReg0 = t0; michael@0: const Register ABIArgGenerator::NonArgReturnVolatileReg1 = t1; michael@0: michael@0: // Encode a standard register when it is being used as rd, the rs, and michael@0: // an extra register(rt). These should never be called with an InvalidReg. michael@0: uint32_t michael@0: js::jit::RS(Register r) michael@0: { michael@0: JS_ASSERT((r.code() & ~RegMask) == 0); michael@0: return r.code() << RSShift; michael@0: } michael@0: michael@0: uint32_t michael@0: js::jit::RT(Register r) michael@0: { michael@0: JS_ASSERT((r.code() & ~RegMask) == 0); michael@0: return r.code() << RTShift; michael@0: } michael@0: michael@0: uint32_t michael@0: js::jit::RT(FloatRegister r) michael@0: { michael@0: JS_ASSERT(r.code() < FloatRegisters::Total); michael@0: return r.code() << RTShift; michael@0: } michael@0: michael@0: uint32_t michael@0: js::jit::RD(Register r) michael@0: { michael@0: JS_ASSERT((r.code() & ~RegMask) == 0); michael@0: return r.code() << RDShift; michael@0: } michael@0: michael@0: uint32_t michael@0: js::jit::RD(FloatRegister r) michael@0: { michael@0: JS_ASSERT(r.code() < FloatRegisters::Total); michael@0: return r.code() << RDShift; michael@0: } michael@0: michael@0: uint32_t michael@0: js::jit::SA(uint32_t value) michael@0: { michael@0: JS_ASSERT(value < 32); michael@0: return value << SAShift; michael@0: } michael@0: michael@0: uint32_t michael@0: js::jit::SA(FloatRegister r) michael@0: { michael@0: JS_ASSERT(r.code() < FloatRegisters::Total); michael@0: return r.code() << SAShift; michael@0: } michael@0: michael@0: Register michael@0: js::jit::toRS(Instruction &i) michael@0: { michael@0: return Register::FromCode((i.encode() & RSMask ) >> RSShift); michael@0: } michael@0: michael@0: Register michael@0: js::jit::toRT(Instruction &i) michael@0: { michael@0: return Register::FromCode((i.encode() & RTMask ) >> RTShift); michael@0: } michael@0: michael@0: Register michael@0: js::jit::toRD(Instruction &i) michael@0: { michael@0: return Register::FromCode((i.encode() & RDMask ) >> RDShift); michael@0: } michael@0: michael@0: Register michael@0: js::jit::toR(Instruction &i) michael@0: { michael@0: return Register::FromCode(i.encode() & RegMask); michael@0: } michael@0: michael@0: void michael@0: InstImm::extractImm16(BOffImm16 *dest) michael@0: { michael@0: *dest = BOffImm16(*this); michael@0: } michael@0: michael@0: // Used to patch jumps created by MacroAssemblerMIPSCompat::jumpWithPatch. michael@0: void michael@0: jit::PatchJump(CodeLocationJump &jump_, CodeLocationLabel label) michael@0: { michael@0: Instruction *inst1 = (Instruction *)jump_.raw(); michael@0: Instruction *inst2 = inst1->next(); michael@0: michael@0: Assembler::updateLuiOriValue(inst1, inst2, (uint32_t)label.raw()); michael@0: michael@0: AutoFlushICache::flush(uintptr_t(inst1), 8); michael@0: } michael@0: michael@0: void michael@0: Assembler::finish() michael@0: { michael@0: JS_ASSERT(!isFinished); michael@0: isFinished = true; michael@0: } michael@0: michael@0: void michael@0: Assembler::executableCopy(uint8_t *buffer) michael@0: { michael@0: JS_ASSERT(isFinished); michael@0: m_buffer.executableCopy(buffer); michael@0: michael@0: // Patch all long jumps during code copy. michael@0: for (size_t i = 0; i < longJumps_.length(); i++) { michael@0: Instruction *inst1 = (Instruction *) ((uint32_t)buffer + longJumps_[i]); michael@0: michael@0: uint32_t value = extractLuiOriValue(inst1, inst1->next()); michael@0: updateLuiOriValue(inst1, inst1->next(), (uint32_t)buffer + value); michael@0: } michael@0: michael@0: AutoFlushICache::setRange(uintptr_t(buffer), m_buffer.size()); michael@0: } michael@0: michael@0: uint32_t michael@0: Assembler::actualOffset(uint32_t off_) const michael@0: { michael@0: return off_; michael@0: } michael@0: michael@0: uint32_t michael@0: Assembler::actualIndex(uint32_t idx_) const michael@0: { michael@0: return idx_; michael@0: } michael@0: michael@0: uint8_t * michael@0: Assembler::PatchableJumpAddress(JitCode *code, uint32_t pe_) michael@0: { michael@0: return code->raw() + pe_; michael@0: } michael@0: michael@0: class RelocationIterator michael@0: { michael@0: CompactBufferReader reader_; michael@0: // offset in bytes michael@0: uint32_t offset_; michael@0: michael@0: public: michael@0: RelocationIterator(CompactBufferReader &reader) michael@0: : reader_(reader) michael@0: { } michael@0: michael@0: bool read() { michael@0: if (!reader_.more()) michael@0: return false; michael@0: offset_ = reader_.readUnsigned(); michael@0: return true; michael@0: } michael@0: michael@0: uint32_t offset() const { michael@0: return offset_; michael@0: } michael@0: }; michael@0: michael@0: uintptr_t michael@0: Assembler::getPointer(uint8_t *instPtr) michael@0: { michael@0: Instruction *inst = (Instruction*)instPtr; michael@0: return Assembler::extractLuiOriValue(inst, inst->next()); michael@0: } michael@0: michael@0: static JitCode * michael@0: CodeFromJump(Instruction *jump) michael@0: { michael@0: uint8_t *target = (uint8_t *)Assembler::extractLuiOriValue(jump, jump->next()); michael@0: return JitCode::FromExecutable(target); michael@0: } michael@0: michael@0: void michael@0: Assembler::TraceJumpRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader) michael@0: { michael@0: RelocationIterator iter(reader); michael@0: while (iter.read()) { michael@0: JitCode *child = CodeFromJump((Instruction *)(code->raw() + iter.offset())); michael@0: MarkJitCodeUnbarriered(trc, &child, "rel32"); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: TraceDataRelocations(JSTracer *trc, uint8_t *buffer, CompactBufferReader &reader) michael@0: { michael@0: while (reader.more()) { michael@0: size_t offset = reader.readUnsigned(); michael@0: Instruction *inst = (Instruction*)(buffer + offset); michael@0: void *ptr = (void *)Assembler::extractLuiOriValue(inst, inst->next()); michael@0: michael@0: // No barrier needed since these are constants. michael@0: gc::MarkGCThingUnbarriered(trc, reinterpret_cast(&ptr), "ion-masm-ptr"); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: TraceDataRelocations(JSTracer *trc, MIPSBuffer *buffer, CompactBufferReader &reader) michael@0: { michael@0: while (reader.more()) { michael@0: BufferOffset bo (reader.readUnsigned()); michael@0: MIPSBuffer::AssemblerBufferInstIterator iter(bo, buffer); michael@0: michael@0: void *ptr = (void *)Assembler::extractLuiOriValue(iter.cur(), iter.next()); michael@0: michael@0: // No barrier needed since these are constants. michael@0: gc::MarkGCThingUnbarriered(trc, reinterpret_cast(&ptr), "ion-masm-ptr"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: Assembler::TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader) michael@0: { michael@0: ::TraceDataRelocations(trc, code->raw(), reader); michael@0: } michael@0: michael@0: void michael@0: Assembler::copyJumpRelocationTable(uint8_t *dest) michael@0: { michael@0: if (jumpRelocations_.length()) michael@0: memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length()); michael@0: } michael@0: michael@0: void michael@0: Assembler::copyDataRelocationTable(uint8_t *dest) michael@0: { michael@0: if (dataRelocations_.length()) michael@0: memcpy(dest, dataRelocations_.buffer(), dataRelocations_.length()); michael@0: } michael@0: michael@0: void michael@0: Assembler::copyPreBarrierTable(uint8_t *dest) michael@0: { michael@0: if (preBarriers_.length()) michael@0: memcpy(dest, preBarriers_.buffer(), preBarriers_.length()); michael@0: } michael@0: michael@0: void michael@0: Assembler::trace(JSTracer *trc) michael@0: { michael@0: for (size_t i = 0; i < jumps_.length(); i++) { michael@0: RelativePatch &rp = jumps_[i]; michael@0: if (rp.kind == Relocation::JITCODE) { michael@0: JitCode *code = JitCode::FromExecutable((uint8_t *)rp.target); michael@0: MarkJitCodeUnbarriered(trc, &code, "masmrel32"); michael@0: JS_ASSERT(code == JitCode::FromExecutable((uint8_t *)rp.target)); michael@0: } michael@0: } michael@0: if (dataRelocations_.length()) { michael@0: CompactBufferReader reader(dataRelocations_); michael@0: ::TraceDataRelocations(trc, &m_buffer, reader); michael@0: } michael@0: } michael@0: michael@0: void michael@0: Assembler::processCodeLabels(uint8_t *rawCode) michael@0: { michael@0: for (size_t i = 0; i < codeLabels_.length(); i++) { michael@0: CodeLabel label = codeLabels_[i]; michael@0: Bind(rawCode, label.dest(), rawCode + actualOffset(label.src()->offset())); michael@0: } michael@0: } michael@0: michael@0: void michael@0: Assembler::Bind(uint8_t *rawCode, AbsoluteLabel *label, const void *address) michael@0: { michael@0: if (label->used()) { michael@0: int32_t src = label->offset(); michael@0: do { michael@0: Instruction *inst = (Instruction *) (rawCode + src); michael@0: uint32_t next = Assembler::extractLuiOriValue(inst, inst->next()); michael@0: Assembler::updateLuiOriValue(inst, inst->next(), (uint32_t)address); michael@0: src = next; michael@0: } while (src != AbsoluteLabel::INVALID_OFFSET); michael@0: } michael@0: label->bind(); michael@0: } michael@0: michael@0: Assembler::Condition michael@0: Assembler::InvertCondition(Condition cond) michael@0: { michael@0: switch (cond) { michael@0: case Equal: michael@0: return NotEqual; michael@0: case NotEqual: michael@0: return Equal; michael@0: case Zero: michael@0: return NonZero; michael@0: case NonZero: michael@0: return Zero; michael@0: case LessThan: michael@0: return GreaterThanOrEqual; michael@0: case LessThanOrEqual: michael@0: return GreaterThan; michael@0: case GreaterThan: michael@0: return LessThanOrEqual; michael@0: case GreaterThanOrEqual: michael@0: return LessThan; michael@0: case Above: michael@0: return BelowOrEqual; michael@0: case AboveOrEqual: michael@0: return Below; michael@0: case Below: michael@0: return AboveOrEqual; michael@0: case BelowOrEqual: michael@0: return Above; michael@0: case Signed: michael@0: return NotSigned; michael@0: case NotSigned: michael@0: return Signed; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected condition"); michael@0: return Equal; michael@0: } michael@0: } michael@0: michael@0: Assembler::DoubleCondition michael@0: Assembler::InvertCondition(DoubleCondition cond) michael@0: { michael@0: switch (cond) { michael@0: case DoubleOrdered: michael@0: return DoubleUnordered; michael@0: case DoubleEqual: michael@0: return DoubleNotEqualOrUnordered; michael@0: case DoubleNotEqual: michael@0: return DoubleEqualOrUnordered; michael@0: case DoubleGreaterThan: michael@0: return DoubleLessThanOrEqualOrUnordered; michael@0: case DoubleGreaterThanOrEqual: michael@0: return DoubleLessThanOrUnordered; michael@0: case DoubleLessThan: michael@0: return DoubleGreaterThanOrEqualOrUnordered; michael@0: case DoubleLessThanOrEqual: michael@0: return DoubleGreaterThanOrUnordered; michael@0: case DoubleUnordered: michael@0: return DoubleOrdered; michael@0: case DoubleEqualOrUnordered: michael@0: return DoubleNotEqual; michael@0: case DoubleNotEqualOrUnordered: michael@0: return DoubleEqual; michael@0: case DoubleGreaterThanOrUnordered: michael@0: return DoubleLessThanOrEqual; michael@0: case DoubleGreaterThanOrEqualOrUnordered: michael@0: return DoubleLessThan; michael@0: case DoubleLessThanOrUnordered: michael@0: return DoubleGreaterThanOrEqual; michael@0: case DoubleLessThanOrEqualOrUnordered: michael@0: return DoubleGreaterThan; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unexpected condition"); michael@0: return DoubleEqual; michael@0: } michael@0: } michael@0: michael@0: BOffImm16::BOffImm16(InstImm inst) michael@0: : data(inst.encode() & Imm16Mask) michael@0: { michael@0: } michael@0: michael@0: bool michael@0: Assembler::oom() const michael@0: { michael@0: return m_buffer.oom() || michael@0: !enoughMemory_ || michael@0: jumpRelocations_.oom() || michael@0: dataRelocations_.oom() || michael@0: preBarriers_.oom(); michael@0: } michael@0: michael@0: bool michael@0: Assembler::addCodeLabel(CodeLabel label) michael@0: { michael@0: return codeLabels_.append(label); michael@0: } michael@0: michael@0: // Size of the instruction stream, in bytes. michael@0: size_t michael@0: Assembler::size() const michael@0: { michael@0: return m_buffer.size(); michael@0: } michael@0: michael@0: // Size of the relocation table, in bytes. michael@0: size_t michael@0: Assembler::jumpRelocationTableBytes() const michael@0: { michael@0: return jumpRelocations_.length(); michael@0: } michael@0: michael@0: size_t michael@0: Assembler::dataRelocationTableBytes() const michael@0: { michael@0: return dataRelocations_.length(); michael@0: } michael@0: michael@0: size_t michael@0: Assembler::preBarrierTableBytes() const michael@0: { michael@0: return preBarriers_.length(); michael@0: } michael@0: michael@0: // Size of the data table, in bytes. michael@0: size_t michael@0: Assembler::bytesNeeded() const michael@0: { michael@0: return size() + michael@0: jumpRelocationTableBytes() + michael@0: dataRelocationTableBytes() + michael@0: preBarrierTableBytes(); michael@0: } michael@0: michael@0: // write a blob of binary into the instruction stream michael@0: BufferOffset michael@0: Assembler::writeInst(uint32_t x, uint32_t *dest) michael@0: { michael@0: if (dest == nullptr) michael@0: return m_buffer.putInt(x); michael@0: michael@0: writeInstStatic(x, dest); michael@0: return BufferOffset(); michael@0: } michael@0: michael@0: void michael@0: Assembler::writeInstStatic(uint32_t x, uint32_t *dest) michael@0: { michael@0: JS_ASSERT(dest != nullptr); michael@0: *dest = x; michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::align(int alignment) michael@0: { michael@0: BufferOffset ret; michael@0: JS_ASSERT(m_buffer.isAligned(4)); michael@0: if (alignment == 8) { michael@0: if (!m_buffer.isAligned(alignment)) { michael@0: BufferOffset tmp = as_nop(); michael@0: if (!ret.assigned()) michael@0: ret = tmp; michael@0: } michael@0: } else { michael@0: JS_ASSERT((alignment & (alignment - 1)) == 0); michael@0: while (size() & (alignment - 1)) { michael@0: BufferOffset tmp = as_nop(); michael@0: if (!ret.assigned()) michael@0: ret = tmp; michael@0: } michael@0: } michael@0: return ret; michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_nop() michael@0: { michael@0: return writeInst(op_special | ff_sll); michael@0: } michael@0: michael@0: // Logical operations. michael@0: BufferOffset michael@0: Assembler::as_and(Register rd, Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, rd, ff_and).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_or(Register rd, Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, rd, ff_or).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_xor(Register rd, Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, rd, ff_xor).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_nor(Register rd, Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, rd, ff_nor).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_andi(Register rd, Register rs, int32_t j) michael@0: { michael@0: JS_ASSERT(Imm16::isInUnsignedRange(j)); michael@0: return writeInst(InstImm(op_andi, rs, rd, Imm16(j)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_ori(Register rd, Register rs, int32_t j) michael@0: { michael@0: JS_ASSERT(Imm16::isInUnsignedRange(j)); michael@0: return writeInst(InstImm(op_ori, rs, rd, Imm16(j)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_xori(Register rd, Register rs, int32_t j) michael@0: { michael@0: JS_ASSERT(Imm16::isInUnsignedRange(j)); michael@0: return writeInst(InstImm(op_xori, rs, rd, Imm16(j)).encode()); michael@0: } michael@0: michael@0: // Branch and jump instructions michael@0: BufferOffset michael@0: Assembler::as_bal(BOffImm16 off) michael@0: { michael@0: BufferOffset bo = writeInst(InstImm(op_regimm, zero, rt_bgezal, off).encode()); michael@0: return bo; michael@0: } michael@0: michael@0: InstImm michael@0: Assembler::getBranchCode(JumpOrCall jumpOrCall) michael@0: { michael@0: if (jumpOrCall == BranchIsCall) michael@0: return InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)); michael@0: michael@0: return InstImm(op_beq, zero, zero, BOffImm16(0)); michael@0: } michael@0: michael@0: InstImm michael@0: Assembler::getBranchCode(Register s, Register t, Condition c) michael@0: { michael@0: JS_ASSERT(c == Assembler::Equal || c == Assembler::NotEqual); michael@0: return InstImm(c == Assembler::Equal ? op_beq : op_bne, s, t, BOffImm16(0)); michael@0: } michael@0: michael@0: InstImm michael@0: Assembler::getBranchCode(Register s, Condition c) michael@0: { michael@0: switch (c) { michael@0: case Assembler::Equal: michael@0: case Assembler::Zero: michael@0: case Assembler::BelowOrEqual: michael@0: return InstImm(op_beq, s, zero, BOffImm16(0)); michael@0: case Assembler::NotEqual: michael@0: case Assembler::NonZero: michael@0: case Assembler::Above: michael@0: return InstImm(op_bne, s, zero, BOffImm16(0)); michael@0: case Assembler::GreaterThan: michael@0: return InstImm(op_bgtz, s, zero, BOffImm16(0)); michael@0: case Assembler::GreaterThanOrEqual: michael@0: case Assembler::NotSigned: michael@0: return InstImm(op_regimm, s, rt_bgez, BOffImm16(0)); michael@0: case Assembler::LessThan: michael@0: case Assembler::Signed: michael@0: return InstImm(op_regimm, s, rt_bltz, BOffImm16(0)); michael@0: case Assembler::LessThanOrEqual: michael@0: return InstImm(op_blez, s, zero, BOffImm16(0)); michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Condition not supported."); michael@0: } michael@0: } michael@0: michael@0: InstImm michael@0: Assembler::getBranchCode(FloatTestKind testKind, FPConditionBit fcc) michael@0: { michael@0: JS_ASSERT(!(fcc && FccMask)); michael@0: uint32_t rtField = ((testKind == TestForTrue ? 1 : 0) | (fcc << FccShift)) << RTShift; michael@0: michael@0: return InstImm(op_cop1, rs_bc1, rtField, BOffImm16(0)); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_j(JOffImm26 off) michael@0: { michael@0: BufferOffset bo = writeInst(InstJump(op_j, off).encode()); michael@0: return bo; michael@0: } michael@0: BufferOffset michael@0: Assembler::as_jal(JOffImm26 off) michael@0: { michael@0: BufferOffset bo = writeInst(InstJump(op_jal, off).encode()); michael@0: return bo; michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_jr(Register rs) michael@0: { michael@0: BufferOffset bo = writeInst(InstReg(op_special, rs, zero, zero, ff_jr).encode()); michael@0: return bo; michael@0: } michael@0: BufferOffset michael@0: Assembler::as_jalr(Register rs) michael@0: { michael@0: BufferOffset bo = writeInst(InstReg(op_special, rs, zero, ra, ff_jalr).encode()); michael@0: return bo; michael@0: } michael@0: michael@0: michael@0: // Arithmetic instructions michael@0: BufferOffset michael@0: Assembler::as_addu(Register rd, Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, rd, ff_addu).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_addiu(Register rd, Register rs, int32_t j) michael@0: { michael@0: JS_ASSERT(Imm16::isInSignedRange(j)); michael@0: return writeInst(InstImm(op_addiu, rs, rd, Imm16(j)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_subu(Register rd, Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, rd, ff_subu).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_mult(Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, ff_mult).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_multu(Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, ff_multu).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_div(Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, ff_div).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_divu(Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, ff_divu).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_mul(Register rd, Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special2, rs, rt, rd, ff_mul).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_lui(Register rd, int32_t j) michael@0: { michael@0: JS_ASSERT(Imm16::isInUnsignedRange(j)); michael@0: return writeInst(InstImm(op_lui, zero, rd, Imm16(j)).encode()); michael@0: } michael@0: michael@0: // Shift instructions michael@0: BufferOffset michael@0: Assembler::as_sll(Register rd, Register rt, uint16_t sa) michael@0: { michael@0: JS_ASSERT(sa < 32); michael@0: return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_sll).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_sllv(Register rd, Register rt, Register rs) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, rd, ff_sllv).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_srl(Register rd, Register rt, uint16_t sa) michael@0: { michael@0: JS_ASSERT(sa < 32); michael@0: return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_srl).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_srlv(Register rd, Register rt, Register rs) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, rd, ff_srlv).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_sra(Register rd, Register rt, uint16_t sa) michael@0: { michael@0: JS_ASSERT(sa < 32); michael@0: return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_sra).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_srav(Register rd, Register rt, Register rs) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, rd, ff_srav).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_rotr(Register rd, Register rt, uint16_t sa) michael@0: { michael@0: JS_ASSERT(sa < 32); michael@0: return writeInst(InstReg(op_special, rs_one, rt, rd, sa, ff_srl).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_rotrv(Register rd, Register rt, Register rs) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, rd, 1, ff_srlv).encode()); michael@0: } michael@0: michael@0: // Load and store instructions michael@0: BufferOffset michael@0: Assembler::as_lb(Register rd, Register rs, int16_t off) michael@0: { michael@0: return writeInst(InstImm(op_lb, rs, rd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_lbu(Register rd, Register rs, int16_t off) michael@0: { michael@0: return writeInst(InstImm(op_lbu, rs, rd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_lh(Register rd, Register rs, int16_t off) michael@0: { michael@0: return writeInst(InstImm(op_lh, rs, rd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_lhu(Register rd, Register rs, int16_t off) michael@0: { michael@0: return writeInst(InstImm(op_lhu, rs, rd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_lw(Register rd, Register rs, int16_t off) michael@0: { michael@0: return writeInst(InstImm(op_lw, rs, rd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_lwl(Register rd, Register rs, int16_t off) michael@0: { michael@0: return writeInst(InstImm(op_lwl, rs, rd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_lwr(Register rd, Register rs, int16_t off) michael@0: { michael@0: return writeInst(InstImm(op_lwr, rs, rd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_sb(Register rd, Register rs, int16_t off) michael@0: { michael@0: return writeInst(InstImm(op_sb, rs, rd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_sh(Register rd, Register rs, int16_t off) michael@0: { michael@0: return writeInst(InstImm(op_sh, rs, rd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_sw(Register rd, Register rs, int16_t off) michael@0: { michael@0: return writeInst(InstImm(op_sw, rs, rd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_swl(Register rd, Register rs, int16_t off) michael@0: { michael@0: return writeInst(InstImm(op_swl, rs, rd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_swr(Register rd, Register rs, int16_t off) michael@0: { michael@0: return writeInst(InstImm(op_swr, rs, rd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: // Move from HI/LO register. michael@0: BufferOffset michael@0: Assembler::as_mfhi(Register rd) michael@0: { michael@0: return writeInst(InstReg(op_special, rd, ff_mfhi).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_mflo(Register rd) michael@0: { michael@0: return writeInst(InstReg(op_special, rd, ff_mflo).encode()); michael@0: } michael@0: michael@0: // Set on less than. michael@0: BufferOffset michael@0: Assembler::as_slt(Register rd, Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, rd, ff_slt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_sltu(Register rd, Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, rd, ff_sltu).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_slti(Register rd, Register rs, int32_t j) michael@0: { michael@0: JS_ASSERT(Imm16::isInSignedRange(j)); michael@0: return writeInst(InstImm(op_slti, rs, rd, Imm16(j)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_sltiu(Register rd, Register rs, uint32_t j) michael@0: { michael@0: JS_ASSERT(Imm16::isInUnsignedRange(j)); michael@0: return writeInst(InstImm(op_sltiu, rs, rd, Imm16(j)).encode()); michael@0: } michael@0: michael@0: // Conditional move. michael@0: BufferOffset michael@0: Assembler::as_movz(Register rd, Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, rd, ff_movz).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_movn(Register rd, Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special, rs, rt, rd, ff_movn).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_movt(Register rd, Register rs, uint16_t cc) michael@0: { michael@0: Register rt; michael@0: rt = Register::FromCode((cc & 0x7) << 2 | 1); michael@0: return writeInst(InstReg(op_special, rs, rt, rd, ff_movci).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_movf(Register rd, Register rs, uint16_t cc) michael@0: { michael@0: Register rt; michael@0: rt = Register::FromCode((cc & 0x7) << 2 | 0); michael@0: return writeInst(InstReg(op_special, rs, rt, rd, ff_movci).encode()); michael@0: } michael@0: michael@0: // Bit twiddling. michael@0: BufferOffset michael@0: Assembler::as_clz(Register rd, Register rs, Register rt) michael@0: { michael@0: return writeInst(InstReg(op_special2, rs, rt, rd, ff_clz).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_ins(Register rt, Register rs, uint16_t pos, uint16_t size) michael@0: { michael@0: JS_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size >= 32); michael@0: Register rd; michael@0: rd = Register::FromCode(pos + size - 1); michael@0: return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ins).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_ext(Register rt, Register rs, uint16_t pos, uint16_t size) michael@0: { michael@0: JS_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size >= 32); michael@0: Register rd; michael@0: rd = Register::FromCode(size - 1); michael@0: return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ext).encode()); michael@0: } michael@0: michael@0: // FP instructions michael@0: BufferOffset michael@0: Assembler::as_ld(FloatRegister fd, Register base, int32_t off) michael@0: { michael@0: JS_ASSERT(Imm16::isInSignedRange(off)); michael@0: return writeInst(InstImm(op_ldc1, base, fd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_sd(FloatRegister fd, Register base, int32_t off) michael@0: { michael@0: JS_ASSERT(Imm16::isInSignedRange(off)); michael@0: return writeInst(InstImm(op_sdc1, base, fd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_ls(FloatRegister fd, Register base, int32_t off) michael@0: { michael@0: JS_ASSERT(Imm16::isInSignedRange(off)); michael@0: return writeInst(InstImm(op_lwc1, base, fd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_ss(FloatRegister fd, Register base, int32_t off) michael@0: { michael@0: JS_ASSERT(Imm16::isInSignedRange(off)); michael@0: return writeInst(InstImm(op_swc1, base, fd, Imm16(off)).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_movs(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_mov_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_movd(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_mov_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_mtc1(Register rt, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_mtc1, rt, fs).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_mfc1(Register rt, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_mfc1, rt, fs).encode()); michael@0: } michael@0: michael@0: // FP convert instructions michael@0: BufferOffset michael@0: Assembler::as_ceilws(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_ceil_w_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_floorws(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_floor_w_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_roundws(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_round_w_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_truncws(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_trunc_w_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_ceilwd(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_ceil_w_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_floorwd(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_floor_w_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_roundwd(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_round_w_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_truncwd(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_trunc_w_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_cvtds(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_cvt_d_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_cvtdw(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_w, zero, fs, fd, ff_cvt_d_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_cvtsd(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_cvt_s_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_cvtsw(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_w, zero, fs, fd, ff_cvt_s_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_cvtwd(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_cvt_w_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_cvtws(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_cvt_w_fmt).encode()); michael@0: } michael@0: michael@0: // FP arithmetic instructions michael@0: BufferOffset michael@0: Assembler::as_adds(FloatRegister fd, FloatRegister fs, FloatRegister ft) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_add_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_addd(FloatRegister fd, FloatRegister fs, FloatRegister ft) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_add_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_subs(FloatRegister fd, FloatRegister fs, FloatRegister ft) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_sub_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_subd(FloatRegister fd, FloatRegister fs, FloatRegister ft) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_sub_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_abss(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_abs_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_absd(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_abs_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_negd(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_neg_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_muls(FloatRegister fd, FloatRegister fs, FloatRegister ft) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_mul_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_muld(FloatRegister fd, FloatRegister fs, FloatRegister ft) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_mul_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_divs(FloatRegister fd, FloatRegister fs, FloatRegister ft) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_s, ft, fs, fd, ff_div_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_divd(FloatRegister fd, FloatRegister fs, FloatRegister ft) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_d, ft, fs, fd, ff_div_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_sqrts(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_sqrt_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_sqrtd(FloatRegister fd, FloatRegister fs) michael@0: { michael@0: return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_sqrt_fmt).encode()); michael@0: } michael@0: michael@0: // FP compare instructions michael@0: BufferOffset michael@0: Assembler::as_cf(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) michael@0: { michael@0: RSField rs = fmt == DoubleFloat ? rs_d : rs_s; michael@0: return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_f_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_cun(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) michael@0: { michael@0: RSField rs = fmt == DoubleFloat ? rs_d : rs_s; michael@0: return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_un_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_ceq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) michael@0: { michael@0: RSField rs = fmt == DoubleFloat ? rs_d : rs_s; michael@0: return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_eq_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_cueq(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) michael@0: { michael@0: RSField rs = fmt == DoubleFloat ? rs_d : rs_s; michael@0: return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ueq_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_colt(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) michael@0: { michael@0: RSField rs = fmt == DoubleFloat ? rs_d : rs_s; michael@0: return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_olt_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_cult(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) michael@0: { michael@0: RSField rs = fmt == DoubleFloat ? rs_d : rs_s; michael@0: return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ult_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_cole(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) michael@0: { michael@0: RSField rs = fmt == DoubleFloat ? rs_d : rs_s; michael@0: return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ole_fmt).encode()); michael@0: } michael@0: michael@0: BufferOffset michael@0: Assembler::as_cule(FloatFormat fmt, FloatRegister fs, FloatRegister ft, FPConditionBit fcc) michael@0: { michael@0: RSField rs = fmt == DoubleFloat ? rs_d : rs_s; michael@0: return writeInst(InstReg(op_cop1, rs, ft, fs, fcc << FccShift, ff_c_ule_fmt).encode()); michael@0: } michael@0: michael@0: michael@0: void michael@0: Assembler::bind(Label *label, BufferOffset boff) michael@0: { michael@0: // If our caller didn't give us an explicit target to bind to michael@0: // then we want to bind to the location of the next instruction michael@0: BufferOffset dest = boff.assigned() ? boff : nextOffset(); michael@0: if (label->used()) { michael@0: int32_t next; michael@0: michael@0: // A used label holds a link to branch that uses it. michael@0: BufferOffset b(label); michael@0: do { michael@0: Instruction *inst = editSrc(b); michael@0: michael@0: // Second word holds a pointer to the next branch in label's chain. michael@0: next = inst[1].encode(); michael@0: bind(reinterpret_cast(inst), b.getOffset(), dest.getOffset()); michael@0: michael@0: b = BufferOffset(next); michael@0: } while (next != LabelBase::INVALID_OFFSET); michael@0: } michael@0: label->bind(dest.getOffset()); michael@0: } michael@0: michael@0: void michael@0: Assembler::bind(InstImm *inst, uint32_t branch, uint32_t target) michael@0: { michael@0: int32_t offset = target - branch; michael@0: InstImm inst_bgezal = InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)); michael@0: InstImm inst_beq = InstImm(op_beq, zero, zero, BOffImm16(0)); michael@0: michael@0: // If encoded offset is 4, then the jump must be short michael@0: if (BOffImm16(inst[0]).decode() == 4) { michael@0: JS_ASSERT(BOffImm16::isInRange(offset)); michael@0: inst[0].setBOffImm16(BOffImm16(offset)); michael@0: inst[1].makeNop(); michael@0: return; michael@0: } michael@0: if (BOffImm16::isInRange(offset)) { michael@0: bool conditional = (inst[0].encode() != inst_bgezal.encode() && michael@0: inst[0].encode() != inst_beq.encode()); michael@0: michael@0: inst[0].setBOffImm16(BOffImm16(offset)); michael@0: inst[1].makeNop(); michael@0: michael@0: // Skip the trailing nops in conditional branches. michael@0: if (conditional) { michael@0: inst[2] = InstImm(op_regimm, zero, rt_bgez, BOffImm16(3 * sizeof(void *))).encode(); michael@0: // There are 2 nops after this michael@0: } michael@0: return; michael@0: } michael@0: michael@0: if (inst[0].encode() == inst_bgezal.encode()) { michael@0: // Handle long call. michael@0: addLongJump(BufferOffset(branch)); michael@0: writeLuiOriInstructions(inst, &inst[1], ScratchRegister, target); michael@0: inst[2] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr).encode(); michael@0: // There is 1 nop after this. michael@0: } else if (inst[0].encode() == inst_beq.encode()) { michael@0: // Handle long unconditional jump. michael@0: addLongJump(BufferOffset(branch)); michael@0: writeLuiOriInstructions(inst, &inst[1], ScratchRegister, target); michael@0: inst[2] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr).encode(); michael@0: // There is 1 nop after this. michael@0: } else { michael@0: // Handle long conditional jump. michael@0: inst[0] = invertBranch(inst[0], BOffImm16(5 * sizeof(void *))); michael@0: // No need for a "nop" here because we can clobber scratch. michael@0: addLongJump(BufferOffset(branch + sizeof(void *))); michael@0: writeLuiOriInstructions(&inst[1], &inst[2], ScratchRegister, target); michael@0: inst[3] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr).encode(); michael@0: // There is 1 nop after this. michael@0: } michael@0: } michael@0: michael@0: void michael@0: Assembler::bind(RepatchLabel *label) michael@0: { michael@0: BufferOffset dest = nextOffset(); michael@0: if (label->used()) { michael@0: // If the label has a use, then change this use to refer to michael@0: // the bound label; michael@0: BufferOffset b(label->offset()); michael@0: Instruction *inst1 = editSrc(b); michael@0: Instruction *inst2 = inst1->next(); michael@0: michael@0: updateLuiOriValue(inst1, inst2, dest.getOffset()); michael@0: } michael@0: label->bind(dest.getOffset()); michael@0: } michael@0: michael@0: void michael@0: Assembler::retarget(Label *label, Label *target) michael@0: { michael@0: if (label->used()) { michael@0: if (target->bound()) { michael@0: bind(label, BufferOffset(target)); michael@0: } else if (target->used()) { michael@0: // The target is not bound but used. Prepend label's branch list michael@0: // onto target's. michael@0: int32_t next; michael@0: BufferOffset labelBranchOffset(label); michael@0: michael@0: // Find the head of the use chain for label. michael@0: do { michael@0: Instruction *inst = editSrc(labelBranchOffset); michael@0: michael@0: // Second word holds a pointer to the next branch in chain. michael@0: next = inst[1].encode(); michael@0: labelBranchOffset = BufferOffset(next); michael@0: } while (next != LabelBase::INVALID_OFFSET); michael@0: michael@0: // Then patch the head of label's use chain to the tail of michael@0: // target's use chain, prepending the entire use chain of target. michael@0: Instruction *inst = editSrc(labelBranchOffset); michael@0: int32_t prev = target->use(label->offset()); michael@0: inst[1].setData(prev); michael@0: } else { michael@0: // The target is unbound and unused. We can just take the head of michael@0: // the list hanging off of label, and dump that into target. michael@0: DebugOnly prev = target->use(label->offset()); michael@0: JS_ASSERT((int32_t)prev == Label::INVALID_OFFSET); michael@0: } michael@0: } michael@0: label->reset(); michael@0: } michael@0: michael@0: void dbg_break() {} michael@0: static int stopBKPT = -1; michael@0: void michael@0: Assembler::as_break(uint32_t code) michael@0: { michael@0: JS_ASSERT(code <= MAX_BREAK_CODE); michael@0: writeInst(op_special | code << RTShift | ff_break); michael@0: } michael@0: michael@0: uint32_t michael@0: Assembler::patchWrite_NearCallSize() michael@0: { michael@0: return 4 * sizeof(uint32_t); michael@0: } michael@0: michael@0: void michael@0: Assembler::patchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall) michael@0: { michael@0: Instruction *inst = (Instruction *) start.raw(); michael@0: uint8_t *dest = toCall.raw(); michael@0: michael@0: // Overwrite whatever instruction used to be here with a call. michael@0: // Always use long jump for two reasons: michael@0: // - Jump has to be the same size because of patchWrite_NearCallSize. michael@0: // - Return address has to be at the end of replaced block. michael@0: // Short jump wouldn't be more efficient. michael@0: writeLuiOriInstructions(inst, &inst[1], ScratchRegister, (uint32_t)dest); michael@0: inst[2] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr); michael@0: inst[3] = InstNOP(); michael@0: michael@0: // Ensure everyone sees the code that was just written into memory. michael@0: AutoFlushICache::flush(uintptr_t(inst), patchWrite_NearCallSize()); michael@0: } michael@0: michael@0: uint32_t michael@0: Assembler::extractLuiOriValue(Instruction *inst0, Instruction *inst1) michael@0: { michael@0: InstImm *i0 = (InstImm *) inst0; michael@0: InstImm *i1 = (InstImm *) inst1; michael@0: JS_ASSERT(i0->extractOpcode() == ((uint32_t)op_lui >> OpcodeShift)); michael@0: JS_ASSERT(i1->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift)); michael@0: michael@0: uint32_t value = i0->extractImm16Value() << 16; michael@0: value = value | i1->extractImm16Value(); michael@0: return value; michael@0: } michael@0: michael@0: void michael@0: Assembler::updateLuiOriValue(Instruction *inst0, Instruction *inst1, uint32_t value) michael@0: { michael@0: JS_ASSERT(inst0->extractOpcode() == ((uint32_t)op_lui >> OpcodeShift)); michael@0: JS_ASSERT(inst1->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift)); michael@0: michael@0: ((InstImm *) inst0)->setImm16(Imm16::upper(Imm32(value))); michael@0: ((InstImm *) inst1)->setImm16(Imm16::lower(Imm32(value))); michael@0: } michael@0: michael@0: void michael@0: Assembler::writeLuiOriInstructions(Instruction *inst0, Instruction *inst1, michael@0: Register reg, uint32_t value) michael@0: { michael@0: *inst0 = InstImm(op_lui, zero, reg, Imm16::upper(Imm32(value))); michael@0: *inst1 = InstImm(op_ori, reg, reg, Imm16::lower(Imm32(value))); michael@0: } michael@0: michael@0: void michael@0: Assembler::patchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue, michael@0: PatchedImmPtr expectedValue) michael@0: { michael@0: Instruction *inst = (Instruction *) label.raw(); michael@0: michael@0: // Extract old Value michael@0: DebugOnly value = Assembler::extractLuiOriValue(&inst[0], &inst[1]); michael@0: JS_ASSERT(value == uint32_t(expectedValue.value)); michael@0: michael@0: // Replace with new value michael@0: Assembler::updateLuiOriValue(inst, inst->next(), uint32_t(newValue.value)); michael@0: michael@0: AutoFlushICache::flush(uintptr_t(inst), 8); michael@0: } michael@0: michael@0: void michael@0: Assembler::patchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, ImmPtr expectedValue) michael@0: { michael@0: patchDataWithValueCheck(label, PatchedImmPtr(newValue.value), michael@0: PatchedImmPtr(expectedValue.value)); michael@0: } michael@0: michael@0: // This just stomps over memory with 32 bits of raw data. Its purpose is to michael@0: // overwrite the call of JITed code with 32 bits worth of an offset. This will michael@0: // is only meant to function on code that has been invalidated, so it should michael@0: // be totally safe. Since that instruction will never be executed again, a michael@0: // ICache flush should not be necessary michael@0: void michael@0: Assembler::patchWrite_Imm32(CodeLocationLabel label, Imm32 imm) michael@0: { michael@0: // Raw is going to be the return address. michael@0: uint32_t *raw = (uint32_t*)label.raw(); michael@0: // Overwrite the 4 bytes before the return address, which will michael@0: // end up being the call instruction. michael@0: *(raw - 1) = imm.value; michael@0: } michael@0: michael@0: uint8_t * michael@0: Assembler::nextInstruction(uint8_t *inst_, uint32_t *count) michael@0: { michael@0: Instruction *inst = reinterpret_cast(inst_); michael@0: if (count != nullptr) michael@0: *count += sizeof(Instruction); michael@0: return reinterpret_cast(inst->next()); michael@0: } michael@0: michael@0: // Since there are no pools in MIPS implementation, this should be simple. michael@0: Instruction * michael@0: Instruction::next() michael@0: { michael@0: return this + 1; michael@0: } michael@0: michael@0: InstImm Assembler::invertBranch(InstImm branch, BOffImm16 skipOffset) michael@0: { michael@0: uint32_t rt = 0; michael@0: Opcode op = (Opcode) (branch.extractOpcode() << OpcodeShift); michael@0: switch(op) { michael@0: case op_beq: michael@0: branch.setBOffImm16(skipOffset); michael@0: branch.setOpcode(op_bne); michael@0: return branch; michael@0: case op_bne: michael@0: branch.setBOffImm16(skipOffset); michael@0: branch.setOpcode(op_beq); michael@0: return branch; michael@0: case op_bgtz: michael@0: branch.setBOffImm16(skipOffset); michael@0: branch.setOpcode(op_blez); michael@0: return branch; michael@0: case op_blez: michael@0: branch.setBOffImm16(skipOffset); michael@0: branch.setOpcode(op_bgtz); michael@0: return branch; michael@0: case op_regimm: michael@0: branch.setBOffImm16(skipOffset); michael@0: rt = branch.extractRT(); michael@0: if (rt == (rt_bltz >> RTShift)) { michael@0: branch.setRT(rt_bgez); michael@0: return branch; michael@0: } michael@0: if (rt == (rt_bgez >> RTShift)) { michael@0: branch.setRT(rt_bltz); michael@0: return branch; michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Error creating long branch."); michael@0: return branch; michael@0: michael@0: case op_cop1: michael@0: JS_ASSERT(branch.extractRS() == rs_bc1 >> RSShift); michael@0: michael@0: branch.setBOffImm16(skipOffset); michael@0: rt = branch.extractRT(); michael@0: if (rt & 0x1) michael@0: branch.setRT((RTField) ((rt & ~0x1) << RTShift)); michael@0: else michael@0: branch.setRT((RTField) ((rt | 0x1) << RTShift)); michael@0: return branch; michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Error creating long branch."); michael@0: return branch; michael@0: } michael@0: michael@0: void michael@0: Assembler::ToggleToJmp(CodeLocationLabel inst_) michael@0: { michael@0: InstImm * inst = (InstImm *)inst_.raw(); michael@0: michael@0: JS_ASSERT(inst->extractOpcode() == ((uint32_t)op_andi >> OpcodeShift)); michael@0: // We converted beq to andi, so now we restore it. michael@0: inst->setOpcode(op_beq); michael@0: michael@0: AutoFlushICache::flush(uintptr_t(inst), 4); michael@0: } michael@0: michael@0: void michael@0: Assembler::ToggleToCmp(CodeLocationLabel inst_) michael@0: { michael@0: InstImm * inst = (InstImm *)inst_.raw(); michael@0: michael@0: // toggledJump is allways used for short jumps. michael@0: JS_ASSERT(inst->extractOpcode() == ((uint32_t)op_beq >> OpcodeShift)); michael@0: // Replace "beq $zero, $zero, offset" with "andi $zero, $zero, offset" michael@0: inst->setOpcode(op_andi); michael@0: michael@0: AutoFlushICache::flush(uintptr_t(inst), 4); michael@0: } michael@0: michael@0: void michael@0: Assembler::ToggleCall(CodeLocationLabel inst_, bool enabled) michael@0: { michael@0: Instruction *inst = (Instruction *)inst_.raw(); michael@0: InstImm *i0 = (InstImm *) inst; michael@0: InstImm *i1 = (InstImm *) i0->next(); michael@0: Instruction *i2 = (Instruction *) i1->next(); michael@0: michael@0: JS_ASSERT(i0->extractOpcode() == ((uint32_t)op_lui >> OpcodeShift)); michael@0: JS_ASSERT(i1->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift)); michael@0: michael@0: if (enabled) { michael@0: InstReg jalr = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr); michael@0: *i2 = jalr; michael@0: } else { michael@0: InstNOP nop; michael@0: *i2 = nop; michael@0: } michael@0: michael@0: AutoFlushICache::flush(uintptr_t(i2), 4); michael@0: } michael@0: michael@0: void Assembler::updateBoundsCheck(uint32_t heapSize, Instruction *inst) michael@0: { michael@0: MOZ_ASSUME_UNREACHABLE("NYI"); michael@0: }