| // 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_ |