240afa6afa
Since Register is an enum type, it is automatically coerced to integer types when passed as a parameter to functions expecting an integer argument. This change expands the use of Immediate and Operand wrappers for Assembler instructions and macros to avoid this automatic coercion. I've also added dummy constructors to Address and FieldAddress classes to address the same problem, but in a way that does not increase verbosity, since Address and FieldAddress are much more prevalent in the code. A cleaner solution would involve making Register no longer coercable to integer types, but this would likely require many changes to the Assemblers. R=regis@google.com Review URL: https://codereview.chromium.org//593363003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@40905 260f80e4-7a28-3924-810f-c04153c831b5
1419 lines
41 KiB
C++
1419 lines
41 KiB
C++
// 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 VM_ASSEMBLER_MIPS_H_
|
|
#define VM_ASSEMBLER_MIPS_H_
|
|
|
|
#ifndef 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/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 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);
|
|
};
|
|
|
|
|
|
class Assembler : public ValueObject {
|
|
public:
|
|
explicit Assembler(bool use_far_branches = false)
|
|
: buffer_(),
|
|
object_pool_(GrowableObjectArray::Handle()),
|
|
prologue_offset_(-1),
|
|
use_far_branches_(use_far_branches),
|
|
delay_slot_available_(false),
|
|
in_delay_slot_(false),
|
|
comments_(),
|
|
allow_constant_pool_(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_; }
|
|
|
|
// 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();
|
|
}
|
|
const GrowableObjectArray& object_pool() const { return object_pool_; }
|
|
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) {
|
|
ASSERT(buffer_.Size() == 0);
|
|
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(bool load_pp = false);
|
|
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);
|
|
|
|
// Instruction pattern from entrypoint is used in dart frame prologs
|
|
// to set up the frame and save a PC which can be used to figure out the
|
|
// RawInstruction object corresponding to the code running in the frame.
|
|
// See EnterDartFrame. There are 6 instructions before we know the PC.
|
|
static const intptr_t kEntryPointToPcMarkerOffset = 6 * Instr::kInstrSize;
|
|
static intptr_t EntryPointToPcMarkerOffset() {
|
|
return kEntryPointToPcMarkerOffset;
|
|
}
|
|
|
|
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);
|
|
|
|
|
|
// 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);
|
|
|
|
// TODO(zra): TraceSimMsg enables printing of helpful messages when
|
|
// --trace_sim is given. Eventually these calls will be changed to Comment.
|
|
void TraceSimMsg(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();
|
|
}
|
|
|
|
void break_(int32_t code) {
|
|
ASSERT(Utils::IsUint(20, code));
|
|
Emit(SPECIAL << kOpcodeShift |
|
|
code << kBreakCodeShift |
|
|
BREAK << kFunctionShift);
|
|
}
|
|
|
|
// 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 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);
|
|
}
|
|
|
|
// Converts a 64-bit signed int in fs to a double in fd.
|
|
void cvtdl(DRegister dd, DRegister ds) {
|
|
FRegister fs = static_cast<FRegister>(ds * 2);
|
|
FRegister fd = static_cast<FRegister>(dd * 2);
|
|
EmitFpuRType(COP1, FMT_L, F0, fs, fd, COP1_CVT_D);
|
|
}
|
|
|
|
void cvtsd(FRegister fd, DRegister ds) {
|
|
FRegister fs = static_cast<FRegister>(ds * 2);
|
|
EmitFpuRType(COP1, FMT_D, F0, fs, fd, COP1_CVT_S);
|
|
}
|
|
|
|
void cvtwd(FRegister fd, DRegister ds) {
|
|
FRegister fs = static_cast<FRegister>(ds * 2);
|
|
EmitFpuRType(COP1, FMT_D, F0, fs, fd, COP1_CVT_W);
|
|
}
|
|
|
|
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 lui(Register rt, const Immediate& imm) {
|
|
ASSERT(Utils::IsUint(kImmBits, imm.value()));
|
|
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 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()));
|
|
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);
|
|
}
|
|
|
|
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()));
|
|
int16_t imm_value = static_cast<int16_t>(imm.value());
|
|
EmitIType(SLTI, rs, rt, imm_value);
|
|
}
|
|
|
|
void sltiu(Register rt, Register rs, const Immediate& imm) {
|
|
ASSERT(Utils::IsUint(kImmBits, imm.value()));
|
|
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 ExternalLabel* label) {
|
|
ASSERT(!in_delay_slot_);
|
|
LoadImmediate(TMP, label->address());
|
|
jr(TMP);
|
|
}
|
|
|
|
void BranchPatchable(const ExternalLabel* label) {
|
|
ASSERT(!in_delay_slot_);
|
|
const uint16_t low = Utils::Low16Bits(label->address());
|
|
const uint16_t high = Utils::High16Bits(label->address());
|
|
lui(T9, Immediate(high));
|
|
ori(T9, T9, Immediate(low));
|
|
jr(T9);
|
|
delay_slot_available_ = false; // CodePatcher expects a nop.
|
|
}
|
|
|
|
void BranchLink(const ExternalLabel* label) {
|
|
ASSERT(!in_delay_slot_);
|
|
LoadImmediate(T9, label->address());
|
|
jalr(T9);
|
|
}
|
|
|
|
void BranchLinkPatchable(const ExternalLabel* label) {
|
|
ASSERT(!in_delay_slot_);
|
|
const int32_t offset =
|
|
Array::data_offset() + 4*AddExternalLabel(label) - kHeapObjectTag;
|
|
LoadWordFromPoolOffset(T9, offset);
|
|
jalr(T9);
|
|
delay_slot_available_ = false; // CodePatcher expects a nop.
|
|
}
|
|
|
|
void Drop(intptr_t stack_elements) {
|
|
ASSERT(stack_elements >= 0);
|
|
if (stack_elements > 0) {
|
|
addiu(SP, SP, Immediate(stack_elements * kWordSize));
|
|
}
|
|
}
|
|
|
|
void LoadPoolPointer() {
|
|
ASSERT(!in_delay_slot_);
|
|
GetNextPC(TMP); // TMP gets the address of the next instruction.
|
|
const intptr_t object_pool_pc_dist =
|
|
Instructions::HeaderSize() - Instructions::object_pool_offset() +
|
|
CodeSize();
|
|
lw(PP, Address(TMP, -object_pool_pc_dist));
|
|
}
|
|
|
|
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 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 {
|
|
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 {
|
|
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::IsUint(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_);
|
|
ASSERT(imm.value() != 0);
|
|
if (Utils::IsUint(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 {
|
|
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_);
|
|
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 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 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 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 LoadWordFromPoolOffset(Register rd, int32_t offset);
|
|
void LoadObject(Register rd, const Object& object);
|
|
void PushObject(const Object& object);
|
|
|
|
// Compares rn with the object. Returns results in rd1 and rd2.
|
|
// rd1 is 1 if rn < object. rd2 is 1 if object < rn. Since both cannot be
|
|
// 1, rd1 == rd2 (== 0) iff rn == object.
|
|
void CompareObject(Register rd1, Register rd2,
|
|
Register rn, 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 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();
|
|
void LeaveDartFrameAndReturn();
|
|
|
|
// 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;
|
|
Address ElementAddressForRegIndex(bool is_load,
|
|
bool is_external,
|
|
intptr_t cid,
|
|
intptr_t index_scale,
|
|
Register array,
|
|
Register index);
|
|
|
|
// 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 allow_constant_pool() const {
|
|
return allow_constant_pool_;
|
|
}
|
|
void set_allow_constant_pool(bool b) {
|
|
allow_constant_pool_ = b;
|
|
}
|
|
|
|
private:
|
|
AssemblerBuffer buffer_;
|
|
GrowableObjectArray& object_pool_; // Objects and patchable jump targets.
|
|
intptr_t prologue_offset_;
|
|
|
|
bool use_far_branches_;
|
|
bool delay_slot_available_;
|
|
bool in_delay_slot_;
|
|
|
|
int32_t AddObject(const Object& obj);
|
|
int32_t AddExternalLabel(const ExternalLabel* label);
|
|
|
|
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 allow_constant_pool_;
|
|
|
|
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 // VM_ASSEMBLER_MIPS_H_
|