MIPS: pre-crankshaft updates to assembler and related files. (1/3)
Highlights: - assembler.h adds FPU definitions used for Crankshaft. - Support optimization of mips call: jalr->jal - includes changes to set_target_address_at(), support routines. - Add 2nd use of Apply() to update target addresses. - Minor debugging improvement in simulator. BUG= TEST= Review URL: http://codereview.chromium.org/7888003 Patch from Paul Lind <plind44@gmail.com>. git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9259 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
e645597aa7
commit
aa00dbdc40
@ -83,6 +83,14 @@ bool Operand::is_reg() const {
|
||||
// RelocInfo.
|
||||
|
||||
void RelocInfo::apply(intptr_t delta) {
|
||||
if (IsCodeTarget(rmode_)) {
|
||||
uint32_t scope1 = (uint32_t) target_address() & ~kImm28Mask;
|
||||
uint32_t scope2 = reinterpret_cast<uint32_t>(pc_) & ~kImm28Mask;
|
||||
|
||||
if (scope1 != scope2) {
|
||||
Assembler::JumpLabelToJumpRegister(pc_);
|
||||
}
|
||||
}
|
||||
if (IsInternalReference(rmode_)) {
|
||||
// Absolute code pointer inside code object moves with the code object.
|
||||
byte* p = reinterpret_cast<byte*>(pc_);
|
||||
@ -218,8 +226,9 @@ bool RelocInfo::IsPatchedReturnSequence() {
|
||||
Instr instr2 = Assembler::instr_at(pc_ + 2 * Assembler::kInstrSize);
|
||||
bool patched_return = ((instr0 & kOpcodeMask) == LUI &&
|
||||
(instr1 & kOpcodeMask) == ORI &&
|
||||
(instr2 & kOpcodeMask) == SPECIAL &&
|
||||
(instr2 & kFunctionFieldMask) == JALR);
|
||||
((instr2 & kOpcodeMask) == JAL ||
|
||||
((instr2 & kOpcodeMask) == SPECIAL &&
|
||||
(instr2 & kFunctionFieldMask) == JALR)));
|
||||
return patched_return;
|
||||
}
|
||||
|
||||
|
@ -172,7 +172,8 @@ Register ToRegister(int num) {
|
||||
// -----------------------------------------------------------------------------
|
||||
// Implementation of RelocInfo.
|
||||
|
||||
const int RelocInfo::kApplyMask = 1 << RelocInfo::INTERNAL_REFERENCE;
|
||||
const int RelocInfo::kApplyMask = RelocInfo::kCodeTargetMask |
|
||||
1 << RelocInfo::INTERNAL_REFERENCE;
|
||||
|
||||
|
||||
bool RelocInfo::IsCodedSpecially() {
|
||||
@ -546,6 +547,19 @@ bool Assembler::IsJ(Instr instr) {
|
||||
}
|
||||
|
||||
|
||||
bool Assembler::IsJal(Instr instr) {
|
||||
return GetOpcodeField(instr) == JAL;
|
||||
}
|
||||
|
||||
bool Assembler::IsJr(Instr instr) {
|
||||
return GetOpcodeField(instr) == SPECIAL && GetFunctionField(instr) == JR;
|
||||
}
|
||||
|
||||
bool Assembler::IsJalr(Instr instr) {
|
||||
return GetOpcodeField(instr) == SPECIAL && GetFunctionField(instr) == JALR;
|
||||
}
|
||||
|
||||
|
||||
bool Assembler::IsLui(Instr instr) {
|
||||
uint32_t opcode = GetOpcodeField(instr);
|
||||
// Checks if the instruction is a load upper immediate.
|
||||
@ -939,7 +953,7 @@ void Assembler::GenInstrImmediate(Opcode opcode,
|
||||
|
||||
|
||||
void Assembler::GenInstrJump(Opcode opcode,
|
||||
uint32_t address) {
|
||||
uint32_t address) {
|
||||
BlockTrampolinePoolScope block_trampoline_pool(this);
|
||||
ASSERT(is_uint26(address));
|
||||
Instr instr = opcode | address;
|
||||
@ -1112,7 +1126,12 @@ void Assembler::bne(Register rs, Register rt, int16_t offset) {
|
||||
|
||||
|
||||
void Assembler::j(int32_t target) {
|
||||
ASSERT(is_uint28(target) && ((target & 3) == 0));
|
||||
#if DEBUG
|
||||
// Get pc of delay slot.
|
||||
uint32_t ipc = reinterpret_cast<uint32_t>(pc_ + 1 * kInstrSize);
|
||||
bool in_range = ((uint32_t)(ipc^target) >> (kImm26Bits+kImmFieldShift)) == 0;
|
||||
ASSERT(in_range && ((target & 3) == 0));
|
||||
#endif
|
||||
GenInstrJump(J, target >> 2);
|
||||
}
|
||||
|
||||
@ -1128,8 +1147,13 @@ void Assembler::jr(Register rs) {
|
||||
|
||||
|
||||
void Assembler::jal(int32_t target) {
|
||||
#ifdef DEBUG
|
||||
// Get pc of delay slot.
|
||||
uint32_t ipc = reinterpret_cast<uint32_t>(pc_ + 1 * kInstrSize);
|
||||
bool in_range = ((uint32_t)(ipc^target) >> (kImm26Bits+kImmFieldShift)) == 0;
|
||||
ASSERT(in_range && ((target & 3) == 0));
|
||||
#endif
|
||||
positions_recorder()->WriteRecordedPositions();
|
||||
ASSERT(is_uint28(target) && ((target & 3) == 0));
|
||||
GenInstrJump(JAL, target >> 2);
|
||||
}
|
||||
|
||||
@ -1142,6 +1166,32 @@ void Assembler::jalr(Register rs, Register rd) {
|
||||
}
|
||||
|
||||
|
||||
void Assembler::j_or_jr(int32_t target, Register rs) {
|
||||
// Get pc of delay slot.
|
||||
uint32_t ipc = reinterpret_cast<uint32_t>(pc_ + 1 * kInstrSize);
|
||||
bool in_range = ((uint32_t)(ipc^target) >> (kImm26Bits+kImmFieldShift)) == 0;
|
||||
|
||||
if (in_range) {
|
||||
j(target);
|
||||
} else {
|
||||
jr(t9);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Assembler::jal_or_jalr(int32_t target, Register rs) {
|
||||
// Get pc of delay slot.
|
||||
uint32_t ipc = reinterpret_cast<uint32_t>(pc_ + 1 * kInstrSize);
|
||||
bool in_range = ((uint32_t)(ipc^target) >> (kImm26Bits+kImmFieldShift)) == 0;
|
||||
|
||||
if (in_range) {
|
||||
jal(target);
|
||||
} else {
|
||||
jalr(t9);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------Data-processing-instructions---------
|
||||
|
||||
// Arithmetic.
|
||||
@ -1614,6 +1664,13 @@ void Assembler::cfc1(Register rt, FPUControlRegister fs) {
|
||||
GenInstrRegister(COP1, CFC1, rt, fs);
|
||||
}
|
||||
|
||||
void Assembler::DoubleAsTwoUInt32(double d, uint32_t* lo, uint32_t* hi) {
|
||||
uint64_t i;
|
||||
memcpy(&i, &d, 8);
|
||||
|
||||
*lo = i & 0xffffffff;
|
||||
*hi = i >> 32;
|
||||
}
|
||||
|
||||
// Arithmetic.
|
||||
|
||||
@ -1972,10 +2029,15 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
|
||||
}
|
||||
if (rinfo.rmode() != RelocInfo::NONE) {
|
||||
// Don't record external references unless the heap will be serialized.
|
||||
if (rmode == RelocInfo::EXTERNAL_REFERENCE &&
|
||||
!Serializer::enabled() &&
|
||||
!FLAG_debug_code) {
|
||||
return;
|
||||
if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
|
||||
#ifdef DEBUG
|
||||
if (!Serializer::enabled()) {
|
||||
Serializer::TooLateToEnableNow();
|
||||
}
|
||||
#endif
|
||||
if (!Serializer::enabled() && !emit_debug_code()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ASSERT(buffer_space() >= kMaxRelocSize); // Too late to grow buffer here.
|
||||
if (rmode == RelocInfo::CODE_TARGET_WITH_ID) {
|
||||
@ -2070,30 +2132,142 @@ Address Assembler::target_address_at(Address pc) {
|
||||
}
|
||||
|
||||
|
||||
// On Mips, a target address is stored in a lui/ori instruction pair, each
|
||||
// of which load 16 bits of the 32-bit address to a register.
|
||||
// Patching the address must replace both instr, and flush the i-cache.
|
||||
//
|
||||
// There is an optimization below, which emits a nop when the address
|
||||
// fits in just 16 bits. This is unlikely to help, and should be benchmarked,
|
||||
// and possibly removed.
|
||||
void Assembler::set_target_address_at(Address pc, Address target) {
|
||||
// On MIPS we patch the address into lui/ori instruction pair.
|
||||
|
||||
// First check we have an li (lui/ori pair).
|
||||
Instr instr2 = instr_at(pc + kInstrSize);
|
||||
#ifdef DEBUG
|
||||
Instr instr1 = instr_at(pc);
|
||||
|
||||
// Check we have indeed the result from a li with MustUseReg true.
|
||||
CHECK((GetOpcodeField(instr1) == LUI && GetOpcodeField(instr2) == ORI));
|
||||
#endif
|
||||
|
||||
uint32_t rt_code = GetRtField(instr2);
|
||||
uint32_t* p = reinterpret_cast<uint32_t*>(pc);
|
||||
uint32_t itarget = reinterpret_cast<uint32_t>(target);
|
||||
|
||||
// lui rt, high-16.
|
||||
// ori rt rt, low-16.
|
||||
#ifdef DEBUG
|
||||
// Check we have the result from a li macro-instruction, using instr pair.
|
||||
Instr instr1 = instr_at(pc);
|
||||
CHECK((GetOpcodeField(instr1) == LUI && GetOpcodeField(instr2) == ORI));
|
||||
#endif
|
||||
|
||||
// Must use 2 instructions to insure patchable code => just use lui and ori.
|
||||
// lui rt, upper-16.
|
||||
// ori rt rt, lower-16.
|
||||
*p = LUI | rt_code | ((itarget & kHiMask) >> kLuiShift);
|
||||
*(p+1) = ORI | rt_code | (rt_code << 5) | (itarget & kImm16Mask);
|
||||
|
||||
CPU::FlushICache(pc, 2 * sizeof(int32_t));
|
||||
// The following code is an optimization for the common case of Call()
|
||||
// or Jump() which is load to register, and jump through register:
|
||||
// li(t9, address); jalr(t9) (or jr(t9)).
|
||||
// If the destination address is in the same 256 MB page as the call, it
|
||||
// is faster to do a direct jal, or j, rather than jump thru register, since
|
||||
// that lets the cpu pipeline prefetch the target address. However each
|
||||
// time the address above is patched, we have to patch the direct jal/j
|
||||
// instruction, as well as possibly revert to jalr/jr if we now cross a
|
||||
// 256 MB page. Note that with the jal/j instructions, we do not need to
|
||||
// load the register, but that code is left, since it makes it easy to
|
||||
// revert this process. A further optimization could try replacing the
|
||||
// li sequence with nops.
|
||||
// This optimization can only be applied if the rt-code from instr2 is the
|
||||
// register used for the jalr/jr. Finally, we have to skip 'jr ra', which is
|
||||
// mips return. Occasionally this lands after an li().
|
||||
|
||||
Instr instr3 = instr_at(pc + 2 * kInstrSize);
|
||||
uint32_t ipc = reinterpret_cast<uint32_t>(pc + 3 * kInstrSize);
|
||||
bool in_range =
|
||||
((uint32_t)(ipc ^ itarget) >> (kImm26Bits + kImmFieldShift)) == 0;
|
||||
uint32_t target_field = (uint32_t)(itarget & kJumpAddrMask) >> kImmFieldShift;
|
||||
bool patched_jump = false;
|
||||
|
||||
#ifndef ALLOW_JAL_IN_BOUNDARY_REGION
|
||||
// This is a workaround to the 24k core E156 bug (affect some 34k cores also).
|
||||
// Since the excluded space is only 64KB out of 256MB (0.02 %), we will just
|
||||
// apply this workaround for all cores so we don't have to identify the core.
|
||||
if (in_range) {
|
||||
// The 24k core E156 bug has some very specific requirements, we only check
|
||||
// the most simple one: if the address of the delay slot instruction is in
|
||||
// the first or last 32 KB of the 256 MB segment.
|
||||
uint32_t segment_mask = ((256 * MB) - 1) ^ ((32 * KB) - 1);
|
||||
uint32_t ipc_segment_addr = ipc & segment_mask;
|
||||
if (ipc_segment_addr == 0 || ipc_segment_addr == segment_mask)
|
||||
in_range = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (IsJalr(instr3)) {
|
||||
// Try to convert JALR to JAL.
|
||||
if (in_range && GetRt(instr2) == GetRs(instr3)) {
|
||||
*(p+2) = JAL | target_field;
|
||||
patched_jump = true;
|
||||
}
|
||||
} else if (IsJr(instr3)) {
|
||||
// Try to convert JR to J, skip returns (jr ra).
|
||||
bool is_ret = static_cast<int>(GetRs(instr3)) == ra.code();
|
||||
if (in_range && !is_ret && GetRt(instr2) == GetRs(instr3)) {
|
||||
*(p+2) = J | target_field;
|
||||
patched_jump = true;
|
||||
}
|
||||
} else if (IsJal(instr3)) {
|
||||
if (in_range) {
|
||||
// We are patching an already converted JAL.
|
||||
*(p+2) = JAL | target_field;
|
||||
} else {
|
||||
// Patch JAL, but out of range, revert to JALR.
|
||||
// JALR rs reg is the rt reg specified in the ORI instruction.
|
||||
uint32_t rs_field = GetRt(instr2) << kRsShift;
|
||||
uint32_t rd_field = ra.code() << kRdShift; // Return-address (ra) reg.
|
||||
*(p+2) = SPECIAL | rs_field | rd_field | JALR;
|
||||
}
|
||||
patched_jump = true;
|
||||
} else if (IsJ(instr3)) {
|
||||
if (in_range) {
|
||||
// We are patching an already converted J (jump).
|
||||
*(p+2) = J | target_field;
|
||||
} else {
|
||||
// Trying patch J, but out of range, just go back to JR.
|
||||
// JR 'rs' reg is the 'rt' reg specified in the ORI instruction (instr2).
|
||||
uint32_t rs_field = GetRt(instr2) << kRsShift;
|
||||
*(p+2) = SPECIAL | rs_field | JR;
|
||||
}
|
||||
patched_jump = true;
|
||||
}
|
||||
|
||||
CPU::FlushICache(pc, (patched_jump ? 3 : 2) * sizeof(int32_t));
|
||||
}
|
||||
|
||||
void Assembler::JumpLabelToJumpRegister(Address pc) {
|
||||
// Address pc points to lui/ori instructions.
|
||||
// Jump to label may follow at pc + 2 * kInstrSize.
|
||||
uint32_t* p = reinterpret_cast<uint32_t*>(pc);
|
||||
#ifdef DEBUG
|
||||
Instr instr1 = instr_at(pc);
|
||||
#endif
|
||||
Instr instr2 = instr_at(pc + 1 * kInstrSize);
|
||||
Instr instr3 = instr_at(pc + 2 * kInstrSize);
|
||||
bool patched = false;
|
||||
|
||||
if (IsJal(instr3)) {
|
||||
ASSERT(GetOpcodeField(instr1) == LUI);
|
||||
ASSERT(GetOpcodeField(instr2) == ORI);
|
||||
|
||||
uint32_t rs_field = GetRt(instr2) << kRsShift;
|
||||
uint32_t rd_field = ra.code() << kRdShift; // Return-address (ra) reg.
|
||||
*(p+2) = SPECIAL | rs_field | rd_field | JALR;
|
||||
patched = true;
|
||||
} else if (IsJ(instr3)) {
|
||||
ASSERT(GetOpcodeField(instr1) == LUI);
|
||||
ASSERT(GetOpcodeField(instr2) == ORI);
|
||||
|
||||
uint32_t rs_field = GetRt(instr2) << kRsShift;
|
||||
*(p+2) = SPECIAL | rs_field | JR;
|
||||
patched = true;
|
||||
}
|
||||
|
||||
if (patched) {
|
||||
CPU::FlushICache(pc+2, sizeof(Address));
|
||||
}
|
||||
}
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
|
@ -168,24 +168,36 @@ Register ToRegister(int num);
|
||||
// Coprocessor register.
|
||||
struct FPURegister {
|
||||
static const int kNumRegisters = v8::internal::kNumFPURegisters;
|
||||
// f0 has been excluded from allocation. This is following ia32
|
||||
// where xmm0 is excluded.
|
||||
static const int kNumAllocatableRegisters = 15;
|
||||
|
||||
// TODO(plind): Warning, inconsistent numbering here. kNumFPURegisters refers
|
||||
// to number of 32-bit FPU regs, but kNumAllocatableRegisters refers to
|
||||
// number of Double regs (64-bit regs, or FPU-reg-pairs).
|
||||
|
||||
// A few double registers are reserved: one as a scratch register and one to
|
||||
// hold 0.0.
|
||||
// f28: 0.0
|
||||
// f30: scratch register.
|
||||
static const int kNumReservedRegisters = 2;
|
||||
static const int kNumAllocatableRegisters = kNumRegisters / 2 -
|
||||
kNumReservedRegisters;
|
||||
|
||||
|
||||
static int ToAllocationIndex(FPURegister reg) {
|
||||
ASSERT(reg.code() != 0);
|
||||
ASSERT(reg.code() % 2 == 0);
|
||||
return (reg.code() / 2) - 1;
|
||||
ASSERT(reg.code() / 2 < kNumAllocatableRegisters);
|
||||
ASSERT(reg.is_valid());
|
||||
return (reg.code() / 2);
|
||||
}
|
||||
|
||||
static FPURegister FromAllocationIndex(int index) {
|
||||
ASSERT(index >= 0 && index < kNumAllocatableRegisters);
|
||||
return from_code((index + 1) * 2);
|
||||
return from_code(index * 2);
|
||||
}
|
||||
|
||||
static const char* AllocationIndexToString(int index) {
|
||||
ASSERT(index >= 0 && index < kNumAllocatableRegisters);
|
||||
const char* const names[] = {
|
||||
"f0",
|
||||
"f2",
|
||||
"f4",
|
||||
"f6",
|
||||
@ -198,9 +210,7 @@ struct FPURegister {
|
||||
"f20",
|
||||
"f22",
|
||||
"f24",
|
||||
"f26",
|
||||
"f28",
|
||||
"f30"
|
||||
"f26"
|
||||
};
|
||||
return names[index];
|
||||
}
|
||||
@ -212,6 +222,23 @@ struct FPURegister {
|
||||
|
||||
bool is_valid() const { return 0 <= code_ && code_ < kNumFPURegisters ; }
|
||||
bool is(FPURegister creg) const { return code_ == creg.code_; }
|
||||
FPURegister low() const {
|
||||
// Find low reg of a Double-reg pair, which is the reg itself.
|
||||
ASSERT(code_ % 2 == 0); // Specified Double reg must be even.
|
||||
FPURegister reg;
|
||||
reg.code_ = code_;
|
||||
ASSERT(reg.is_valid());
|
||||
return reg;
|
||||
}
|
||||
FPURegister high() const {
|
||||
// Find high reg of a Doubel-reg pair, which is reg + 1.
|
||||
ASSERT(code_ % 2 == 0); // Specified Double reg must be even.
|
||||
FPURegister reg;
|
||||
reg.code_ = code_ + 1;
|
||||
ASSERT(reg.is_valid());
|
||||
return reg;
|
||||
}
|
||||
|
||||
int code() const {
|
||||
ASSERT(is_valid());
|
||||
return code_;
|
||||
@ -228,9 +255,19 @@ struct FPURegister {
|
||||
int code_;
|
||||
};
|
||||
|
||||
typedef FPURegister DoubleRegister;
|
||||
// V8 now supports the O32 ABI, and the FPU Registers are organized as 32
|
||||
// 32-bit registers, f0 through f31. When used as 'double' they are used
|
||||
// in pairs, starting with the even numbered register. So a double operation
|
||||
// on f0 really uses f0 and f1.
|
||||
// (Modern mips hardware also supports 32 64-bit registers, via setting
|
||||
// (priviledged) Status Register FR bit to 1. This is used by the N32 ABI,
|
||||
// but it is not in common use. Someday we will want to support this in v8.)
|
||||
|
||||
const FPURegister no_creg = { -1 };
|
||||
// For O32 ABI, Floats and Doubles refer to same set of 32 32-bit registers.
|
||||
typedef FPURegister DoubleRegister;
|
||||
typedef FPURegister FloatRegister;
|
||||
|
||||
const FPURegister no_freg = { -1 };
|
||||
|
||||
const FPURegister f0 = { 0 }; // Return value in hard float mode.
|
||||
const FPURegister f1 = { 1 };
|
||||
@ -265,6 +302,8 @@ const FPURegister f29 = { 29 };
|
||||
const FPURegister f30 = { 30 };
|
||||
const FPURegister f31 = { 31 };
|
||||
|
||||
const FPURegister kDoubleRegZero = f28;
|
||||
|
||||
// FPU (coprocessor 1) control registers.
|
||||
// Currently only FCSR (#31) is implemented.
|
||||
struct FPUControlRegister {
|
||||
@ -331,6 +370,10 @@ class MemOperand : public Operand {
|
||||
explicit MemOperand(Register rn, int32_t offset = 0);
|
||||
int32_t offset() const { return offset_; }
|
||||
|
||||
bool OffsetIsInt16Encodable() const {
|
||||
return is_int16(offset_);
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t offset_;
|
||||
|
||||
@ -504,6 +547,8 @@ class Assembler : public AssemblerBase {
|
||||
static Address target_address_at(Address pc);
|
||||
static void set_target_address_at(Address pc, Address target);
|
||||
|
||||
static void JumpLabelToJumpRegister(Address pc);
|
||||
|
||||
// This sets the branch destination (which gets loaded at the call address).
|
||||
// This is for calls and branches within generated code.
|
||||
inline static void set_target_at(Address instruction_payload,
|
||||
@ -534,9 +579,13 @@ class Assembler : public AssemblerBase {
|
||||
static const int kExternalTargetSize = 0 * kInstrSize;
|
||||
|
||||
// Number of consecutive instructions used to store 32bit constant.
|
||||
// Used in RelocInfo::target_address_address() function to tell serializer
|
||||
// address of the instruction that follows LUI/ORI instruction pair.
|
||||
static const int kInstructionsFor32BitConstant = 2;
|
||||
// Before jump-optimizations, this constant was used in
|
||||
// RelocInfo::target_address_address() function to tell serializer address of
|
||||
// the instruction that follows LUI/ORI instruction pair. Now, with new jump
|
||||
// optimization, where jump-through-register instruction that usually
|
||||
// follows LUI/ORI pair is substituted with J/JAL, this constant equals
|
||||
// to 3 instructions (LUI+ORI+J/JAL/JR/JALR).
|
||||
static const int kInstructionsFor32BitConstant = 3;
|
||||
|
||||
// Distance between the instruction referring to the address of the call
|
||||
// target and the return address.
|
||||
@ -623,6 +672,8 @@ class Assembler : public AssemblerBase {
|
||||
void jal(int32_t target);
|
||||
void jalr(Register rs, Register rd = ra);
|
||||
void jr(Register target);
|
||||
void j_or_jr(int32_t target, Register rs);
|
||||
void jal_or_jalr(int32_t target, Register rs);
|
||||
|
||||
|
||||
//-------Data-processing-instructions---------
|
||||
@ -892,6 +943,10 @@ class Assembler : public AssemblerBase {
|
||||
static bool IsLui(Instr instr);
|
||||
static bool IsOri(Instr instr);
|
||||
|
||||
static bool IsJal(Instr instr);
|
||||
static bool IsJr(Instr instr);
|
||||
static bool IsJalr(Instr instr);
|
||||
|
||||
static bool IsNop(Instr instr, unsigned int type);
|
||||
static bool IsPop(Instr instr);
|
||||
static bool IsPush(Instr instr);
|
||||
@ -976,6 +1031,8 @@ class Assembler : public AssemblerBase {
|
||||
return internal_trampoline_exception_;
|
||||
}
|
||||
|
||||
void DoubleAsTwoUInt32(double d, uint32_t* lo, uint32_t* hi);
|
||||
|
||||
bool is_trampoline_emitted() const {
|
||||
return trampoline_emitted_;
|
||||
}
|
||||
|
@ -191,6 +191,7 @@ bool Instruction::IsLinkingInstruction() const {
|
||||
const int op = OpcodeFieldRaw();
|
||||
switch (op) {
|
||||
case JAL:
|
||||
return true;
|
||||
case REGIMM:
|
||||
switch (RtFieldRaw()) {
|
||||
case BGEZAL:
|
||||
@ -272,7 +273,7 @@ Instruction::Type Instruction::InstructionType() const {
|
||||
case MOVCI:
|
||||
return kRegisterType;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return kUnsupported;
|
||||
};
|
||||
break;
|
||||
case SPECIAL2:
|
||||
@ -281,7 +282,7 @@ Instruction::Type Instruction::InstructionType() const {
|
||||
case CLZ:
|
||||
return kRegisterType;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return kUnsupported;
|
||||
};
|
||||
break;
|
||||
case SPECIAL3:
|
||||
@ -290,7 +291,7 @@ Instruction::Type Instruction::InstructionType() const {
|
||||
case EXT:
|
||||
return kRegisterType;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return kUnsupported;
|
||||
};
|
||||
break;
|
||||
case COP1: // Coprocessor instructions.
|
||||
@ -341,7 +342,7 @@ Instruction::Type Instruction::InstructionType() const {
|
||||
case JAL:
|
||||
return kJumpType;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return kUnsupported;
|
||||
};
|
||||
return kUnsupported;
|
||||
}
|
||||
|
@ -204,6 +204,10 @@ static const int kImm26Bits = 26;
|
||||
static const int kImm28Shift = 0;
|
||||
static const int kImm28Bits = 28;
|
||||
|
||||
// In branches and jumps immediate fields point to words, not bytes,
|
||||
// and are therefore shifted by 2.
|
||||
static const int kImmFieldShift = 2;
|
||||
|
||||
static const int kFsShift = 11;
|
||||
static const int kFsBits = 5;
|
||||
static const int kFtShift = 16;
|
||||
@ -233,7 +237,7 @@ static const int kFunctionFieldMask =
|
||||
static const int kHiMask = 0xffff << 16;
|
||||
static const int kLoMask = 0xffff;
|
||||
static const int kSignMask = 0x80000000;
|
||||
|
||||
static const int kJumpAddrMask = (1 << (kImm26Bits + kImmFieldShift)) - 1;
|
||||
|
||||
// ----- MIPS Opcodes and Function Fields.
|
||||
// We use this presentation to stay close to the table representation in
|
||||
@ -290,12 +294,12 @@ enum Opcode {
|
||||
enum SecondaryField {
|
||||
// SPECIAL Encoding of Function Field.
|
||||
SLL = ((0 << 3) + 0),
|
||||
MOVCI = ((0 << 3) + 1),
|
||||
SRL = ((0 << 3) + 2),
|
||||
SRA = ((0 << 3) + 3),
|
||||
SLLV = ((0 << 3) + 4),
|
||||
SRLV = ((0 << 3) + 6),
|
||||
SRAV = ((0 << 3) + 7),
|
||||
MOVCI = ((0 << 3) + 1),
|
||||
|
||||
JR = ((1 << 3) + 0),
|
||||
JALR = ((1 << 3) + 1),
|
||||
@ -498,14 +502,38 @@ inline Condition ReverseCondition(Condition cc) {
|
||||
|
||||
// ----- Coprocessor conditions.
|
||||
enum FPUCondition {
|
||||
F, // False.
|
||||
UN, // Unordered.
|
||||
EQ, // Equal.
|
||||
UEQ, // Unordered or Equal.
|
||||
OLT, // Ordered or Less Than.
|
||||
ULT, // Unordered or Less Than.
|
||||
OLE, // Ordered or Less Than or Equal.
|
||||
ULE // Unordered or Less Than or Equal.
|
||||
kNoFPUCondition = -1,
|
||||
|
||||
F = 0, // False.
|
||||
UN = 1, // Unordered.
|
||||
EQ = 2, // Equal.
|
||||
UEQ = 3, // Unordered or Equal.
|
||||
OLT = 4, // Ordered or Less Than.
|
||||
ULT = 5, // Unordered or Less Than.
|
||||
OLE = 6, // Ordered or Less Than or Equal.
|
||||
ULE = 7 // Unordered or Less Than or Equal.
|
||||
};
|
||||
|
||||
|
||||
// FPU rounding modes.
|
||||
enum FPURoundingMode {
|
||||
RN = 0 << 0, // Round to Nearest.
|
||||
RZ = 1 << 0, // Round towards zero.
|
||||
RP = 2 << 0, // Round towards Plus Infinity.
|
||||
RM = 3 << 0, // Round towards Minus Infinity.
|
||||
|
||||
// Aliases.
|
||||
kRoundToNearest = RN,
|
||||
kRoundToZero = RZ,
|
||||
kRoundToPlusInf = RP,
|
||||
kRoundToMinusInf = RM
|
||||
};
|
||||
|
||||
static const uint32_t kFPURoundingModeMask = 3 << 0;
|
||||
|
||||
enum CheckForInexactConversion {
|
||||
kCheckForInexactConversion,
|
||||
kDontCheckForInexactConversion
|
||||
};
|
||||
|
||||
|
||||
@ -716,7 +744,7 @@ class Instruction {
|
||||
|
||||
inline int32_t Imm26Value() const {
|
||||
ASSERT(InstructionType() == kJumpType);
|
||||
return Bits(kImm16Shift + kImm26Bits - 1, kImm26Shift);
|
||||
return Bits(kImm26Shift + kImm26Bits - 1, kImm26Shift);
|
||||
}
|
||||
|
||||
// Say if the instruction should not be used in a branch delay slot.
|
||||
|
@ -112,7 +112,7 @@ class Decoder {
|
||||
void PrintUImm16(Instruction* instr);
|
||||
void PrintSImm16(Instruction* instr);
|
||||
void PrintXImm16(Instruction* instr);
|
||||
void PrintImm26(Instruction* instr);
|
||||
void PrintXImm26(Instruction* instr);
|
||||
void PrintCode(Instruction* instr); // For break and trap instructions.
|
||||
// Printing of instruction name.
|
||||
void PrintInstructionName(Instruction* instr);
|
||||
@ -273,9 +273,9 @@ void Decoder::PrintXImm16(Instruction* instr) {
|
||||
|
||||
|
||||
// Print 26-bit immediate value.
|
||||
void Decoder::PrintImm26(Instruction* instr) {
|
||||
int32_t imm = instr->Imm26Value();
|
||||
out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
|
||||
void Decoder::PrintXImm26(Instruction* instr) {
|
||||
uint32_t imm = instr->Imm26Value() << kImmFieldShift;
|
||||
out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm);
|
||||
}
|
||||
|
||||
|
||||
@ -383,9 +383,9 @@ int Decoder::FormatOption(Instruction* instr, const char* format) {
|
||||
}
|
||||
return 6;
|
||||
} else {
|
||||
ASSERT(STRING_STARTS_WITH(format, "imm26"));
|
||||
PrintImm26(instr);
|
||||
return 5;
|
||||
ASSERT(STRING_STARTS_WITH(format, "imm26x"));
|
||||
PrintXImm26(instr);
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
case 'r': { // 'r: registers.
|
||||
@ -926,10 +926,10 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) {
|
||||
void Decoder::DecodeTypeJump(Instruction* instr) {
|
||||
switch (instr->OpcodeFieldRaw()) {
|
||||
case J:
|
||||
Format(instr, "j 'imm26");
|
||||
Format(instr, "j 'imm26x");
|
||||
break;
|
||||
case JAL:
|
||||
Format(instr, "jal 'imm26");
|
||||
Format(instr, "jal 'imm26x");
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
@ -958,6 +958,7 @@ int Decoder::InstructionDecode(byte* instr_ptr) {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
Format(instr, "UNSUPPORTED");
|
||||
UNSUPPORTED_MIPS();
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +87,6 @@ static const RegList kCalleeSavedFPU =
|
||||
static const int kNumCalleeSavedFPU = 6;
|
||||
// Number of registers for which space is reserved in safepoints. Must be a
|
||||
// multiple of 8.
|
||||
// TODO(mips): Only 8 registers may actually be sufficient. Revisit.
|
||||
static const int kNumSafepointRegisters = 24;
|
||||
|
||||
// Define the list of registers actually saved at safepoints.
|
||||
|
@ -33,6 +33,7 @@
|
||||
|
||||
#if defined(V8_TARGET_ARCH_MIPS)
|
||||
|
||||
#include "cpu.h"
|
||||
#include "disasm.h"
|
||||
#include "assembler.h"
|
||||
#include "globals.h" // Need the BitCast.
|
||||
@ -1215,6 +1216,8 @@ int32_t Simulator::get_pc() const {
|
||||
int Simulator::ReadW(int32_t addr, Instruction* instr) {
|
||||
if (addr >=0 && addr < 0x400) {
|
||||
// This has to be a NULL-dereference, drop into debugger.
|
||||
PrintF("Memory read from bad address: 0x%08x, pc=0x%08x\n",
|
||||
addr, reinterpret_cast<intptr_t>(instr));
|
||||
MipsDebugger dbg(this);
|
||||
dbg.Debug();
|
||||
}
|
||||
@ -1234,6 +1237,8 @@ int Simulator::ReadW(int32_t addr, Instruction* instr) {
|
||||
void Simulator::WriteW(int32_t addr, int value, Instruction* instr) {
|
||||
if (addr >= 0 && addr < 0x400) {
|
||||
// This has to be a NULL-dereference, drop into debugger.
|
||||
PrintF("Memory write to bad address: 0x%08x, pc=0x%08x\n",
|
||||
addr, reinterpret_cast<intptr_t>(instr));
|
||||
MipsDebugger dbg(this);
|
||||
dbg.Debug();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user