| Index: runtime/vm/assembler_mips.h
|
| diff --git a/runtime/vm/assembler_mips.h b/runtime/vm/assembler_mips.h
|
| deleted file mode 100644
|
| index 7073e3078830b282307b06d35b3b03f4fb5be6e4..0000000000000000000000000000000000000000
|
| --- a/runtime/vm/assembler_mips.h
|
| +++ /dev/null
|
| @@ -1,1722 +0,0 @@
|
| -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -#ifndef RUNTIME_VM_ASSEMBLER_MIPS_H_
|
| -#define RUNTIME_VM_ASSEMBLER_MIPS_H_
|
| -
|
| -#ifndef RUNTIME_VM_ASSEMBLER_H_
|
| -#error Do not include assembler_mips.h directly; use assembler.h instead.
|
| -#endif
|
| -
|
| -#include "platform/assert.h"
|
| -#include "platform/utils.h"
|
| -#include "vm/constants_mips.h"
|
| -#include "vm/hash_map.h"
|
| -#include "vm/object.h"
|
| -#include "vm/simulator.h"
|
| -
|
| -// References to documentation in this file refer to:
|
| -// "MIPS® Architecture For Programmers Volume I-A:
|
| -// Introduction to the MIPS32® Architecture" in short "VolI-A"
|
| -// and
|
| -// "MIPS® Architecture For Programmers Volume II-A:
|
| -// The MIPS32® Instruction Set" in short "VolII-A"
|
| -namespace dart {
|
| -
|
| -// Forward declarations.
|
| -class RuntimeEntry;
|
| -class StubEntry;
|
| -
|
| -class Immediate : public ValueObject {
|
| - public:
|
| - explicit Immediate(int32_t value) : value_(value) {}
|
| -
|
| - Immediate(const Immediate& other) : ValueObject(), value_(other.value_) {}
|
| - Immediate& operator=(const Immediate& other) {
|
| - value_ = other.value_;
|
| - return *this;
|
| - }
|
| -
|
| - private:
|
| - int32_t value_;
|
| -
|
| - int32_t value() const { return value_; }
|
| -
|
| - friend class Assembler;
|
| -};
|
| -
|
| -
|
| -class Address : public ValueObject {
|
| - public:
|
| - explicit Address(Register base, int32_t offset = 0)
|
| - : ValueObject(), base_(base), offset_(offset) {}
|
| -
|
| - // This addressing mode does not exist.
|
| - Address(Register base, Register offset);
|
| -
|
| - Address(const Address& other)
|
| - : ValueObject(), base_(other.base_), offset_(other.offset_) {}
|
| - Address& operator=(const Address& other) {
|
| - base_ = other.base_;
|
| - offset_ = other.offset_;
|
| - return *this;
|
| - }
|
| -
|
| - uint32_t encoding() const {
|
| - ASSERT(Utils::IsInt(kImmBits, offset_));
|
| - uint16_t imm_value = static_cast<uint16_t>(offset_);
|
| - return (base_ << kRsShift) | imm_value;
|
| - }
|
| -
|
| - static bool CanHoldOffset(int32_t offset) {
|
| - return Utils::IsInt(kImmBits, offset);
|
| - }
|
| -
|
| - Register base() const { return base_; }
|
| - int32_t offset() const { return offset_; }
|
| -
|
| - private:
|
| - Register base_;
|
| - int32_t offset_;
|
| -};
|
| -
|
| -
|
| -class FieldAddress : public Address {
|
| - public:
|
| - FieldAddress(Register base, int32_t disp)
|
| - : Address(base, disp - kHeapObjectTag) {}
|
| -
|
| - FieldAddress(const FieldAddress& other) : Address(other) {}
|
| -
|
| - FieldAddress& operator=(const FieldAddress& other) {
|
| - Address::operator=(other);
|
| - return *this;
|
| - }
|
| -};
|
| -
|
| -
|
| -class Label : public ValueObject {
|
| - public:
|
| - Label() : position_(0) {}
|
| -
|
| - ~Label() {
|
| - // Assert if label is being destroyed with unresolved branches pending.
|
| - ASSERT(!IsLinked());
|
| - }
|
| -
|
| - // Returns the position for bound and linked labels. Cannot be used
|
| - // for unused labels.
|
| - intptr_t Position() const {
|
| - ASSERT(!IsUnused());
|
| - return IsBound() ? -position_ - kWordSize : position_ - kWordSize;
|
| - }
|
| -
|
| - bool IsBound() const { return position_ < 0; }
|
| - bool IsUnused() const { return position_ == 0; }
|
| - bool IsLinked() const { return position_ > 0; }
|
| -
|
| - private:
|
| - intptr_t position_;
|
| -
|
| - void Reinitialize() { position_ = 0; }
|
| -
|
| - void BindTo(intptr_t position) {
|
| - ASSERT(!IsBound());
|
| - position_ = -position - kWordSize;
|
| - ASSERT(IsBound());
|
| - }
|
| -
|
| - void LinkTo(intptr_t position) {
|
| - ASSERT(!IsBound());
|
| - position_ = position + kWordSize;
|
| - ASSERT(IsLinked());
|
| - }
|
| -
|
| - friend class Assembler;
|
| - DISALLOW_COPY_AND_ASSIGN(Label);
|
| -};
|
| -
|
| -
|
| -// There is no dedicated status register on MIPS, but Condition values are used
|
| -// and passed around by the intermediate language, so we need a Condition type.
|
| -// We delay code generation of a comparison that would result in a traditional
|
| -// condition code in the status register by keeping both register operands and
|
| -// the relational operator between them as the Condition.
|
| -class Condition : public ValueObject {
|
| - public:
|
| - enum Bits {
|
| - kLeftPos = 0,
|
| - kLeftSize = 6,
|
| - kRightPos = kLeftPos + kLeftSize,
|
| - kRightSize = 6,
|
| - kRelOpPos = kRightPos + kRightSize,
|
| - kRelOpSize = 4,
|
| - kImmPos = kRelOpPos + kRelOpSize,
|
| - kImmSize = 16,
|
| - };
|
| -
|
| - class LeftBits : public BitField<uword, Register, kLeftPos, kLeftSize> {};
|
| - class RightBits : public BitField<uword, Register, kRightPos, kRightSize> {};
|
| - class RelOpBits
|
| - : public BitField<uword, RelationOperator, kRelOpPos, kRelOpSize> {};
|
| - class ImmBits : public BitField<uword, uint16_t, kImmPos, kImmSize> {};
|
| -
|
| - Register left() const {
|
| - ASSERT(IsValid());
|
| - return LeftBits::decode(bits_);
|
| - }
|
| -
|
| - Register right() const {
|
| - ASSERT(IsValid());
|
| - return RightBits::decode(bits_);
|
| - }
|
| - RelationOperator rel_op() const { return RelOpBits::decode(bits_); }
|
| - int16_t imm() const {
|
| - ASSERT(IsValid());
|
| - return static_cast<int16_t>(ImmBits::decode(bits_));
|
| - }
|
| -
|
| - static bool IsValidImm(int32_t value) {
|
| - // We want both value and value + 1 to fit in an int16_t.
|
| - return (-0x08000 <= value) && (value < 0x7fff);
|
| - }
|
| -
|
| - void set_rel_op(RelationOperator value) {
|
| - ASSERT(IsValidRelOp(value));
|
| - bits_ = RelOpBits::update(value, bits_);
|
| - }
|
| -
|
| - bool IsValid() const { return rel_op() != INVALID_RELATION; }
|
| -
|
| - // Uninitialized condition.
|
| - Condition() : ValueObject(), bits_(RelOpBits::update(INVALID_RELATION, 0)) {}
|
| -
|
| - // Copy constructor.
|
| - Condition(const Condition& other) : ValueObject(), bits_(other.bits_) {}
|
| -
|
| - // Copy assignment operator.
|
| - Condition& operator=(const Condition& other) {
|
| - bits_ = other.bits_;
|
| - return *this;
|
| - }
|
| -
|
| - Condition(Register left,
|
| - Register right,
|
| - RelationOperator rel_op,
|
| - int16_t imm = 0) {
|
| - // At most one constant, ZR or immediate.
|
| - ASSERT(!(((left == ZR) || (left == IMM)) &&
|
| - ((right == ZR) || (right == IMM))));
|
| - // Non-zero immediate value is only allowed for IMM.
|
| - ASSERT((imm != 0) == ((left == IMM) || (right == IMM)));
|
| - set_left(left);
|
| - set_right(right);
|
| - if (rel_op == INVALID_RELATION) {
|
| - SetToInvalidState();
|
| - } else {
|
| - set_rel_op(rel_op);
|
| - }
|
| - set_imm(imm);
|
| - }
|
| -
|
| - private:
|
| - void SetToInvalidState() {
|
| - bits_ = RelOpBits::update(INVALID_RELATION, bits_);
|
| - }
|
| -
|
| - static bool IsValidRelOp(RelationOperator value) {
|
| - return (AL <= value) && (value <= ULE);
|
| - }
|
| -
|
| - static bool IsValidRegister(Register value) {
|
| - return (ZR <= value) && (value <= IMM) && (value != AT);
|
| - }
|
| -
|
| - void set_left(Register value) {
|
| - ASSERT(IsValidRegister(value));
|
| - bits_ = LeftBits::update(value, bits_);
|
| - }
|
| -
|
| - void set_right(Register value) {
|
| - ASSERT(IsValidRegister(value));
|
| - bits_ = RightBits::update(value, bits_);
|
| - }
|
| -
|
| - void set_imm(int16_t value) {
|
| - ASSERT(IsValidImm(value));
|
| - bits_ = ImmBits::update(static_cast<uint16_t>(value), bits_);
|
| - }
|
| -
|
| - uword bits_;
|
| -};
|
| -
|
| -
|
| -class Assembler : public ValueObject {
|
| - public:
|
| - explicit Assembler(bool use_far_branches = false)
|
| - : buffer_(),
|
| - prologue_offset_(-1),
|
| - has_single_entry_point_(true),
|
| - use_far_branches_(use_far_branches),
|
| - delay_slot_available_(false),
|
| - in_delay_slot_(false),
|
| - comments_(),
|
| - constant_pool_allowed_(true) {}
|
| - ~Assembler() {}
|
| -
|
| - void PopRegister(Register r) { Pop(r); }
|
| -
|
| - void Bind(Label* label);
|
| - void Jump(Label* label) { b(label); }
|
| -
|
| - // Misc. functionality
|
| - intptr_t CodeSize() const { return buffer_.Size(); }
|
| - intptr_t prologue_offset() const { return prologue_offset_; }
|
| - bool has_single_entry_point() const { return has_single_entry_point_; }
|
| -
|
| - // Count the fixups that produce a pointer offset, without processing
|
| - // the fixups.
|
| - intptr_t CountPointerOffsets() const { return buffer_.CountPointerOffsets(); }
|
| -
|
| - const ZoneGrowableArray<intptr_t>& GetPointerOffsets() const {
|
| - return buffer_.pointer_offsets();
|
| - }
|
| -
|
| - ObjectPoolWrapper& object_pool_wrapper() { return object_pool_wrapper_; }
|
| -
|
| - RawObjectPool* MakeObjectPool() {
|
| - return object_pool_wrapper_.MakeObjectPool();
|
| - }
|
| -
|
| - void FinalizeInstructions(const MemoryRegion& region) {
|
| - buffer_.FinalizeInstructions(region);
|
| - }
|
| -
|
| - bool use_far_branches() const {
|
| - return FLAG_use_far_branches || use_far_branches_;
|
| - }
|
| -
|
| - void set_use_far_branches(bool b) { use_far_branches_ = b; }
|
| -
|
| - void EnterFrame();
|
| - void LeaveFrameAndReturn();
|
| -
|
| - // Set up a stub frame so that the stack traversal code can easily identify
|
| - // a stub frame.
|
| - void EnterStubFrame(intptr_t frame_size = 0);
|
| - void LeaveStubFrame();
|
| - // A separate macro for when a Ret immediately follows, so that we can use
|
| - // the branch delay slot.
|
| - void LeaveStubFrameAndReturn(Register ra = RA);
|
| -
|
| - void MonomorphicCheckedEntry();
|
| -
|
| - void UpdateAllocationStats(intptr_t cid,
|
| - Register temp_reg,
|
| - Heap::Space space);
|
| -
|
| - void UpdateAllocationStatsWithSize(intptr_t cid,
|
| - Register size_reg,
|
| - Register temp_reg,
|
| - Heap::Space space);
|
| -
|
| -
|
| - void MaybeTraceAllocation(intptr_t cid, Register temp_reg, Label* trace);
|
| -
|
| - // Inlined allocation of an instance of class 'cls', code has no runtime
|
| - // calls. Jump to 'failure' if the instance cannot be allocated here.
|
| - // Allocated instance is returned in 'instance_reg'.
|
| - // Only the tags field of the object is initialized.
|
| - void TryAllocate(const Class& cls,
|
| - Label* failure,
|
| - Register instance_reg,
|
| - Register temp_reg);
|
| -
|
| - void TryAllocateArray(intptr_t cid,
|
| - intptr_t instance_size,
|
| - Label* failure,
|
| - Register instance,
|
| - Register end_address,
|
| - Register temp1,
|
| - Register temp2);
|
| -
|
| - // Debugging and bringup support.
|
| - void Stop(const char* message);
|
| - void Unimplemented(const char* message);
|
| - void Untested(const char* message);
|
| - void Unreachable(const char* message);
|
| -
|
| - static void InitializeMemoryWithBreakpoints(uword data, intptr_t length);
|
| -
|
| - void Comment(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
|
| - static bool EmittingComments();
|
| -
|
| - const Code::Comments& GetCodeComments() const;
|
| -
|
| - static const char* RegisterName(Register reg);
|
| -
|
| - static const char* FpuRegisterName(FpuRegister reg);
|
| -
|
| - void SetPrologueOffset() {
|
| - if (prologue_offset_ == -1) {
|
| - prologue_offset_ = CodeSize();
|
| - }
|
| - }
|
| -
|
| - // A utility to be able to assemble an instruction into the delay slot.
|
| - Assembler* delay_slot() {
|
| - ASSERT(delay_slot_available_);
|
| - ASSERT(buffer_.Load<int32_t>(buffer_.GetPosition() - sizeof(int32_t)) ==
|
| - Instr::kNopInstruction);
|
| - buffer_.Remit<int32_t>();
|
| - delay_slot_available_ = false;
|
| - in_delay_slot_ = true;
|
| - return this;
|
| - }
|
| -
|
| - // CPU instructions in alphabetical order.
|
| - void addd(DRegister dd, DRegister ds, DRegister dt) {
|
| - // DRegisters start at the even FRegisters.
|
| - FRegister fd = static_cast<FRegister>(dd * 2);
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - FRegister ft = static_cast<FRegister>(dt * 2);
|
| - EmitFpuRType(COP1, FMT_D, ft, fs, fd, COP1_ADD);
|
| - }
|
| -
|
| - void addiu(Register rt, Register rs, const Immediate& imm) {
|
| - ASSERT(Utils::IsInt(kImmBits, imm.value()));
|
| - const uint16_t imm_value = static_cast<uint16_t>(imm.value());
|
| - EmitIType(ADDIU, rs, rt, imm_value);
|
| - }
|
| -
|
| - void addu(Register rd, Register rs, Register rt) {
|
| - EmitRType(SPECIAL, rs, rt, rd, 0, ADDU);
|
| - }
|
| -
|
| - void and_(Register rd, Register rs, Register rt) {
|
| - EmitRType(SPECIAL, rs, rt, rd, 0, AND);
|
| - }
|
| -
|
| - void andi(Register rt, Register rs, const Immediate& imm) {
|
| - ASSERT(Utils::IsUint(kImmBits, imm.value()));
|
| - const uint16_t imm_value = static_cast<uint16_t>(imm.value());
|
| - EmitIType(ANDI, rs, rt, imm_value);
|
| - }
|
| -
|
| - // Unconditional branch.
|
| - void b(Label* l) { beq(R0, R0, l); }
|
| -
|
| - void bal(Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - EmitRegImmBranch(BGEZAL, R0, l);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - // Branch on floating point false.
|
| - void bc1f(Label* l) {
|
| - EmitFpuBranch(false, l);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - // Branch on floating point true.
|
| - void bc1t(Label* l) {
|
| - EmitFpuBranch(true, l);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - // Branch if equal.
|
| - void beq(Register rs, Register rt, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - EmitBranch(BEQ, rs, rt, l);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - // Branch if equal, likely taken.
|
| - // Delay slot executed only when branch taken.
|
| - void beql(Register rs, Register rt, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - EmitBranch(BEQL, rs, rt, l);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - // Branch if rs >= 0.
|
| - void bgez(Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - EmitRegImmBranch(BGEZ, rs, l);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - // Branch if rs >= 0, likely taken.
|
| - // Delay slot executed only when branch taken.
|
| - void bgezl(Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - EmitRegImmBranch(BGEZL, rs, l);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - // Branch if rs > 0.
|
| - void bgtz(Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - EmitBranch(BGTZ, rs, R0, l);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - // Branch if rs > 0, likely taken.
|
| - // Delay slot executed only when branch taken.
|
| - void bgtzl(Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - EmitBranch(BGTZL, rs, R0, l);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - // Branch if rs <= 0.
|
| - void blez(Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - EmitBranch(BLEZ, rs, R0, l);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - // Branch if rs <= 0, likely taken.
|
| - // Delay slot executed only when branch taken.
|
| - void blezl(Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - EmitBranch(BLEZL, rs, R0, l);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - // Branch if rs < 0.
|
| - void bltz(Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - EmitRegImmBranch(BLTZ, rs, l);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - // Branch if rs < 0, likely taken.
|
| - // Delay slot executed only when branch taken.
|
| - void bltzl(Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - EmitRegImmBranch(BLTZL, rs, l);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - // Branch if not equal.
|
| - void bne(Register rs, Register rt, Label* l) {
|
| - ASSERT(!in_delay_slot_); // Jump within a delay slot is not supported.
|
| - EmitBranch(BNE, rs, rt, l);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - // Branch if not equal, likely taken.
|
| - // Delay slot executed only when branch taken.
|
| - void bnel(Register rs, Register rt, Label* l) {
|
| - ASSERT(!in_delay_slot_); // Jump within a delay slot is not supported.
|
| - EmitBranch(BNEL, rs, rt, l);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - static int32_t BreakEncoding(int32_t code) {
|
| - ASSERT(Utils::IsUint(20, code));
|
| - return SPECIAL << kOpcodeShift | code << kBreakCodeShift |
|
| - BREAK << kFunctionShift;
|
| - }
|
| -
|
| -
|
| - void break_(int32_t code) { Emit(BreakEncoding(code)); }
|
| -
|
| - static uword GetBreakInstructionFiller() { return BreakEncoding(0); }
|
| -
|
| - // FPU compare, always false.
|
| - void cfd(DRegister ds, DRegister dt) {
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - FRegister ft = static_cast<FRegister>(dt * 2);
|
| - EmitFpuRType(COP1, FMT_D, ft, fs, F0, COP1_C_F);
|
| - }
|
| -
|
| - // FPU compare, true if unordered, i.e. one is NaN.
|
| - void cund(DRegister ds, DRegister dt) {
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - FRegister ft = static_cast<FRegister>(dt * 2);
|
| - EmitFpuRType(COP1, FMT_D, ft, fs, F0, COP1_C_UN);
|
| - }
|
| -
|
| - // FPU compare, true if equal.
|
| - void ceqd(DRegister ds, DRegister dt) {
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - FRegister ft = static_cast<FRegister>(dt * 2);
|
| - EmitFpuRType(COP1, FMT_D, ft, fs, F0, COP1_C_EQ);
|
| - }
|
| -
|
| - // FPU compare, true if unordered or equal.
|
| - void cueqd(DRegister ds, DRegister dt) {
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - FRegister ft = static_cast<FRegister>(dt * 2);
|
| - EmitFpuRType(COP1, FMT_D, ft, fs, F0, COP1_C_UEQ);
|
| - }
|
| -
|
| - // FPU compare, true if less than.
|
| - void coltd(DRegister ds, DRegister dt) {
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - FRegister ft = static_cast<FRegister>(dt * 2);
|
| - EmitFpuRType(COP1, FMT_D, ft, fs, F0, COP1_C_OLT);
|
| - }
|
| -
|
| - // FPU compare, true if unordered or less than.
|
| - void cultd(DRegister ds, DRegister dt) {
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - FRegister ft = static_cast<FRegister>(dt * 2);
|
| - EmitFpuRType(COP1, FMT_D, ft, fs, F0, COP1_C_ULT);
|
| - }
|
| -
|
| - // FPU compare, true if less or equal.
|
| - void coled(DRegister ds, DRegister dt) {
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - FRegister ft = static_cast<FRegister>(dt * 2);
|
| - EmitFpuRType(COP1, FMT_D, ft, fs, F0, COP1_C_OLE);
|
| - }
|
| -
|
| - // FPU compare, true if unordered or less or equal.
|
| - void culed(DRegister ds, DRegister dt) {
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - FRegister ft = static_cast<FRegister>(dt * 2);
|
| - EmitFpuRType(COP1, FMT_D, ft, fs, F0, COP1_C_ULE);
|
| - }
|
| -
|
| - void clo(Register rd, Register rs) {
|
| - EmitRType(SPECIAL2, rs, rd, rd, 0, CLO);
|
| - }
|
| -
|
| - void clz(Register rd, Register rs) {
|
| - EmitRType(SPECIAL2, rs, rd, rd, 0, CLZ);
|
| - }
|
| -
|
| - // Convert a double in ds to a 32-bit signed int in fd rounding towards 0.
|
| - void truncwd(FRegister fd, DRegister ds) {
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - EmitFpuRType(COP1, FMT_D, F0, fs, fd, COP1_TRUNC_W);
|
| - }
|
| -
|
| - // Convert a 32-bit float in fs to a 64-bit double in dd.
|
| - void cvtds(DRegister dd, FRegister fs) {
|
| - FRegister fd = static_cast<FRegister>(dd * 2);
|
| - EmitFpuRType(COP1, FMT_S, F0, fs, fd, COP1_CVT_D);
|
| - }
|
| -
|
| - // Converts a 32-bit signed int in fs to a double in fd.
|
| - void cvtdw(DRegister dd, FRegister fs) {
|
| - FRegister fd = static_cast<FRegister>(dd * 2);
|
| - EmitFpuRType(COP1, FMT_W, F0, fs, fd, COP1_CVT_D);
|
| - }
|
| -
|
| - // Convert a 64-bit double in ds to a 32-bit float in fd.
|
| - void cvtsd(FRegister fd, DRegister ds) {
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - EmitFpuRType(COP1, FMT_D, F0, fs, fd, COP1_CVT_S);
|
| - }
|
| -
|
| - void div(Register rs, Register rt) { EmitRType(SPECIAL, rs, rt, R0, 0, DIV); }
|
| -
|
| - void divd(DRegister dd, DRegister ds, DRegister dt) {
|
| - FRegister fd = static_cast<FRegister>(dd * 2);
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - FRegister ft = static_cast<FRegister>(dt * 2);
|
| - EmitFpuRType(COP1, FMT_D, ft, fs, fd, COP1_DIV);
|
| - }
|
| -
|
| - void divu(Register rs, Register rt) {
|
| - EmitRType(SPECIAL, rs, rt, R0, 0, DIVU);
|
| - }
|
| -
|
| - void jalr(Register rs, Register rd = RA) {
|
| - ASSERT(rs != rd);
|
| - ASSERT(!in_delay_slot_); // Jump within a delay slot is not supported.
|
| - EmitRType(SPECIAL, rs, R0, rd, 0, JALR);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - void jr(Register rs) {
|
| - ASSERT(!in_delay_slot_); // Jump within a delay slot is not supported.
|
| - EmitRType(SPECIAL, rs, R0, R0, 0, JR);
|
| - EmitBranchDelayNop();
|
| - }
|
| -
|
| - void lb(Register rt, const Address& addr) { EmitLoadStore(LB, rt, addr); }
|
| -
|
| - void lbu(Register rt, const Address& addr) { EmitLoadStore(LBU, rt, addr); }
|
| -
|
| - void ldc1(DRegister dt, const Address& addr) {
|
| - FRegister ft = static_cast<FRegister>(dt * 2);
|
| - EmitFpuLoadStore(LDC1, ft, addr);
|
| - }
|
| -
|
| - void lh(Register rt, const Address& addr) { EmitLoadStore(LH, rt, addr); }
|
| -
|
| - void lhu(Register rt, const Address& addr) { EmitLoadStore(LHU, rt, addr); }
|
| -
|
| - void ll(Register rt, const Address& addr) { EmitLoadStore(LL, rt, addr); }
|
| -
|
| - void lui(Register rt, const Immediate& imm) {
|
| - ASSERT(Utils::IsUint(kImmBits, imm.value()));
|
| - const uint16_t imm_value = static_cast<uint16_t>(imm.value());
|
| - EmitIType(LUI, R0, rt, imm_value);
|
| - }
|
| -
|
| - void lw(Register rt, const Address& addr) { EmitLoadStore(LW, rt, addr); }
|
| -
|
| - void lwc1(FRegister ft, const Address& addr) {
|
| - EmitFpuLoadStore(LWC1, ft, addr);
|
| - }
|
| -
|
| - void madd(Register rs, Register rt) {
|
| - EmitRType(SPECIAL2, rs, rt, R0, 0, MADD);
|
| - }
|
| -
|
| - void maddu(Register rs, Register rt) {
|
| - EmitRType(SPECIAL2, rs, rt, R0, 0, MADDU);
|
| - }
|
| -
|
| - void mfc1(Register rt, FRegister fs) {
|
| - Emit(COP1 << kOpcodeShift | COP1_MF << kCop1SubShift | rt << kRtShift |
|
| - fs << kFsShift);
|
| - }
|
| -
|
| - void mfhi(Register rd) { EmitRType(SPECIAL, R0, R0, rd, 0, MFHI); }
|
| -
|
| - void mflo(Register rd) { EmitRType(SPECIAL, R0, R0, rd, 0, MFLO); }
|
| -
|
| - void mov(Register rd, Register rs) { or_(rd, rs, ZR); }
|
| -
|
| - void movd(DRegister dd, DRegister ds) {
|
| - FRegister fd = static_cast<FRegister>(dd * 2);
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - EmitFpuRType(COP1, FMT_D, F0, fs, fd, COP1_MOV);
|
| - }
|
| -
|
| - // Move if floating point false.
|
| - void movf(Register rd, Register rs) {
|
| - EmitRType(SPECIAL, rs, R0, rd, 0, MOVCI);
|
| - }
|
| -
|
| - void movn(Register rd, Register rs, Register rt) {
|
| - EmitRType(SPECIAL, rs, rt, rd, 0, MOVN);
|
| - }
|
| -
|
| - // Move if floating point true.
|
| - void movt(Register rd, Register rs) {
|
| - EmitRType(SPECIAL, rs, R1, rd, 0, MOVCI);
|
| - }
|
| -
|
| - // rd <- (rt == 0) ? rs : rd;
|
| - void movz(Register rd, Register rs, Register rt) {
|
| - EmitRType(SPECIAL, rs, rt, rd, 0, MOVZ);
|
| - }
|
| -
|
| - void movs(FRegister fd, FRegister fs) {
|
| - EmitFpuRType(COP1, FMT_S, F0, fs, fd, COP1_MOV);
|
| - }
|
| -
|
| - void mtc1(Register rt, FRegister fs) {
|
| - Emit(COP1 << kOpcodeShift | COP1_MT << kCop1SubShift | rt << kRtShift |
|
| - fs << kFsShift);
|
| - }
|
| -
|
| - void mthi(Register rs) { EmitRType(SPECIAL, rs, R0, R0, 0, MTHI); }
|
| -
|
| - void mtlo(Register rs) { EmitRType(SPECIAL, rs, R0, R0, 0, MTLO); }
|
| -
|
| - void muld(DRegister dd, DRegister ds, DRegister dt) {
|
| - FRegister fd = static_cast<FRegister>(dd * 2);
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - FRegister ft = static_cast<FRegister>(dt * 2);
|
| - EmitFpuRType(COP1, FMT_D, ft, fs, fd, COP1_MUL);
|
| - }
|
| -
|
| - void mult(Register rs, Register rt) {
|
| - EmitRType(SPECIAL, rs, rt, R0, 0, MULT);
|
| - }
|
| -
|
| - void multu(Register rs, Register rt) {
|
| - EmitRType(SPECIAL, rs, rt, R0, 0, MULTU);
|
| - }
|
| -
|
| - void negd(DRegister dd, DRegister ds) {
|
| - FRegister fd = static_cast<FRegister>(dd * 2);
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - EmitFpuRType(COP1, FMT_D, F0, fs, fd, COP1_NEG);
|
| - }
|
| -
|
| - void nop() { Emit(Instr::kNopInstruction); }
|
| -
|
| - void nor(Register rd, Register rs, Register rt) {
|
| - EmitRType(SPECIAL, rs, rt, rd, 0, NOR);
|
| - }
|
| -
|
| - void or_(Register rd, Register rs, Register rt) {
|
| - EmitRType(SPECIAL, rs, rt, rd, 0, OR);
|
| - }
|
| -
|
| - void ori(Register rt, Register rs, const Immediate& imm) {
|
| - ASSERT(Utils::IsUint(kImmBits, imm.value()));
|
| - const uint16_t imm_value = static_cast<uint16_t>(imm.value());
|
| - EmitIType(ORI, rs, rt, imm_value);
|
| - }
|
| -
|
| - void sb(Register rt, const Address& addr) { EmitLoadStore(SB, rt, addr); }
|
| -
|
| - // rt = 1 on success, 0 on failure.
|
| - void sc(Register rt, const Address& addr) { EmitLoadStore(SC, rt, addr); }
|
| -
|
| - void sdc1(DRegister dt, const Address& addr) {
|
| - FRegister ft = static_cast<FRegister>(dt * 2);
|
| - EmitFpuLoadStore(SDC1, ft, addr);
|
| - }
|
| -
|
| - void sh(Register rt, const Address& addr) { EmitLoadStore(SH, rt, addr); }
|
| -
|
| - void sll(Register rd, Register rt, int sa) {
|
| - EmitRType(SPECIAL, R0, rt, rd, sa, SLL);
|
| - }
|
| -
|
| - void sllv(Register rd, Register rt, Register rs) {
|
| - EmitRType(SPECIAL, rs, rt, rd, 0, SLLV);
|
| - }
|
| -
|
| - void slt(Register rd, Register rs, Register rt) {
|
| - EmitRType(SPECIAL, rs, rt, rd, 0, SLT);
|
| - }
|
| -
|
| - void slti(Register rt, Register rs, const Immediate& imm) {
|
| - ASSERT(Utils::IsInt(kImmBits, imm.value()));
|
| - const uint16_t imm_value = static_cast<uint16_t>(imm.value());
|
| - EmitIType(SLTI, rs, rt, imm_value);
|
| - }
|
| -
|
| - // Although imm argument is int32_t, it is interpreted as an uint32_t.
|
| - // For example, -1 stands for 0xffffffffUL: it is encoded as 0xffff in the
|
| - // instruction imm field and is then sign extended back to 0xffffffffUL.
|
| - void sltiu(Register rt, Register rs, const Immediate& imm) {
|
| - ASSERT(Utils::IsInt(kImmBits, imm.value()));
|
| - const uint16_t imm_value = static_cast<uint16_t>(imm.value());
|
| - EmitIType(SLTIU, rs, rt, imm_value);
|
| - }
|
| -
|
| - void sltu(Register rd, Register rs, Register rt) {
|
| - EmitRType(SPECIAL, rs, rt, rd, 0, SLTU);
|
| - }
|
| -
|
| - void sqrtd(DRegister dd, DRegister ds) {
|
| - FRegister fd = static_cast<FRegister>(dd * 2);
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - EmitFpuRType(COP1, FMT_D, F0, fs, fd, COP1_SQRT);
|
| - }
|
| -
|
| - void sra(Register rd, Register rt, int sa) {
|
| - EmitRType(SPECIAL, R0, rt, rd, sa, SRA);
|
| - }
|
| -
|
| - void srav(Register rd, Register rt, Register rs) {
|
| - EmitRType(SPECIAL, rs, rt, rd, 0, SRAV);
|
| - }
|
| -
|
| - void srl(Register rd, Register rt, int sa) {
|
| - EmitRType(SPECIAL, R0, rt, rd, sa, SRL);
|
| - }
|
| -
|
| - void srlv(Register rd, Register rt, Register rs) {
|
| - EmitRType(SPECIAL, rs, rt, rd, 0, SRLV);
|
| - }
|
| -
|
| - void subd(DRegister dd, DRegister ds, DRegister dt) {
|
| - FRegister fd = static_cast<FRegister>(dd * 2);
|
| - FRegister fs = static_cast<FRegister>(ds * 2);
|
| - FRegister ft = static_cast<FRegister>(dt * 2);
|
| - EmitFpuRType(COP1, FMT_D, ft, fs, fd, COP1_SUB);
|
| - }
|
| -
|
| - void subu(Register rd, Register rs, Register rt) {
|
| - EmitRType(SPECIAL, rs, rt, rd, 0, SUBU);
|
| - }
|
| -
|
| - void sw(Register rt, const Address& addr) { EmitLoadStore(SW, rt, addr); }
|
| -
|
| - void swc1(FRegister ft, const Address& addr) {
|
| - EmitFpuLoadStore(SWC1, ft, addr);
|
| - }
|
| -
|
| - void xori(Register rt, Register rs, const Immediate& imm) {
|
| - ASSERT(Utils::IsUint(kImmBits, imm.value()));
|
| - const uint16_t imm_value = static_cast<uint16_t>(imm.value());
|
| - EmitIType(XORI, rs, rt, imm_value);
|
| - }
|
| -
|
| - void xor_(Register rd, Register rs, Register rt) {
|
| - EmitRType(SPECIAL, rs, rt, rd, 0, XOR);
|
| - }
|
| -
|
| - // Macros in alphabetical order.
|
| -
|
| - // Addition of rs and rt with the result placed in rd.
|
| - // After, ro < 0 if there was signed overflow, ro >= 0 otherwise.
|
| - // rd and ro must not be TMP.
|
| - // ro must be different from all the other registers.
|
| - // If rd, rs, and rt are the same register, then a scratch register different
|
| - // from the other registers is needed.
|
| - void AdduDetectOverflow(Register rd,
|
| - Register rs,
|
| - Register rt,
|
| - Register ro,
|
| - Register scratch = kNoRegister);
|
| -
|
| - // ro must be different from rd and rs.
|
| - // rd and ro must not be TMP.
|
| - // If rd and rs are the same, a scratch register different from the other
|
| - // registers is needed.
|
| - void AddImmediateDetectOverflow(Register rd,
|
| - Register rs,
|
| - int32_t imm,
|
| - Register ro,
|
| - Register scratch = kNoRegister) {
|
| - ASSERT(!in_delay_slot_);
|
| - LoadImmediate(rd, imm);
|
| - AdduDetectOverflow(rd, rs, rd, ro, scratch);
|
| - }
|
| -
|
| - // Subtraction of rt from rs (rs - rt) with the result placed in rd.
|
| - // After, ro < 0 if there was signed overflow, ro >= 0 otherwise.
|
| - // None of rd, rs, rt, or ro may be TMP.
|
| - // ro must be different from the other registers.
|
| - void SubuDetectOverflow(Register rd, Register rs, Register rt, Register ro);
|
| -
|
| - // ro must be different from rd and rs.
|
| - // None of rd, rs, rt, or ro may be TMP.
|
| - void SubImmediateDetectOverflow(Register rd,
|
| - Register rs,
|
| - int32_t imm,
|
| - Register ro) {
|
| - ASSERT(!in_delay_slot_);
|
| - LoadImmediate(rd, imm);
|
| - SubuDetectOverflow(rd, rs, rd, ro);
|
| - }
|
| -
|
| - void Branch(const StubEntry& stub_entry, Register pp = PP);
|
| -
|
| - void BranchLink(const StubEntry& stub_entry,
|
| - Patchability patchable = kNotPatchable);
|
| -
|
| - void BranchLinkPatchable(const StubEntry& stub_entry);
|
| - void BranchLinkToRuntime();
|
| -
|
| - // Emit a call that shares its object pool entries with other calls
|
| - // that have the same equivalence marker.
|
| - void BranchLinkWithEquivalence(const StubEntry& stub_entry,
|
| - const Object& equivalence);
|
| -
|
| - void Drop(intptr_t stack_elements) {
|
| - ASSERT(stack_elements >= 0);
|
| - if (stack_elements > 0) {
|
| - addiu(SP, SP, Immediate(stack_elements * kWordSize));
|
| - }
|
| - }
|
| -
|
| - void LoadPoolPointer(Register reg = PP) {
|
| - ASSERT(!in_delay_slot_);
|
| - CheckCodePointer();
|
| - lw(reg, FieldAddress(CODE_REG, Code::object_pool_offset()));
|
| - set_constant_pool_allowed(reg == PP);
|
| - }
|
| -
|
| - void CheckCodePointer();
|
| -
|
| - void RestoreCodePointer();
|
| -
|
| - void LoadImmediate(Register rd, int32_t value) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (Utils::IsInt(kImmBits, value)) {
|
| - addiu(rd, ZR, Immediate(value));
|
| - } else {
|
| - const uint16_t low = Utils::Low16Bits(value);
|
| - const uint16_t high = Utils::High16Bits(value);
|
| - lui(rd, Immediate(high));
|
| - if (low != 0) {
|
| - ori(rd, rd, Immediate(low));
|
| - }
|
| - }
|
| - }
|
| -
|
| - void LoadImmediate(DRegister rd, double value) {
|
| - ASSERT(!in_delay_slot_);
|
| - FRegister frd = static_cast<FRegister>(rd * 2);
|
| - const int64_t ival = bit_cast<uint64_t, double>(value);
|
| - const int32_t low = Utils::Low32Bits(ival);
|
| - const int32_t high = Utils::High32Bits(ival);
|
| - if (low != 0) {
|
| - LoadImmediate(TMP, low);
|
| - mtc1(TMP, frd);
|
| - } else {
|
| - mtc1(ZR, frd);
|
| - }
|
| -
|
| - if (high != 0) {
|
| - LoadImmediate(TMP, high);
|
| - mtc1(TMP, static_cast<FRegister>(frd + 1));
|
| - } else {
|
| - mtc1(ZR, static_cast<FRegister>(frd + 1));
|
| - }
|
| - }
|
| -
|
| - void LoadImmediate(FRegister rd, float value) {
|
| - ASSERT(!in_delay_slot_);
|
| - const int32_t ival = bit_cast<int32_t, float>(value);
|
| - if (ival == 0) {
|
| - mtc1(ZR, rd);
|
| - } else {
|
| - LoadImmediate(TMP, ival);
|
| - mtc1(TMP, rd);
|
| - }
|
| - }
|
| -
|
| - void AddImmediate(Register rd, Register rs, int32_t value) {
|
| - ASSERT(!in_delay_slot_);
|
| - if ((value == 0) && (rd == rs)) return;
|
| - // If value is 0, we still want to move rs to rd if they aren't the same.
|
| - if (Utils::IsInt(kImmBits, value)) {
|
| - addiu(rd, rs, Immediate(value));
|
| - } else {
|
| - LoadImmediate(TMP, value);
|
| - addu(rd, rs, TMP);
|
| - }
|
| - }
|
| -
|
| - void AddImmediate(Register rd, int32_t value) {
|
| - ASSERT(!in_delay_slot_);
|
| - AddImmediate(rd, rd, value);
|
| - }
|
| -
|
| - void AndImmediate(Register rd, Register rs, int32_t imm) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (imm == 0) {
|
| - mov(rd, ZR);
|
| - return;
|
| - }
|
| -
|
| - if (Utils::IsUint(kImmBits, imm)) {
|
| - andi(rd, rs, Immediate(imm));
|
| - } else {
|
| - LoadImmediate(TMP, imm);
|
| - and_(rd, rs, TMP);
|
| - }
|
| - }
|
| -
|
| - void OrImmediate(Register rd, Register rs, int32_t imm) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (imm == 0) {
|
| - mov(rd, rs);
|
| - return;
|
| - }
|
| -
|
| - if (Utils::IsUint(kImmBits, imm)) {
|
| - ori(rd, rs, Immediate(imm));
|
| - } else {
|
| - LoadImmediate(TMP, imm);
|
| - or_(rd, rs, TMP);
|
| - }
|
| - }
|
| -
|
| - void XorImmediate(Register rd, Register rs, int32_t imm) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (imm == 0) {
|
| - mov(rd, rs);
|
| - return;
|
| - }
|
| -
|
| - if (Utils::IsUint(kImmBits, imm)) {
|
| - xori(rd, rs, Immediate(imm));
|
| - } else {
|
| - LoadImmediate(TMP, imm);
|
| - xor_(rd, rs, TMP);
|
| - }
|
| - }
|
| -
|
| - Register LoadConditionOperand(Register rd,
|
| - const Object& operand,
|
| - int16_t* imm) {
|
| - if (operand.IsSmi()) {
|
| - const int32_t val = reinterpret_cast<int32_t>(operand.raw());
|
| - if (val == 0) {
|
| - return ZR;
|
| - } else if (Condition::IsValidImm(val)) {
|
| - ASSERT(*imm == 0);
|
| - *imm = val;
|
| - return IMM;
|
| - }
|
| - }
|
| - LoadObject(rd, operand);
|
| - return rd;
|
| - }
|
| -
|
| - // Branch to label if condition is true.
|
| - void BranchOnCondition(Condition cond, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - Register left = cond.left();
|
| - Register right = cond.right();
|
| - RelationOperator rel_op = cond.rel_op();
|
| - switch (rel_op) {
|
| - case NV:
|
| - return;
|
| - case AL:
|
| - b(l);
|
| - return;
|
| - case EQ: // fall through.
|
| - case NE: {
|
| - if (left == IMM) {
|
| - addiu(AT, ZR, Immediate(cond.imm()));
|
| - left = AT;
|
| - } else if (right == IMM) {
|
| - addiu(AT, ZR, Immediate(cond.imm()));
|
| - right = AT;
|
| - }
|
| - if (rel_op == EQ) {
|
| - beq(left, right, l);
|
| - } else {
|
| - bne(left, right, l);
|
| - }
|
| - break;
|
| - }
|
| - case GT: {
|
| - if (left == ZR) {
|
| - bltz(right, l);
|
| - } else if (right == ZR) {
|
| - bgtz(left, l);
|
| - } else if (left == IMM) {
|
| - slti(AT, right, Immediate(cond.imm()));
|
| - bne(AT, ZR, l);
|
| - } else if (right == IMM) {
|
| - slti(AT, left, Immediate(cond.imm() + 1));
|
| - beq(AT, ZR, l);
|
| - } else {
|
| - slt(AT, right, left);
|
| - bne(AT, ZR, l);
|
| - }
|
| - break;
|
| - }
|
| - case GE: {
|
| - if (left == ZR) {
|
| - blez(right, l);
|
| - } else if (right == ZR) {
|
| - bgez(left, l);
|
| - } else if (left == IMM) {
|
| - slti(AT, right, Immediate(cond.imm() + 1));
|
| - bne(AT, ZR, l);
|
| - } else if (right == IMM) {
|
| - slti(AT, left, Immediate(cond.imm()));
|
| - beq(AT, ZR, l);
|
| - } else {
|
| - slt(AT, left, right);
|
| - beq(AT, ZR, l);
|
| - }
|
| - break;
|
| - }
|
| - case LT: {
|
| - if (left == ZR) {
|
| - bgtz(right, l);
|
| - } else if (right == ZR) {
|
| - bltz(left, l);
|
| - } else if (left == IMM) {
|
| - slti(AT, right, Immediate(cond.imm() + 1));
|
| - beq(AT, ZR, l);
|
| - } else if (right == IMM) {
|
| - slti(AT, left, Immediate(cond.imm()));
|
| - bne(AT, ZR, l);
|
| - } else {
|
| - slt(AT, left, right);
|
| - bne(AT, ZR, l);
|
| - }
|
| - break;
|
| - }
|
| - case LE: {
|
| - if (left == ZR) {
|
| - bgez(right, l);
|
| - } else if (right == ZR) {
|
| - blez(left, l);
|
| - } else if (left == IMM) {
|
| - slti(AT, right, Immediate(cond.imm()));
|
| - beq(AT, ZR, l);
|
| - } else if (right == IMM) {
|
| - slti(AT, left, Immediate(cond.imm() + 1));
|
| - bne(AT, ZR, l);
|
| - } else {
|
| - slt(AT, right, left);
|
| - beq(AT, ZR, l);
|
| - }
|
| - break;
|
| - }
|
| - case UGT: {
|
| - ASSERT((left != IMM) && (right != IMM)); // No unsigned constants used.
|
| - if (left == ZR) {
|
| - // NV: Never branch. Fall through.
|
| - } else if (right == ZR) {
|
| - bne(left, ZR, l);
|
| - } else {
|
| - sltu(AT, right, left);
|
| - bne(AT, ZR, l);
|
| - }
|
| - break;
|
| - }
|
| - case UGE: {
|
| - ASSERT((left != IMM) && (right != IMM)); // No unsigned constants used.
|
| - if (left == ZR) {
|
| - beq(right, ZR, l);
|
| - } else if (right == ZR) {
|
| - // AL: Always branch to l.
|
| - beq(ZR, ZR, l);
|
| - } else {
|
| - sltu(AT, left, right);
|
| - beq(AT, ZR, l);
|
| - }
|
| - break;
|
| - }
|
| - case ULT: {
|
| - ASSERT((left != IMM) && (right != IMM)); // No unsigned constants used.
|
| - if (left == ZR) {
|
| - bne(right, ZR, l);
|
| - } else if (right == ZR) {
|
| - // NV: Never branch. Fall through.
|
| - } else {
|
| - sltu(AT, left, right);
|
| - bne(AT, ZR, l);
|
| - }
|
| - break;
|
| - }
|
| - case ULE: {
|
| - ASSERT((left != IMM) && (right != IMM)); // No unsigned constants used.
|
| - if (left == ZR) {
|
| - // AL: Always branch to l.
|
| - beq(ZR, ZR, l);
|
| - } else if (right == ZR) {
|
| - beq(left, ZR, l);
|
| - } else {
|
| - sltu(AT, right, left);
|
| - beq(AT, ZR, l);
|
| - }
|
| - break;
|
| - }
|
| - default:
|
| - UNREACHABLE();
|
| - }
|
| - }
|
| -
|
| - void BranchEqual(Register rd, Register rn, Label* l) { beq(rd, rn, l); }
|
| -
|
| - void BranchEqual(Register rd, const Immediate& imm, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (imm.value() == 0) {
|
| - beq(rd, ZR, l);
|
| - } else {
|
| - ASSERT(rd != CMPRES2);
|
| - LoadImmediate(CMPRES2, imm.value());
|
| - beq(rd, CMPRES2, l);
|
| - }
|
| - }
|
| -
|
| - void BranchEqual(Register rd, const Object& object, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - ASSERT(rd != CMPRES2);
|
| - LoadObject(CMPRES2, object);
|
| - beq(rd, CMPRES2, l);
|
| - }
|
| -
|
| - void BranchNotEqual(Register rd, Register rn, Label* l) { bne(rd, rn, l); }
|
| -
|
| - void BranchNotEqual(Register rd, const Immediate& imm, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (imm.value() == 0) {
|
| - bne(rd, ZR, l);
|
| - } else {
|
| - ASSERT(rd != CMPRES2);
|
| - LoadImmediate(CMPRES2, imm.value());
|
| - bne(rd, CMPRES2, l);
|
| - }
|
| - }
|
| -
|
| - void BranchNotEqual(Register rd, const Object& object, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - ASSERT(rd != CMPRES2);
|
| - LoadObject(CMPRES2, object);
|
| - bne(rd, CMPRES2, l);
|
| - }
|
| -
|
| - void BranchSignedGreater(Register rd, Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - slt(CMPRES2, rs, rd); // CMPRES2 = rd > rs ? 1 : 0.
|
| - bne(CMPRES2, ZR, l);
|
| - }
|
| -
|
| - void BranchSignedGreater(Register rd, const Immediate& imm, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (imm.value() == 0) {
|
| - bgtz(rd, l);
|
| - } else {
|
| - if (Utils::IsInt(kImmBits, imm.value() + 1)) {
|
| - slti(CMPRES2, rd, Immediate(imm.value() + 1));
|
| - beq(CMPRES2, ZR, l);
|
| - } else {
|
| - ASSERT(rd != CMPRES2);
|
| - LoadImmediate(CMPRES2, imm.value());
|
| - BranchSignedGreater(rd, CMPRES2, l);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void BranchUnsignedGreater(Register rd, Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - sltu(CMPRES2, rs, rd);
|
| - bne(CMPRES2, ZR, l);
|
| - }
|
| -
|
| - void BranchUnsignedGreater(Register rd, const Immediate& imm, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (imm.value() == 0) {
|
| - BranchNotEqual(rd, Immediate(0), l);
|
| - } else {
|
| - if ((imm.value() != -1) && Utils::IsInt(kImmBits, imm.value() + 1)) {
|
| - sltiu(CMPRES2, rd, Immediate(imm.value() + 1));
|
| - beq(CMPRES2, ZR, l);
|
| - } else {
|
| - ASSERT(rd != CMPRES2);
|
| - LoadImmediate(CMPRES2, imm.value());
|
| - BranchUnsignedGreater(rd, CMPRES2, l);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void BranchSignedGreaterEqual(Register rd, Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - slt(CMPRES2, rd, rs); // CMPRES2 = rd < rs ? 1 : 0.
|
| - beq(CMPRES2, ZR, l); // If CMPRES2 = 0, then rd >= rs.
|
| - }
|
| -
|
| - void BranchSignedGreaterEqual(Register rd, const Immediate& imm, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (imm.value() == 0) {
|
| - bgez(rd, l);
|
| - } else {
|
| - if (Utils::IsInt(kImmBits, imm.value())) {
|
| - slti(CMPRES2, rd, imm);
|
| - beq(CMPRES2, ZR, l);
|
| - } else {
|
| - ASSERT(rd != CMPRES2);
|
| - LoadImmediate(CMPRES2, imm.value());
|
| - BranchSignedGreaterEqual(rd, CMPRES2, l);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void BranchUnsignedGreaterEqual(Register rd, Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - sltu(CMPRES2, rd, rs); // CMPRES2 = rd < rs ? 1 : 0.
|
| - beq(CMPRES2, ZR, l);
|
| - }
|
| -
|
| - void BranchUnsignedGreaterEqual(Register rd, const Immediate& imm, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (imm.value() == 0) {
|
| - b(l);
|
| - } else {
|
| - if (Utils::IsInt(kImmBits, imm.value())) {
|
| - sltiu(CMPRES2, rd, imm);
|
| - beq(CMPRES2, ZR, l);
|
| - } else {
|
| - ASSERT(rd != CMPRES2);
|
| - LoadImmediate(CMPRES2, imm.value());
|
| - BranchUnsignedGreaterEqual(rd, CMPRES2, l);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void BranchSignedLess(Register rd, Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - BranchSignedGreater(rs, rd, l);
|
| - }
|
| -
|
| - void BranchSignedLess(Register rd, const Immediate& imm, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (imm.value() == 0) {
|
| - bltz(rd, l);
|
| - } else {
|
| - if (Utils::IsInt(kImmBits, imm.value())) {
|
| - slti(CMPRES2, rd, imm);
|
| - bne(CMPRES2, ZR, l);
|
| - } else {
|
| - ASSERT(rd != CMPRES2);
|
| - LoadImmediate(CMPRES2, imm.value());
|
| - BranchSignedGreater(CMPRES2, rd, l);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void BranchUnsignedLess(Register rd, Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - BranchUnsignedGreater(rs, rd, l);
|
| - }
|
| -
|
| - void BranchUnsignedLess(Register rd, const Immediate& imm, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (imm.value() == 0) {
|
| - // Never branch. Fall through.
|
| - } else {
|
| - if (Utils::IsInt(kImmBits, imm.value())) {
|
| - sltiu(CMPRES2, rd, imm);
|
| - bne(CMPRES2, ZR, l);
|
| - } else {
|
| - ASSERT(rd != CMPRES2);
|
| - LoadImmediate(CMPRES2, imm.value());
|
| - BranchUnsignedGreater(CMPRES2, rd, l);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void BranchSignedLessEqual(Register rd, Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - BranchSignedGreaterEqual(rs, rd, l);
|
| - }
|
| -
|
| - void BranchSignedLessEqual(Register rd, const Immediate& imm, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (imm.value() == 0) {
|
| - blez(rd, l);
|
| - } else {
|
| - if (Utils::IsInt(kImmBits, imm.value() + 1)) {
|
| - slti(CMPRES2, rd, Immediate(imm.value() + 1));
|
| - bne(CMPRES2, ZR, l);
|
| - } else {
|
| - ASSERT(rd != CMPRES2);
|
| - LoadImmediate(CMPRES2, imm.value());
|
| - BranchSignedGreaterEqual(CMPRES2, rd, l);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void BranchUnsignedLessEqual(Register rd, Register rs, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - BranchUnsignedGreaterEqual(rs, rd, l);
|
| - }
|
| -
|
| - void BranchUnsignedLessEqual(Register rd, const Immediate& imm, Label* l) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (imm.value() == 0) {
|
| - beq(rd, ZR, l);
|
| - } else {
|
| - if ((imm.value() != -1) && Utils::IsInt(kImmBits, imm.value() + 1)) {
|
| - sltiu(CMPRES2, rd, Immediate(imm.value() + 1));
|
| - bne(CMPRES2, ZR, l);
|
| - } else {
|
| - ASSERT(rd != CMPRES2);
|
| - LoadImmediate(CMPRES2, imm.value());
|
| - BranchUnsignedGreaterEqual(CMPRES2, rd, l);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void Push(Register rt) {
|
| - ASSERT(!in_delay_slot_);
|
| - addiu(SP, SP, Immediate(-kWordSize));
|
| - sw(rt, Address(SP));
|
| - }
|
| -
|
| - void Pop(Register rt) {
|
| - ASSERT(!in_delay_slot_);
|
| - lw(rt, Address(SP));
|
| - addiu(SP, SP, Immediate(kWordSize));
|
| - }
|
| -
|
| - void Ret() { jr(RA); }
|
| -
|
| - void SmiTag(Register reg) { sll(reg, reg, kSmiTagSize); }
|
| -
|
| - void SmiTag(Register dst, Register src) { sll(dst, src, kSmiTagSize); }
|
| -
|
| - void SmiUntag(Register reg) { sra(reg, reg, kSmiTagSize); }
|
| -
|
| - void SmiUntag(Register dst, Register src) { sra(dst, src, kSmiTagSize); }
|
| -
|
| - void BranchIfNotSmi(Register reg, Label* label) {
|
| - andi(CMPRES1, reg, Immediate(kSmiTagMask));
|
| - bne(CMPRES1, ZR, label);
|
| - }
|
| -
|
| - void BranchIfSmi(Register reg, Label* label) {
|
| - andi(CMPRES1, reg, Immediate(kSmiTagMask));
|
| - beq(CMPRES1, ZR, label);
|
| - }
|
| -
|
| - void LoadFromOffset(Register reg, Register base, int32_t offset) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (Utils::IsInt(kImmBits, offset)) {
|
| - lw(reg, Address(base, offset));
|
| - } else {
|
| - LoadImmediate(TMP, offset);
|
| - addu(TMP, base, TMP);
|
| - lw(reg, Address(TMP, 0));
|
| - }
|
| - }
|
| -
|
| - void LoadFieldFromOffset(Register reg, Register base, int32_t offset) {
|
| - LoadFromOffset(reg, base, offset - kHeapObjectTag);
|
| - }
|
| -
|
| - void StoreToOffset(Register reg, Register base, int32_t offset) {
|
| - ASSERT(!in_delay_slot_);
|
| - if (Utils::IsInt(kImmBits, offset)) {
|
| - sw(reg, Address(base, offset));
|
| - } else {
|
| - LoadImmediate(TMP, offset);
|
| - addu(TMP, base, TMP);
|
| - sw(reg, Address(TMP, 0));
|
| - }
|
| - }
|
| -
|
| - void StoreFieldToOffset(Register reg, Register base, int32_t offset) {
|
| - StoreToOffset(reg, base, offset - kHeapObjectTag);
|
| - }
|
| -
|
| -
|
| - void StoreDToOffset(DRegister reg, Register base, int32_t offset) {
|
| - ASSERT(!in_delay_slot_);
|
| - FRegister lo = static_cast<FRegister>(reg * 2);
|
| - FRegister hi = static_cast<FRegister>(reg * 2 + 1);
|
| - swc1(lo, Address(base, offset));
|
| - swc1(hi, Address(base, offset + kWordSize));
|
| - }
|
| -
|
| - void LoadDFromOffset(DRegister reg, Register base, int32_t offset) {
|
| - ASSERT(!in_delay_slot_);
|
| - FRegister lo = static_cast<FRegister>(reg * 2);
|
| - FRegister hi = static_cast<FRegister>(reg * 2 + 1);
|
| - lwc1(lo, Address(base, offset));
|
| - lwc1(hi, Address(base, offset + kWordSize));
|
| - }
|
| -
|
| - // dest gets the address of the following instruction. If temp is given,
|
| - // RA is preserved using it as a temporary.
|
| - void GetNextPC(Register dest, Register temp = kNoRegister);
|
| -
|
| - void ReserveAlignedFrameSpace(intptr_t frame_space);
|
| -
|
| - // Create a frame for calling into runtime that preserves all volatile
|
| - // registers. Frame's SP is guaranteed to be correctly aligned and
|
| - // frame_space bytes are reserved under it.
|
| - void EnterCallRuntimeFrame(intptr_t frame_space);
|
| - void LeaveCallRuntimeFrame();
|
| -
|
| - void LoadObject(Register rd, const Object& object);
|
| - void LoadUniqueObject(Register rd, const Object& object);
|
| - void LoadFunctionFromCalleePool(Register dst,
|
| - const Function& function,
|
| - Register new_pp);
|
| - void LoadNativeEntry(Register rd,
|
| - const ExternalLabel* label,
|
| - Patchability patchable);
|
| - void PushObject(const Object& object);
|
| -
|
| - void LoadIsolate(Register result);
|
| -
|
| - void LoadClassId(Register result, Register object);
|
| - void LoadClassById(Register result, Register class_id);
|
| - void LoadClass(Register result, Register object);
|
| - void LoadClassIdMayBeSmi(Register result, Register object);
|
| - void LoadTaggedClassIdMayBeSmi(Register result, Register object);
|
| -
|
| - void StoreIntoObject(Register object, // Object we are storing into.
|
| - const Address& dest, // Where we are storing into.
|
| - Register value, // Value we are storing.
|
| - bool can_value_be_smi = true);
|
| - void StoreIntoObjectOffset(Register object,
|
| - int32_t offset,
|
| - Register value,
|
| - bool can_value_be_smi = true);
|
| -
|
| - void StoreIntoObjectNoBarrier(Register object,
|
| - const Address& dest,
|
| - Register value);
|
| - void StoreIntoObjectNoBarrierOffset(Register object,
|
| - int32_t offset,
|
| - Register value);
|
| - void StoreIntoObjectNoBarrier(Register object,
|
| - const Address& dest,
|
| - const Object& value);
|
| - void StoreIntoObjectNoBarrierOffset(Register object,
|
| - int32_t offset,
|
| - const Object& value);
|
| -
|
| - void CallRuntime(const RuntimeEntry& entry, intptr_t argument_count);
|
| -
|
| - // Set up a Dart frame on entry with a frame pointer and PC information to
|
| - // enable easy access to the RawInstruction object of code corresponding
|
| - // to this frame.
|
| - void EnterDartFrame(intptr_t frame_size);
|
| - void LeaveDartFrame(RestorePP restore_pp = kRestoreCallerPP);
|
| - void LeaveDartFrameAndReturn(Register ra = RA);
|
| -
|
| - // Set up a Dart frame for a function compiled for on-stack replacement.
|
| - // The frame layout is a normal Dart frame, but the frame is partially set
|
| - // up on entry (it is the frame of the unoptimized code).
|
| - void EnterOsrFrame(intptr_t extra_size);
|
| -
|
| - Address ElementAddressForIntIndex(bool is_external,
|
| - intptr_t cid,
|
| - intptr_t index_scale,
|
| - Register array,
|
| - intptr_t index) const;
|
| - void LoadElementAddressForIntIndex(Register address,
|
| - bool is_external,
|
| - intptr_t cid,
|
| - intptr_t index_scale,
|
| - Register array,
|
| - intptr_t index);
|
| - Address ElementAddressForRegIndex(bool is_load,
|
| - bool is_external,
|
| - intptr_t cid,
|
| - intptr_t index_scale,
|
| - Register array,
|
| - Register index);
|
| - void LoadElementAddressForRegIndex(Register address,
|
| - bool is_load,
|
| - bool is_external,
|
| - intptr_t cid,
|
| - intptr_t index_scale,
|
| - Register array,
|
| - Register index);
|
| -
|
| - void LoadHalfWordUnaligned(Register dst, Register addr, Register tmp);
|
| - void LoadHalfWordUnsignedUnaligned(Register dst, Register addr, Register tmp);
|
| - void StoreHalfWordUnaligned(Register src, Register addr, Register tmp);
|
| - void LoadWordUnaligned(Register dst, Register addr, Register tmp);
|
| - void StoreWordUnaligned(Register src, Register addr, Register tmp);
|
| -
|
| - static Address VMTagAddress() {
|
| - return Address(THR, Thread::vm_tag_offset());
|
| - }
|
| -
|
| - // On some other platforms, we draw a distinction between safe and unsafe
|
| - // smis.
|
| - static bool IsSafe(const Object& object) { return true; }
|
| - static bool IsSafeSmi(const Object& object) { return object.IsSmi(); }
|
| -
|
| - bool constant_pool_allowed() const { return constant_pool_allowed_; }
|
| - void set_constant_pool_allowed(bool b) { constant_pool_allowed_ = b; }
|
| -
|
| - private:
|
| - AssemblerBuffer buffer_;
|
| - ObjectPoolWrapper object_pool_wrapper_;
|
| -
|
| - intptr_t prologue_offset_;
|
| - bool has_single_entry_point_;
|
| - bool use_far_branches_;
|
| - bool delay_slot_available_;
|
| - bool in_delay_slot_;
|
| -
|
| - class CodeComment : public ZoneAllocated {
|
| - public:
|
| - CodeComment(intptr_t pc_offset, const String& comment)
|
| - : pc_offset_(pc_offset), comment_(comment) {}
|
| -
|
| - intptr_t pc_offset() const { return pc_offset_; }
|
| - const String& comment() const { return comment_; }
|
| -
|
| - private:
|
| - intptr_t pc_offset_;
|
| - const String& comment_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(CodeComment);
|
| - };
|
| -
|
| - GrowableArray<CodeComment*> comments_;
|
| -
|
| - bool constant_pool_allowed_;
|
| -
|
| - void BranchLink(const ExternalLabel* label);
|
| - void BranchLink(const Code& code, Patchability patchable);
|
| -
|
| - bool CanLoadFromObjectPool(const Object& object) const;
|
| -
|
| - void LoadWordFromPoolOffset(Register rd, int32_t offset, Register pp = PP);
|
| - void LoadObjectHelper(Register rd, const Object& object, bool is_unique);
|
| -
|
| - void Emit(int32_t value) {
|
| - // Emitting an instruction clears the delay slot state.
|
| - in_delay_slot_ = false;
|
| - delay_slot_available_ = false;
|
| - AssemblerBuffer::EnsureCapacity ensured(&buffer_);
|
| - buffer_.Emit<int32_t>(value);
|
| - }
|
| -
|
| - // Encode CPU instructions according to the types specified in
|
| - // Figures 4-1, 4-2 and 4-3 in VolI-A.
|
| - void EmitIType(Opcode opcode, Register rs, Register rt, uint16_t imm) {
|
| - Emit(opcode << kOpcodeShift | rs << kRsShift | rt << kRtShift | imm);
|
| - }
|
| -
|
| - void EmitLoadStore(Opcode opcode, Register rt, const Address& addr) {
|
| - Emit(opcode << kOpcodeShift | rt << kRtShift | addr.encoding());
|
| - }
|
| -
|
| - void EmitFpuLoadStore(Opcode opcode, FRegister ft, const Address& addr) {
|
| - Emit(opcode << kOpcodeShift | ft << kFtShift | addr.encoding());
|
| - }
|
| -
|
| - void EmitRegImmType(Opcode opcode, Register rs, RtRegImm code, uint16_t imm) {
|
| - Emit(opcode << kOpcodeShift | rs << kRsShift | code << kRtShift | imm);
|
| - }
|
| -
|
| - void EmitJType(Opcode opcode, uint32_t destination) { UNIMPLEMENTED(); }
|
| -
|
| - void EmitRType(Opcode opcode,
|
| - Register rs,
|
| - Register rt,
|
| - Register rd,
|
| - int sa,
|
| - SpecialFunction func) {
|
| - ASSERT(Utils::IsUint(5, sa));
|
| - Emit(opcode << kOpcodeShift | rs << kRsShift | rt << kRtShift |
|
| - rd << kRdShift | sa << kSaShift | func << kFunctionShift);
|
| - }
|
| -
|
| - void EmitFpuRType(Opcode opcode,
|
| - Format fmt,
|
| - FRegister ft,
|
| - FRegister fs,
|
| - FRegister fd,
|
| - Cop1Function func) {
|
| - Emit(opcode << kOpcodeShift | fmt << kFmtShift | ft << kFtShift |
|
| - fs << kFsShift | fd << kFdShift | func << kCop1FnShift);
|
| - }
|
| -
|
| - int32_t EncodeBranchOffset(int32_t offset, int32_t instr);
|
| -
|
| - void EmitFarJump(int32_t offset, bool link);
|
| - void EmitFarBranch(Opcode b, Register rs, Register rt, int32_t offset);
|
| - void EmitFarRegImmBranch(RtRegImm b, Register rs, int32_t offset);
|
| - void EmitFarFpuBranch(bool kind, int32_t offset);
|
| - void EmitBranch(Opcode b, Register rs, Register rt, Label* label);
|
| - void EmitRegImmBranch(RtRegImm b, Register rs, Label* label);
|
| - void EmitFpuBranch(bool kind, Label* label);
|
| -
|
| - void EmitBranchDelayNop() {
|
| - Emit(Instr::kNopInstruction); // Branch delay NOP.
|
| - delay_slot_available_ = true;
|
| - }
|
| -
|
| - void StoreIntoObjectFilter(Register object, Register value, Label* no_update);
|
| -
|
| - // Shorter filtering sequence that assumes that value is not a smi.
|
| - void StoreIntoObjectFilterNoSmi(Register object,
|
| - Register value,
|
| - Label* no_update);
|
| -
|
| - DISALLOW_ALLOCATION();
|
| - DISALLOW_COPY_AND_ASSIGN(Assembler);
|
| -};
|
| -
|
| -} // namespace dart
|
| -
|
| -#endif // RUNTIME_VM_ASSEMBLER_MIPS_H_
|
|
|