82367b380d
Also includes changes to keep it from aborting unexpectedly. Among other things, I changed the Disassembler::Disassemble to return false when the underlying decoder can't decode an instruction. Then, the MIPS SimulatorDebugger will refuse to step or cont on an instruction that the Disassembler can't decode. Review URL: https://codereview.chromium.org//12431016 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@19958 260f80e4-7a28-3924-810f-c04153c831b5
429 lines
11 KiB
C++
429 lines
11 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 "vm/constants_mips.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 {
|
|
|
|
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:
|
|
Address(Register base, int32_t offset = 0)
|
|
: ValueObject(), base_(base), offset_(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(16, offset_));
|
|
uint16_t imm_value = static_cast<uint16_t>(offset_);
|
|
return (base_ << kRsShift) | imm_value;
|
|
}
|
|
|
|
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.
|
|
int 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:
|
|
int position_;
|
|
|
|
void Reinitialize() {
|
|
position_ = 0;
|
|
}
|
|
|
|
void BindTo(int position) {
|
|
ASSERT(!IsBound());
|
|
position_ = -position - kWordSize;
|
|
ASSERT(IsBound());
|
|
}
|
|
|
|
void LinkTo(int position) {
|
|
ASSERT(!IsBound());
|
|
position_ = position + kWordSize;
|
|
ASSERT(IsLinked());
|
|
}
|
|
|
|
friend class Assembler;
|
|
DISALLOW_COPY_AND_ASSIGN(Label);
|
|
};
|
|
|
|
|
|
class CPUFeatures : public AllStatic {
|
|
public:
|
|
static void InitOnce() { }
|
|
static bool double_truncate_round_supported() {
|
|
UNIMPLEMENTED();
|
|
return false;
|
|
}
|
|
};
|
|
|
|
|
|
class Assembler : public ValueObject {
|
|
public:
|
|
Assembler()
|
|
: buffer_(),
|
|
object_pool_(GrowableObjectArray::Handle()),
|
|
prologue_offset_(-1),
|
|
delay_slot_available_(false),
|
|
in_delay_slot_(false),
|
|
comments_() { }
|
|
~Assembler() { }
|
|
|
|
void PopRegister(Register r) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
void Bind(Label* label) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
// Misc. functionality
|
|
int CodeSize() const { return buffer_.Size(); }
|
|
int prologue_offset() const { return -1; }
|
|
const ZoneGrowableArray<int>& GetPointerOffsets() const {
|
|
return buffer_.pointer_offsets();
|
|
}
|
|
const GrowableObjectArray& object_pool() const { return object_pool_; }
|
|
void FinalizeInstructions(const MemoryRegion& region) {
|
|
buffer_.FinalizeInstructions(region);
|
|
}
|
|
|
|
// 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) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
// Set up a stub frame so that the stack traversal code can easily identify
|
|
// a stub frame.
|
|
void EnterStubFrame() {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
// 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.
|
|
static const intptr_t kOffsetOfSavedPCfromEntrypoint = -1; // UNIMPLEMENTED.
|
|
|
|
// 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,
|
|
bool near_jump,
|
|
Register instance_reg) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
// Debugging and bringup support.
|
|
void Stop(const char* message) { UNIMPLEMENTED(); }
|
|
void Unimplemented(const char* message);
|
|
void Untested(const char* message);
|
|
void Unreachable(const char* message);
|
|
|
|
static void InitializeMemoryWithBreakpoints(uword data, int length);
|
|
|
|
void Comment(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
|
|
|
|
const Code::Comments& GetCodeComments() const;
|
|
|
|
static const char* RegisterName(Register reg) {
|
|
UNIMPLEMENTED();
|
|
return NULL;
|
|
}
|
|
|
|
static const char* FpuRegisterName(FpuRegister reg) {
|
|
UNIMPLEMENTED();
|
|
return NULL;
|
|
}
|
|
|
|
// 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.
|
|
void addiu(Register rt, Register rs, const Immediate& imm) {
|
|
ASSERT(Utils::IsInt(16, imm.value()));
|
|
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(16, imm.value()));
|
|
uint16_t imm_value = static_cast<uint16_t>(imm.value());
|
|
EmitIType(ANDI, rs, rt, imm_value);
|
|
}
|
|
|
|
void break_(int32_t code) {
|
|
ASSERT(Utils::IsUint(20, code));
|
|
Emit(SPECIAL << kOpcodeShift |
|
|
code << kBreakCodeShift |
|
|
BREAK << kFunctionShift);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void div(Register rs, Register rt) {
|
|
EmitRType(SPECIAL, rs, rt, R0, 0, DIV);
|
|
}
|
|
|
|
void divu(Register rs, Register rt) {
|
|
EmitRType(SPECIAL, rs, rt, R0, 0, DIVU);
|
|
}
|
|
|
|
|
|
void lb(Register rt, const Address& addr) {
|
|
EmitLoadStore(LB, rt, addr);
|
|
}
|
|
|
|
void lbu(Register rt, const Address& addr) {
|
|
EmitLoadStore(LBU, rt, 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(16, 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 mfhi(Register rd) {
|
|
EmitRType(SPECIAL, R0, R0, rd, 0, MFHI);
|
|
}
|
|
|
|
void mflo(Register rd) {
|
|
EmitRType(SPECIAL, R0, R0, rd, 0, MFLO);
|
|
}
|
|
|
|
void ori(Register rt, Register rs, const Immediate& imm) {
|
|
ASSERT(Utils::IsUint(16, imm.value()));
|
|
uint16_t imm_value = static_cast<uint16_t>(imm.value());
|
|
EmitIType(ORI, rs, rt, imm_value);
|
|
}
|
|
|
|
void jr(Register rs) {
|
|
ASSERT(!in_delay_slot_); // Jump within a delay slot is not supported.
|
|
EmitRType(SPECIAL, rs, R0, R0, 0, JR);
|
|
Emit(Instr::kNopInstruction); // Branch delay NOP.
|
|
delay_slot_available_ = true;
|
|
}
|
|
|
|
void sb(Register rt, const Address& addr) {
|
|
EmitLoadStore(SB, rt, addr);
|
|
}
|
|
|
|
void sh(Register rt, const Address& addr) {
|
|
EmitLoadStore(SH, rt, addr);
|
|
}
|
|
|
|
void sw(Register rt, const Address& addr) {
|
|
EmitLoadStore(SW, rt, addr);
|
|
}
|
|
|
|
void sll(Register rd, Register rt, int sa) {
|
|
EmitRType(SPECIAL, R0, rt, rd, sa, SLL);
|
|
}
|
|
|
|
// Macros.
|
|
void LoadImmediate(Register rd, int32_t value) {
|
|
lui(rd, Immediate((value >> 16) & 0xffff));
|
|
ori(rd, rd, Immediate(value & 0xffff));
|
|
}
|
|
|
|
private:
|
|
AssemblerBuffer buffer_;
|
|
GrowableObjectArray& object_pool_; // Objects and patchable jump targets.
|
|
int prologue_offset_;
|
|
|
|
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_;
|
|
|
|
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 EmitRegImmType(Opcode opcode,
|
|
Register rs,
|
|
RtRegImm code,
|
|
uint16_t imm) {
|
|
Emit(opcode << kOpcodeShift |
|
|
rs << kRsShift |
|
|
code << kRtShift |
|
|
imm);
|
|
}
|
|
|
|
void EmitJType(Opcode opcode, Label* label) {
|
|
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);
|
|
}
|
|
|
|
DISALLOW_ALLOCATION();
|
|
DISALLOW_COPY_AND_ASSIGN(Assembler);
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // VM_ASSEMBLER_MIPS_H_
|