Reland "MIPS: Add support for arch. revision 6 to mips32 port."

Fixing gclient runhooks failure caused by reverted commit r23050.

TEST=
BUG=
R=jkummerow@chromium.org, paul.lind@imgtec.com

Review URL: https://codereview.chromium.org/467583002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23088 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
dusan.milosavljevic@imgtec.com 2014-08-12 19:04:15 +00:00
parent 1d2a0759f2
commit 8804736ba3
22 changed files with 2483 additions and 483 deletions

View File

@ -58,6 +58,14 @@
# Default arch variant for MIPS.
'mips_arch_variant%': 'r2',
# Possible values fp32, fp64, fpxx.
# fp32 - 32 32-bit FPU registers are available, doubles are placed in
# register pairs.
# fp64 - 32 64-bit FPU registers are available.
# fpxx - compatibility mode, it chooses fp32 or fp64 depending on runtime
# detection
'mips_fpu_mode%': 'fp32',
'v8_enable_backtrace%': 0,
# Enable profiling support. Only required on Windows.
@ -272,10 +280,33 @@
'cflags': ['-msoft-float'],
'ldflags': ['-msoft-float'],
}],
['mips_fpu_mode=="fp64"', {
'cflags': ['-mfp64'],
}],
['mips_fpu_mode=="fpxx"', {
'cflags': ['-mfpxx'],
}],
['mips_fpu_mode=="fp32"', {
'cflags': ['-mfp32'],
}],
['mips_arch_variant=="r6"', {
'cflags!': ['-mfp32'],
'cflags': ['-mips32r6', '-Wa,-mips32r6'],
'ldflags': [
'-mips32r6',
'-Wl,--dynamic-linker=$(LDSO_PATH)',
'-Wl,--rpath=$(LD_R_PATH)',
],
}],
['mips_arch_variant=="r2"', {
'cflags': ['-mips32r2', '-Wa,-mips32r2'],
}],
['mips_arch_variant=="r1"', {
'cflags!': ['-mfp64'],
'cflags': ['-mips32', '-Wa,-mips32'],
}],
['mips_arch_variant=="rx"', {
'cflags!': ['-mfp64'],
'cflags': ['-mips32', '-Wa,-mips32'],
}],
],
@ -297,8 +328,34 @@
'__mips_soft_float=1'
],
}],
['mips_arch_variant=="rx"', {
'defines': [
'_MIPS_ARCH_MIPS32RX',
'FPU_MODE_FPXX',
],
}],
['mips_arch_variant=="r6"', {
'defines': [
'_MIPS_ARCH_MIPS32R6',
'FPU_MODE_FP64',
],
}],
['mips_arch_variant=="r2"', {
'defines': ['_MIPS_ARCH_MIPS32R2',],
'conditions': [
['mips_fpu_mode=="fp64"', {
'defines': ['FPU_MODE_FP64',],
}],
['mips_fpu_mode=="fpxx"', {
'defines': ['FPU_MODE_FPXX',],
}],
['mips_fpu_mode=="fp32"', {
'defines': ['FPU_MODE_FP32',],
}],
],
}],
['mips_arch_variant=="r1"', {
'defines': ['FPU_MODE_FP32',],
}],
],
}], # v8_target_arch=="mips"
@ -321,13 +378,37 @@
'cflags': ['-msoft-float'],
'ldflags': ['-msoft-float'],
}],
['mips_fpu_mode=="fp64"', {
'cflags': ['-mfp64'],
}],
['mips_fpu_mode=="fpxx"', {
'cflags': ['-mfpxx'],
}],
['mips_fpu_mode=="fp32"', {
'cflags': ['-mfp32'],
}],
['mips_arch_variant=="r6"', {
'cflags!': ['-mfp32'],
'cflags': ['-mips32r6', '-Wa,-mips32r6'],
'ldflags': [
'-mips32r6',
'-Wl,--dynamic-linker=$(LDSO_PATH)',
'-Wl,--rpath=$(LD_R_PATH)',
],
}],
['mips_arch_variant=="r2"', {
'cflags': ['-mips32r2', '-Wa,-mips32r2'],
}],
['mips_arch_variant=="r1"', {
'cflags!': ['-mfp64'],
'cflags': ['-mips32', '-Wa,-mips32'],
}],
}],
['mips_arch_variant=="rx"', {
'cflags!': ['-mfp64'],
'cflags': ['-mips32', '-Wa,-mips32'],
}],
['mips_arch_variant=="loongson"', {
'cflags!': ['-mfp64'],
'cflags': ['-mips3', '-Wa,-mips3'],
}],
],
@ -349,11 +430,40 @@
'__mips_soft_float=1'
],
}],
['mips_arch_variant=="rx"', {
'defines': [
'_MIPS_ARCH_MIPS32RX',
'FPU_MODE_FPXX',
],
}],
['mips_arch_variant=="r6"', {
'defines': [
'_MIPS_ARCH_MIPS32R6',
'FPU_MODE_FP64',
],
}],
['mips_arch_variant=="r2"', {
'defines': ['_MIPS_ARCH_MIPS32R2',],
'conditions': [
['mips_fpu_mode=="fp64"', {
'defines': ['FPU_MODE_FP64',],
}],
['mips_fpu_mode=="fpxx"', {
'defines': ['FPU_MODE_FPXX',],
}],
['mips_fpu_mode=="fp32"', {
'defines': ['FPU_MODE_FP32',],
}],
],
}],
['mips_arch_variant=="r1"', {
'defines': ['FPU_MODE_FP32',],
}],
['mips_arch_variant=="loongson"', {
'defines': ['_MIPS_ARCH_LOONGSON',],
'defines': [
'_MIPS_ARCH_LOONGSON',
'FPU_MODE_FP32',
],
}],
],
}], # v8_target_arch=="mipsel"

View File

@ -27,16 +27,16 @@ inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
__asm__ __volatile__(".set push\n"
".set noreorder\n"
"1:\n"
"ll %0, %5\n" // prev = *ptr
"bne %0, %3, 2f\n" // if (prev != old_value) goto 2
"move %2, %4\n" // tmp = new_value
"sc %2, %1\n" // *ptr = tmp (with atomic check)
"beqz %2, 1b\n" // start again on atomic error
"ll %0, 0(%4)\n" // prev = *ptr
"bne %0, %2, 2f\n" // if (prev != old_value) goto 2
"move %1, %3\n" // tmp = new_value
"sc %1, 0(%4)\n" // *ptr = tmp (with atomic check)
"beqz %1, 1b\n" // start again on atomic error
"nop\n" // delay slot nop
"2:\n"
".set pop\n"
: "=&r" (prev), "=m" (*ptr), "=&r" (tmp)
: "Ir" (old_value), "r" (new_value), "m" (*ptr)
: "=&r" (prev), "=&r" (tmp)
: "Ir" (old_value), "r" (new_value), "r" (ptr)
: "memory");
return prev;
}
@ -48,15 +48,16 @@ inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 temp, old;
__asm__ __volatile__(".set push\n"
".set noreorder\n"
".set at\n"
"1:\n"
"ll %1, %2\n" // old = *ptr
"move %0, %3\n" // temp = new_value
"sc %0, %2\n" // *ptr = temp (with atomic check)
"ll %1, 0(%3)\n" // old = *ptr
"move %0, %2\n" // temp = new_value
"sc %0, 0(%3)\n" // *ptr = temp (with atomic check)
"beqz %0, 1b\n" // start again on atomic error
"nop\n" // delay slot nop
".set pop\n"
: "=&r" (temp), "=&r" (old), "=m" (*ptr)
: "r" (new_value), "m" (*ptr)
: "=&r" (temp), "=&r" (old)
: "r" (new_value), "r" (ptr)
: "memory");
return old;
@ -71,14 +72,14 @@ inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
__asm__ __volatile__(".set push\n"
".set noreorder\n"
"1:\n"
"ll %0, %2\n" // temp = *ptr
"addu %1, %0, %3\n" // temp2 = temp + increment
"sc %1, %2\n" // *ptr = temp2 (with atomic check)
"ll %0, 0(%3)\n" // temp = *ptr
"addu %1, %0, %2\n" // temp2 = temp + increment
"sc %1, 0(%3)\n" // *ptr = temp2 (with atomic check)
"beqz %1, 1b\n" // start again on atomic error
"addu %1, %0, %3\n" // temp2 = temp + increment
"addu %1, %0, %2\n" // temp2 = temp + increment
".set pop\n"
: "=&r" (temp), "=&r" (temp2), "=m" (*ptr)
: "Ir" (increment), "m" (*ptr)
: "=&r" (temp), "=&r" (temp2)
: "Ir" (increment), "r" (ptr)
: "memory");
// temp2 now holds the final value.
return temp2;

View File

@ -115,6 +115,30 @@ static uint32_t ReadELFHWCaps() {
#endif // V8_HOST_ARCH_ARM
#if V8_HOST_ARCH_MIPS
int __detect_fp64_mode(void) {
double result = 0;
// Bit representation of (double)1 is 0x3FF0000000000000.
asm(
"lui $t0, 0x3FF0\n\t"
"ldc1 $f0, %0\n\t"
"mtc1 $t0, $f1\n\t"
"sdc1 $f0, %0\n\t"
: "+m" (result)
: : "t0", "$f0", "$f1", "memory");
return !(result == 1);
}
int __detect_mips_arch_revision(void) {
// TODO(dusmil): Do the specific syscall as soon as it is implemented in mips
// kernel. Currently fail-back to the least common denominator which is
// mips32 revision 1.
return 1;
}
#endif
// Extract the information exposed by the kernel via /proc/cpuinfo.
class CPUInfo V8_FINAL {
public:
@ -263,7 +287,8 @@ CPU::CPU() : stepping_(0),
has_thumb2_(false),
has_vfp_(false),
has_vfp3_(false),
has_vfp3_d32_(false) {
has_vfp3_d32_(false),
is_fp64_mode_(false) {
memcpy(vendor_, "Unknown", 8);
#if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
int cpu_info[4];
@ -466,6 +491,10 @@ CPU::CPU() : stepping_(0),
char* cpu_model = cpu_info.ExtractField("cpu model");
has_fpu_ = HasListItem(cpu_model, "FPU");
delete[] cpu_model;
#ifdef V8_HOST_ARCH_MIPS
is_fp64_mode_ = __detect_fp64_mode();
architecture_ = __detect_mips_arch_revision();
#endif
#elif V8_HOST_ARCH_ARM64

View File

@ -77,6 +77,9 @@ class CPU V8_FINAL {
bool has_vfp3() const { return has_vfp3_; }
bool has_vfp3_d32() const { return has_vfp3_d32_; }
// mips features
bool is_fp64_mode() const { return is_fp64_mode_; }
private:
char vendor_[13];
int stepping_;
@ -104,6 +107,7 @@ class CPU V8_FINAL {
bool has_vfp_;
bool has_vfp3_;
bool has_vfp3_d32_;
bool is_fp64_mode_;
};
} } // namespace v8::base

View File

@ -610,8 +610,12 @@ enum CpuFeature {
MOVW_MOVT_IMMEDIATE_LOADS,
VFP32DREGS,
NEON,
// MIPS
// MIPS, MIPS64
FPU,
FP64FPU,
MIPSr1,
MIPSr2,
MIPSr6,
// ARM64
ALWAYS_ALIGN_CSP,
NUMBER_OF_CPU_FEATURES

View File

@ -98,10 +98,32 @@ void CpuFeatures::ProbeImpl(bool cross_compile) {
#ifndef __mips__
// For the simulator build, use FPU.
supported_ |= 1u << FPU;
#if defined(_MIPS_ARCH_MIPS32R6)
// FP64 mode is implied on r6.
supported_ |= 1u << FP64FPU;
#endif
#if defined(FPU_MODE_FP64)
supported_ |= 1u << FP64FPU;
#endif
#else
// Probe for additional features at runtime.
base::CPU cpu;
if (cpu.has_fpu()) supported_ |= 1u << FPU;
#if defined(FPU_MODE_FPXX)
if (cpu.is_fp64_mode()) supported_ |= 1u << FP64FPU;
#elif defined(FPU_MODE_FP64)
supported_ |= 1u << FP64FPU;
#endif
#if defined(_MIPS_ARCH_MIPS32RX)
if (cpu.architecture() == 6) {
supported_ |= 1u << MIPSr6;
} else if (cpu.architecture() == 2) {
supported_ |= 1u << MIPSr1;
supported_ |= 1u << MIPSr2;
} else {
supported_ |= 1u << MIPSr1;
}
#endif
#endif
}
@ -484,7 +506,9 @@ bool Assembler::IsBranch(Instr instr) {
opcode == BGTZL ||
(opcode == REGIMM && (rt_field == BLTZ || rt_field == BGEZ ||
rt_field == BLTZAL || rt_field == BGEZAL)) ||
(opcode == COP1 && rs_field == BC1); // Coprocessor branch.
(opcode == COP1 && rs_field == BC1) || // Coprocessor branch.
(opcode == COP1 && rs_field == BC1EQZ) ||
(opcode == COP1 && rs_field == BC1NEZ);
}
@ -529,12 +553,18 @@ bool Assembler::IsJal(Instr instr) {
bool Assembler::IsJr(Instr instr) {
return GetOpcodeField(instr) == SPECIAL && GetFunctionField(instr) == JR;
if (!IsMipsArchVariant(kMips32r6)) {
return GetOpcodeField(instr) == SPECIAL && GetFunctionField(instr) == JR;
} else {
return GetOpcodeField(instr) == SPECIAL &&
GetRdField(instr) == 0 && GetFunctionField(instr) == JALR;
}
}
bool Assembler::IsJalr(Instr instr) {
return GetOpcodeField(instr) == SPECIAL && GetFunctionField(instr) == JALR;
return GetOpcodeField(instr) == SPECIAL &&
GetRdField(instr) != 0 && GetFunctionField(instr) == JALR;
}
@ -1019,6 +1049,88 @@ int32_t Assembler::branch_offset(Label* L, bool jump_elimination_allowed) {
}
int32_t Assembler::branch_offset_compact(Label* L,
bool jump_elimination_allowed) {
int32_t target_pos;
if (L->is_bound()) {
target_pos = L->pos();
} else {
if (L->is_linked()) {
target_pos = L->pos();
L->link_to(pc_offset());
} else {
L->link_to(pc_offset());
if (!trampoline_emitted_) {
unbound_labels_count_++;
next_buffer_check_ -= kTrampolineSlotsSize;
}
return kEndOfChain;
}
}
int32_t offset = target_pos - pc_offset();
DCHECK((offset & 3) == 0);
DCHECK(is_int16(offset >> 2));
return offset;
}
int32_t Assembler::branch_offset21(Label* L, bool jump_elimination_allowed) {
int32_t target_pos;
if (L->is_bound()) {
target_pos = L->pos();
} else {
if (L->is_linked()) {
target_pos = L->pos();
L->link_to(pc_offset());
} else {
L->link_to(pc_offset());
if (!trampoline_emitted_) {
unbound_labels_count_++;
next_buffer_check_ -= kTrampolineSlotsSize;
}
return kEndOfChain;
}
}
int32_t offset = target_pos - (pc_offset() + kBranchPCOffset);
DCHECK((offset & 3) == 0);
DCHECK(((offset >> 2) & 0xFFE00000) == 0); // Offset is 21bit width.
return offset;
}
int32_t Assembler::branch_offset21_compact(Label* L,
bool jump_elimination_allowed) {
int32_t target_pos;
if (L->is_bound()) {
target_pos = L->pos();
} else {
if (L->is_linked()) {
target_pos = L->pos();
L->link_to(pc_offset());
} else {
L->link_to(pc_offset());
if (!trampoline_emitted_) {
unbound_labels_count_++;
next_buffer_check_ -= kTrampolineSlotsSize;
}
return kEndOfChain;
}
}
int32_t offset = target_pos - pc_offset();
DCHECK((offset & 3) == 0);
DCHECK(((offset >> 2) & 0xFFe00000) == 0); // Offset is 21bit width.
return offset;
}
void Assembler::label_at_put(Label* L, int at_offset) {
int target_pos;
if (L->is_bound()) {
@ -1072,7 +1184,33 @@ void Assembler::bgez(Register rs, int16_t offset) {
}
void Assembler::bgezc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(BLEZL, rt, rt, offset);
}
void Assembler::bgeuc(Register rs, Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rs.is(zero_reg)));
DCHECK(!(rt.is(zero_reg)));
DCHECK(rs.code() != rt.code());
GenInstrImmediate(BLEZ, rs, rt, offset);
}
void Assembler::bgec(Register rs, Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rs.is(zero_reg)));
DCHECK(!(rt.is(zero_reg)));
DCHECK(rs.code() != rt.code());
GenInstrImmediate(BLEZL, rs, rt, offset);
}
void Assembler::bgezal(Register rs, int16_t offset) {
DCHECK(!IsMipsArchVariant(kMips32r6) || rs.is(zero_reg));
BlockTrampolinePoolScope block_trampoline_pool(this);
positions_recorder()->WriteRecordedPositions();
GenInstrImmediate(REGIMM, rs, BGEZAL, offset);
@ -1087,6 +1225,13 @@ void Assembler::bgtz(Register rs, int16_t offset) {
}
void Assembler::bgtzc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(BGTZL, zero_reg, rt, offset);
}
void Assembler::blez(Register rs, int16_t offset) {
BlockTrampolinePoolScope block_trampoline_pool(this);
GenInstrImmediate(BLEZ, rs, zero_reg, offset);
@ -1094,6 +1239,38 @@ void Assembler::blez(Register rs, int16_t offset) {
}
void Assembler::blezc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(BLEZL, zero_reg, rt, offset);
}
void Assembler::bltzc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(BGTZL, rt, rt, offset);
}
void Assembler::bltuc(Register rs, Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rs.is(zero_reg)));
DCHECK(!(rt.is(zero_reg)));
DCHECK(rs.code() != rt.code());
GenInstrImmediate(BGTZ, rs, rt, offset);
}
void Assembler::bltc(Register rs, Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rs.is(zero_reg)));
DCHECK(!(rt.is(zero_reg)));
DCHECK(rs.code() != rt.code());
GenInstrImmediate(BGTZL, rs, rt, offset);
}
void Assembler::bltz(Register rs, int16_t offset) {
BlockTrampolinePoolScope block_trampoline_pool(this);
GenInstrImmediate(REGIMM, rs, BLTZ, offset);
@ -1102,6 +1279,7 @@ void Assembler::bltz(Register rs, int16_t offset) {
void Assembler::bltzal(Register rs, int16_t offset) {
DCHECK(!IsMipsArchVariant(kMips32r6) || rs.is(zero_reg));
BlockTrampolinePoolScope block_trampoline_pool(this);
positions_recorder()->WriteRecordedPositions();
GenInstrImmediate(REGIMM, rs, BLTZAL, offset);
@ -1116,6 +1294,101 @@ void Assembler::bne(Register rs, Register rt, int16_t offset) {
}
void Assembler::bovc(Register rs, Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rs.is(zero_reg)));
DCHECK(rs.code() >= rt.code());
GenInstrImmediate(ADDI, rs, rt, offset);
}
void Assembler::bnvc(Register rs, Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rs.is(zero_reg)));
DCHECK(rs.code() >= rt.code());
GenInstrImmediate(DADDI, rs, rt, offset);
}
void Assembler::blezalc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(BLEZ, zero_reg, rt, offset);
}
void Assembler::bgezalc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(BLEZ, rt, rt, offset);
}
void Assembler::bgezall(Register rs, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rs.is(zero_reg)));
GenInstrImmediate(REGIMM, rs, BGEZALL, offset);
}
void Assembler::bltzalc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(BGTZ, rt, rt, offset);
}
void Assembler::bgtzalc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(BGTZ, zero_reg, rt, offset);
}
void Assembler::beqzalc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(ADDI, zero_reg, rt, offset);
}
void Assembler::bnezalc(Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rt.is(zero_reg)));
GenInstrImmediate(DADDI, zero_reg, rt, offset);
}
void Assembler::beqc(Register rs, Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(rs.code() < rt.code());
GenInstrImmediate(ADDI, rs, rt, offset);
}
void Assembler::beqzc(Register rs, int32_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rs.is(zero_reg)));
Instr instr = BEQZC | (rs.code() << kRsShift) | offset;
emit(instr);
}
void Assembler::bnec(Register rs, Register rt, int16_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(rs.code() < rt.code());
GenInstrImmediate(DADDI, rs, rt, offset);
}
void Assembler::bnezc(Register rs, int32_t offset) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK(!(rs.is(zero_reg)));
Instr instr = BNEZC | (rs.code() << kRsShift) | offset;
emit(instr);
}
void Assembler::j(int32_t target) {
#if DEBUG
// Get pc of delay slot.
@ -1129,12 +1402,16 @@ void Assembler::j(int32_t target) {
void Assembler::jr(Register rs) {
BlockTrampolinePoolScope block_trampoline_pool(this);
if (rs.is(ra)) {
positions_recorder()->WriteRecordedPositions();
if (!IsMipsArchVariant(kMips32r6)) {
BlockTrampolinePoolScope block_trampoline_pool(this);
if (rs.is(ra)) {
positions_recorder()->WriteRecordedPositions();
}
GenInstrRegister(SPECIAL, rs, zero_reg, zero_reg, 0, JR);
BlockTrampolinePoolFor(1); // For associated delay slot.
} else {
jalr(rs, zero_reg);
}
GenInstrRegister(SPECIAL, rs, zero_reg, zero_reg, 0, JR);
BlockTrampolinePoolFor(1); // For associated delay slot.
}
@ -1205,7 +1482,41 @@ void Assembler::subu(Register rd, Register rs, Register rt) {
void Assembler::mul(Register rd, Register rs, Register rt) {
GenInstrRegister(SPECIAL2, rs, rt, rd, 0, MUL);
if (!IsMipsArchVariant(kMips32r6)) {
GenInstrRegister(SPECIAL2, rs, rt, rd, 0, MUL);
} else {
GenInstrRegister(SPECIAL, rs, rt, rd, MUL_OP, MUL_MUH);
}
}
void Assembler::mulu(Register rd, Register rs, Register rt) {
DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL, rs, rt, rd, MUL_OP, MUL_MUH_U);
}
void Assembler::muh(Register rd, Register rs, Register rt) {
DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL, rs, rt, rd, MUH_OP, MUL_MUH);
}
void Assembler::muhu(Register rd, Register rs, Register rt) {
DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL, rs, rt, rd, MUH_OP, MUL_MUH_U);
}
void Assembler::mod(Register rd, Register rs, Register rt) {
DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL, rs, rt, rd, MOD_OP, DIV_MOD);
}
void Assembler::modu(Register rd, Register rs, Register rt) {
DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL, rs, rt, rd, MOD_OP, DIV_MOD_U);
}
@ -1224,11 +1535,23 @@ void Assembler::div(Register rs, Register rt) {
}
void Assembler::div(Register rd, Register rs, Register rt) {
DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL, rs, rt, rd, DIV_OP, DIV_MOD);
}
void Assembler::divu(Register rs, Register rt) {
GenInstrRegister(SPECIAL, rs, rt, zero_reg, 0, DIVU);
}
void Assembler::divu(Register rd, Register rs, Register rt) {
DCHECK(IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL, rs, rt, rd, DIV_OP, DIV_MOD_U);
}
// Logical.
void Assembler::and_(Register rd, Register rs, Register rt) {
@ -1311,7 +1634,7 @@ void Assembler::srav(Register rd, Register rt, Register rs) {
void Assembler::rotr(Register rd, Register rt, uint16_t sa) {
// Should be called via MacroAssembler::Ror.
DCHECK(rd.is_valid() && rt.is_valid() && is_uint5(sa));
DCHECK(kArchVariant == kMips32r2);
DCHECK(IsMipsArchVariant(kMips32r2));
Instr instr = SPECIAL | (1 << kRsShift) | (rt.code() << kRtShift)
| (rd.code() << kRdShift) | (sa << kSaShift) | SRL;
emit(instr);
@ -1321,7 +1644,7 @@ void Assembler::rotr(Register rd, Register rt, uint16_t sa) {
void Assembler::rotrv(Register rd, Register rt, Register rs) {
// Should be called via MacroAssembler::Ror.
DCHECK(rd.is_valid() && rt.is_valid() && rs.is_valid() );
DCHECK(kArchVariant == kMips32r2);
DCHECK(IsMipsArchVariant(kMips32r2));
Instr instr = SPECIAL | (rs.code() << kRsShift) | (rt.code() << kRtShift)
| (rd.code() << kRdShift) | (1 << kSaShift) | SRLV;
emit(instr);
@ -1445,6 +1768,14 @@ void Assembler::lui(Register rd, int32_t j) {
}
void Assembler::aui(Register rs, Register rt, int32_t j) {
// This instruction uses same opcode as 'lui'. The difference in encoding is
// 'lui' has zero reg. for rs field.
DCHECK(is_uint16(j));
GenInstrImmediate(LUI, rs, rt, j);
}
// -------------Misc-instructions--------------
// Break / Trap instructions.
@ -1588,15 +1919,19 @@ void Assembler::movf(Register rd, Register rs, uint16_t cc) {
// Bit twiddling.
void Assembler::clz(Register rd, Register rs) {
// Clz instr requires same GPR number in 'rd' and 'rt' fields.
GenInstrRegister(SPECIAL2, rs, rd, rd, 0, CLZ);
if (!IsMipsArchVariant(kMips32r6)) {
// Clz instr requires same GPR number in 'rd' and 'rt' fields.
GenInstrRegister(SPECIAL2, rs, rd, rd, 0, CLZ);
} else {
GenInstrRegister(SPECIAL, rs, zero_reg, rd, 1, CLZ_R6);
}
}
void Assembler::ins_(Register rt, Register rs, uint16_t pos, uint16_t size) {
// Should be called via MacroAssembler::Ins.
// Ins instr has 'rt' field as dest, and two uint5: msb, lsb.
DCHECK(kArchVariant == kMips32r2);
DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL3, rs, rt, pos + size - 1, pos, INS);
}
@ -1604,13 +1939,13 @@ void Assembler::ins_(Register rt, Register rs, uint16_t pos, uint16_t size) {
void Assembler::ext_(Register rt, Register rs, uint16_t pos, uint16_t size) {
// Should be called via MacroAssembler::Ext.
// Ext instr has 'rt' field as dest, and two uint5: msb, lsb.
DCHECK(kArchVariant == kMips32r2);
DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6));
GenInstrRegister(SPECIAL3, rs, rt, size - 1, pos, EXT);
}
void Assembler::pref(int32_t hint, const MemOperand& rs) {
DCHECK(kArchVariant != kLoongson);
DCHECK(!IsMipsArchVariant(kLoongson));
DCHECK(is_uint5(hint) && is_uint16(rs.offset_));
Instr instr = PREF | (rs.rm().code() << kRsShift) | (hint << kRtShift)
| (rs.offset_);
@ -1629,12 +1964,20 @@ void Assembler::lwc1(FPURegister fd, const MemOperand& src) {
void Assembler::ldc1(FPURegister fd, const MemOperand& src) {
// Workaround for non-8-byte alignment of HeapNumber, convert 64-bit
// load to two 32-bit loads.
GenInstrImmediate(LWC1, src.rm(), fd, src.offset_ +
Register::kMantissaOffset);
FPURegister nextfpreg;
nextfpreg.setcode(fd.code() + 1);
GenInstrImmediate(LWC1, src.rm(), nextfpreg, src.offset_ +
Register::kExponentOffset);
if (IsFp64Mode()) {
GenInstrImmediate(LWC1, src.rm(), fd, src.offset_ +
Register::kMantissaOffset);
GenInstrImmediate(LW, src.rm(), at, src.offset_ +
Register::kExponentOffset);
mthc1(at, fd);
} else {
GenInstrImmediate(LWC1, src.rm(), fd, src.offset_ +
Register::kMantissaOffset);
FPURegister nextfpreg;
nextfpreg.setcode(fd.code() + 1);
GenInstrImmediate(LWC1, src.rm(), nextfpreg, src.offset_ +
Register::kExponentOffset);
}
}
@ -1646,12 +1989,20 @@ void Assembler::swc1(FPURegister fd, const MemOperand& src) {
void Assembler::sdc1(FPURegister fd, const MemOperand& src) {
// Workaround for non-8-byte alignment of HeapNumber, convert 64-bit
// store to two 32-bit stores.
GenInstrImmediate(SWC1, src.rm(), fd, src.offset_ +
Register::kMantissaOffset);
FPURegister nextfpreg;
nextfpreg.setcode(fd.code() + 1);
GenInstrImmediate(SWC1, src.rm(), nextfpreg, src.offset_ +
Register::kExponentOffset);
if (IsFp64Mode()) {
GenInstrImmediate(SWC1, src.rm(), fd, src.offset_ +
Register::kMantissaOffset);
mfhc1(at, fd);
GenInstrImmediate(SW, src.rm(), at, src.offset_ +
Register::kExponentOffset);
} else {
GenInstrImmediate(SWC1, src.rm(), fd, src.offset_ +
Register::kMantissaOffset);
FPURegister nextfpreg;
nextfpreg.setcode(fd.code() + 1);
GenInstrImmediate(SWC1, src.rm(), nextfpreg, src.offset_ +
Register::kExponentOffset);
}
}
@ -1660,11 +2011,21 @@ void Assembler::mtc1(Register rt, FPURegister fs) {
}
void Assembler::mthc1(Register rt, FPURegister fs) {
GenInstrRegister(COP1, MTHC1, rt, fs, f0);
}
void Assembler::mfc1(Register rt, FPURegister fs) {
GenInstrRegister(COP1, MFC1, rt, fs, f0);
}
void Assembler::mfhc1(Register rt, FPURegister fs) {
GenInstrRegister(COP1, MFHC1, rt, fs, f0);
}
void Assembler::ctc1(Register rt, FPUControlRegister fs) {
GenInstrRegister(COP1, CTC1, rt, fs);
}
@ -1785,25 +2146,25 @@ void Assembler::ceil_w_d(FPURegister fd, FPURegister fs) {
void Assembler::cvt_l_s(FPURegister fd, FPURegister fs) {
DCHECK(kArchVariant == kMips32r2);
DCHECK(IsMipsArchVariant(kMips32r2));
GenInstrRegister(COP1, S, f0, fs, fd, CVT_L_S);
}
void Assembler::cvt_l_d(FPURegister fd, FPURegister fs) {
DCHECK(kArchVariant == kMips32r2);
DCHECK(IsMipsArchVariant(kMips32r2));
GenInstrRegister(COP1, D, f0, fs, fd, CVT_L_D);
}
void Assembler::trunc_l_s(FPURegister fd, FPURegister fs) {
DCHECK(kArchVariant == kMips32r2);
DCHECK(IsMipsArchVariant(kMips32r2));
GenInstrRegister(COP1, S, f0, fs, fd, TRUNC_L_S);
}
void Assembler::trunc_l_d(FPURegister fd, FPURegister fs) {
DCHECK(kArchVariant == kMips32r2);
DCHECK(IsMipsArchVariant(kMips32r2));
GenInstrRegister(COP1, D, f0, fs, fd, TRUNC_L_D);
}
@ -1838,13 +2199,45 @@ void Assembler::ceil_l_d(FPURegister fd, FPURegister fs) {
}
void Assembler::min(SecondaryField fmt, FPURegister fd, FPURegister ft,
FPURegister fs) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, MIN);
}
void Assembler::mina(SecondaryField fmt, FPURegister fd, FPURegister ft,
FPURegister fs) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, MINA);
}
void Assembler::max(SecondaryField fmt, FPURegister fd, FPURegister ft,
FPURegister fs) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, MAX);
}
void Assembler::maxa(SecondaryField fmt, FPURegister fd, FPURegister ft,
FPURegister fs) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK((fmt == D) || (fmt == S));
GenInstrRegister(COP1, fmt, ft, fs, fd, MAXA);
}
void Assembler::cvt_s_w(FPURegister fd, FPURegister fs) {
GenInstrRegister(COP1, W, f0, fs, fd, CVT_S_W);
}
void Assembler::cvt_s_l(FPURegister fd, FPURegister fs) {
DCHECK(kArchVariant == kMips32r2);
DCHECK(IsMipsArchVariant(kMips32r2));
GenInstrRegister(COP1, L, f0, fs, fd, CVT_S_L);
}
@ -1860,7 +2253,7 @@ void Assembler::cvt_d_w(FPURegister fd, FPURegister fs) {
void Assembler::cvt_d_l(FPURegister fd, FPURegister fs) {
DCHECK(kArchVariant == kMips32r2);
DCHECK(IsMipsArchVariant(kMips32r2));
GenInstrRegister(COP1, L, f0, fs, fd, CVT_D_L);
}
@ -1870,7 +2263,32 @@ void Assembler::cvt_d_s(FPURegister fd, FPURegister fs) {
}
// Conditions.
// Conditions for >= MIPSr6.
void Assembler::cmp(FPUCondition cond, SecondaryField fmt,
FPURegister fd, FPURegister fs, FPURegister ft) {
DCHECK(IsMipsArchVariant(kMips32r6));
DCHECK((fmt & ~(31 << kRsShift)) == 0);
Instr instr = COP1 | fmt | ft.code() << kFtShift |
fs.code() << kFsShift | fd.code() << kFdShift | (0 << 5) | cond;
emit(instr);
}
void Assembler::bc1eqz(int16_t offset, FPURegister ft) {
DCHECK(IsMipsArchVariant(kMips32r6));
Instr instr = COP1 | BC1EQZ | ft.code() << kFtShift | (offset & kImm16Mask);
emit(instr);
}
void Assembler::bc1nez(int16_t offset, FPURegister ft) {
DCHECK(IsMipsArchVariant(kMips32r6));
Instr instr = COP1 | BC1NEZ | ft.code() << kFtShift | (offset & kImm16Mask);
emit(instr);
}
// Conditions for < MIPSr6.
void Assembler::c(FPUCondition cond, SecondaryField fmt,
FPURegister fs, FPURegister ft, uint16_t cc) {
DCHECK(is_uint3(cc));
@ -2184,7 +2602,7 @@ void Assembler::set_target_address_at(Address pc,
// 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);
*(p + 1) = ORI | rt_code | (rt_code << 5) | (itarget & kImm16Mask);
// The following code is an optimization for the common case of Call()
// or Jump() which is load to register, and jump through register:
@ -2227,20 +2645,20 @@ void Assembler::set_target_address_at(Address pc,
if (IsJalr(instr3)) {
// Try to convert JALR to JAL.
if (in_range && GetRt(instr2) == GetRs(instr3)) {
*(p+2) = JAL | target_field;
*(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;
*(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;
*(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.
@ -2252,12 +2670,16 @@ void Assembler::set_target_address_at(Address pc,
} else if (IsJ(instr3)) {
if (in_range) {
// We are patching an already converted J (jump).
*(p+2) = J | target_field;
*(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;
if (IsMipsArchVariant(kMips32r6)) {
*(p + 2) = SPECIAL | rs_field | (zero_reg.code() << kRdShift) | JALR;
} else {
*(p + 2) = SPECIAL | rs_field | JR;
}
}
patched_jump = true;
}
@ -2285,19 +2707,23 @@ void Assembler::JumpLabelToJumpRegister(Address pc) {
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;
*(p + 2) = SPECIAL | rs_field | rd_field | JALR;
patched = true;
} else if (IsJ(instr3)) {
DCHECK(GetOpcodeField(instr1) == LUI);
DCHECK(GetOpcodeField(instr2) == ORI);
uint32_t rs_field = GetRt(instr2) << kRsShift;
*(p+2) = SPECIAL | rs_field | JR;
if (IsMipsArchVariant(kMips32r6)) {
*(p + 2) = SPECIAL | rs_field | (zero_reg.code() << kRdShift) | JALR;
} else {
*(p + 2) = SPECIAL | rs_field | JR;
}
patched = true;
}
if (patched) {
CpuFeatures::FlushICache(pc+2, sizeof(Address));
CpuFeatures::FlushICache(pc + 2, sizeof(Address));
}
}

View File

@ -328,6 +328,8 @@ const FPURegister f31 = { 31 };
#define kLithiumScratchReg2 s4
#define kLithiumScratchDouble f30
#define kDoubleRegZero f28
// Used on mips32r6 for compare operations.
#define kDoubleCompareReg f31
// FPU (coprocessor 1) control registers.
// Currently only FCSR (#31) is implemented.
@ -465,11 +467,20 @@ class Assembler : public AssemblerBase {
// position. Links the label to the current position if it is still unbound.
// Manages the jump elimination optimization if the second parameter is true.
int32_t branch_offset(Label* L, bool jump_elimination_allowed);
int32_t branch_offset_compact(Label* L, bool jump_elimination_allowed);
int32_t branch_offset21(Label* L, bool jump_elimination_allowed);
int32_t branch_offset21_compact(Label* L, bool jump_elimination_allowed);
int32_t shifted_branch_offset(Label* L, bool jump_elimination_allowed) {
int32_t o = branch_offset(L, jump_elimination_allowed);
DCHECK((o & 3) == 0); // Assert the offset is aligned.
return o >> 2;
}
int32_t shifted_branch_offset_compact(Label* L,
bool jump_elimination_allowed) {
int32_t o = branch_offset_compact(L, jump_elimination_allowed);
DCHECK((o & 3) == 0); // Assert the offset is aligned.
return o >> 2;
}
uint32_t jump_address(Label* L);
// Puts a labels target address at the given position.
@ -627,15 +638,99 @@ class Assembler : public AssemblerBase {
beq(rs, rt, branch_offset(L, false) >> 2);
}
void bgez(Register rs, int16_t offset);
void bgezc(Register rt, int16_t offset);
void bgezc(Register rt, Label* L) {
bgezc(rt, branch_offset_compact(L, false)>>2);
}
void bgeuc(Register rs, Register rt, int16_t offset);
void bgeuc(Register rs, Register rt, Label* L) {
bgeuc(rs, rt, branch_offset_compact(L, false)>>2);
}
void bgec(Register rs, Register rt, int16_t offset);
void bgec(Register rs, Register rt, Label* L) {
bgec(rs, rt, branch_offset_compact(L, false)>>2);
}
void bgezal(Register rs, int16_t offset);
void bgezalc(Register rt, int16_t offset);
void bgezalc(Register rt, Label* L) {
bgezalc(rt, branch_offset_compact(L, false)>>2);
}
void bgezall(Register rs, int16_t offset);
void bgezall(Register rs, Label* L) {
bgezall(rs, branch_offset(L, false)>>2);
}
void bgtz(Register rs, int16_t offset);
void bgtzc(Register rt, int16_t offset);
void bgtzc(Register rt, Label* L) {
bgtzc(rt, branch_offset_compact(L, false)>>2);
}
void blez(Register rs, int16_t offset);
void blezc(Register rt, int16_t offset);
void blezc(Register rt, Label* L) {
blezc(rt, branch_offset_compact(L, false)>>2);
}
void bltz(Register rs, int16_t offset);
void bltzc(Register rt, int16_t offset);
void bltzc(Register rt, Label* L) {
bltzc(rt, branch_offset_compact(L, false)>>2);
}
void bltuc(Register rs, Register rt, int16_t offset);
void bltuc(Register rs, Register rt, Label* L) {
bltuc(rs, rt, branch_offset_compact(L, false)>>2);
}
void bltc(Register rs, Register rt, int16_t offset);
void bltc(Register rs, Register rt, Label* L) {
bltc(rs, rt, branch_offset_compact(L, false)>>2);
}
void bltzal(Register rs, int16_t offset);
void blezalc(Register rt, int16_t offset);
void blezalc(Register rt, Label* L) {
blezalc(rt, branch_offset_compact(L, false)>>2);
}
void bltzalc(Register rt, int16_t offset);
void bltzalc(Register rt, Label* L) {
bltzalc(rt, branch_offset_compact(L, false)>>2);
}
void bgtzalc(Register rt, int16_t offset);
void bgtzalc(Register rt, Label* L) {
bgtzalc(rt, branch_offset_compact(L, false)>>2);
}
void beqzalc(Register rt, int16_t offset);
void beqzalc(Register rt, Label* L) {
beqzalc(rt, branch_offset_compact(L, false)>>2);
}
void beqc(Register rs, Register rt, int16_t offset);
void beqc(Register rs, Register rt, Label* L) {
beqc(rs, rt, branch_offset_compact(L, false)>>2);
}
void beqzc(Register rs, int32_t offset);
void beqzc(Register rs, Label* L) {
beqzc(rs, branch_offset21_compact(L, false)>>2);
}
void bnezalc(Register rt, int16_t offset);
void bnezalc(Register rt, Label* L) {
bnezalc(rt, branch_offset_compact(L, false)>>2);
}
void bnec(Register rs, Register rt, int16_t offset);
void bnec(Register rs, Register rt, Label* L) {
bnec(rs, rt, branch_offset_compact(L, false)>>2);
}
void bnezc(Register rt, int32_t offset);
void bnezc(Register rt, Label* L) {
bnezc(rt, branch_offset21_compact(L, false)>>2);
}
void bne(Register rs, Register rt, int16_t offset);
void bne(Register rs, Register rt, Label* L) {
bne(rs, rt, branch_offset(L, false)>>2);
}
void bovc(Register rs, Register rt, int16_t offset);
void bovc(Register rs, Register rt, Label* L) {
bovc(rs, rt, branch_offset_compact(L, false)>>2);
}
void bnvc(Register rs, Register rt, int16_t offset);
void bnvc(Register rs, Register rt, Label* L) {
bnvc(rs, rt, branch_offset_compact(L, false)>>2);
}
// Never use the int16_t b(l)cond version with a branch offset
// instead of using the Label* version.
@ -658,7 +753,14 @@ class Assembler : public AssemblerBase {
void multu(Register rs, Register rt);
void div(Register rs, Register rt);
void divu(Register rs, Register rt);
void div(Register rd, Register rs, Register rt);
void divu(Register rd, Register rs, Register rt);
void mod(Register rd, Register rs, Register rt);
void modu(Register rd, Register rs, Register rt);
void mul(Register rd, Register rs, Register rt);
void muh(Register rd, Register rs, Register rt);
void mulu(Register rd, Register rs, Register rt);
void muhu(Register rd, Register rs, Register rt);
void addiu(Register rd, Register rs, int32_t j);
@ -672,6 +774,7 @@ class Assembler : public AssemblerBase {
void ori(Register rd, Register rs, int32_t j);
void xori(Register rd, Register rs, int32_t j);
void lui(Register rd, int32_t j);
void aui(Register rs, Register rt, int32_t j);
// Shifts.
// Please note: sll(zero_reg, zero_reg, x) instructions are reserved as nop
@ -736,6 +839,15 @@ class Assembler : public AssemblerBase {
void movt(Register rd, Register rs, uint16_t cc = 0);
void movf(Register rd, Register rs, uint16_t cc = 0);
void sel(SecondaryField fmt, FPURegister fd, FPURegister ft,
FPURegister fs, uint8_t sel);
void seleqz(Register rs, Register rt, Register rd);
void seleqz(SecondaryField fmt, FPURegister fd, FPURegister ft,
FPURegister fs);
void selnez(Register rs, Register rt, Register rd);
void selnez(SecondaryField fmt, FPURegister fd, FPURegister ft,
FPURegister fs);
// Bit twiddling.
void clz(Register rd, Register rs);
void ins_(Register rt, Register rs, uint16_t pos, uint16_t size);
@ -751,7 +863,10 @@ class Assembler : public AssemblerBase {
void sdc1(FPURegister fs, const MemOperand& dst);
void mtc1(Register rt, FPURegister fs);
void mthc1(Register rt, FPURegister fs);
void mfc1(Register rt, FPURegister fs);
void mfhc1(Register rt, FPURegister fs);
void ctc1(Register rt, FPUControlRegister fs);
void cfc1(Register rt, FPUControlRegister fs);
@ -790,6 +905,11 @@ class Assembler : public AssemblerBase {
void ceil_l_s(FPURegister fd, FPURegister fs);
void ceil_l_d(FPURegister fd, FPURegister fs);
void min(SecondaryField fmt, FPURegister fd, FPURegister ft, FPURegister fs);
void mina(SecondaryField fmt, FPURegister fd, FPURegister ft, FPURegister fs);
void max(SecondaryField fmt, FPURegister fd, FPURegister ft, FPURegister fs);
void maxa(SecondaryField fmt, FPURegister fd, FPURegister ft, FPURegister fs);
void cvt_s_w(FPURegister fd, FPURegister fs);
void cvt_s_l(FPURegister fd, FPURegister fs);
void cvt_s_d(FPURegister fd, FPURegister fs);
@ -798,7 +918,20 @@ class Assembler : public AssemblerBase {
void cvt_d_l(FPURegister fd, FPURegister fs);
void cvt_d_s(FPURegister fd, FPURegister fs);
// Conditions and branches.
// Conditions and branches for MIPSr6.
void cmp(FPUCondition cond, SecondaryField fmt,
FPURegister fd, FPURegister ft, FPURegister fs);
void bc1eqz(int16_t offset, FPURegister ft);
void bc1eqz(Label* L, FPURegister ft) {
bc1eqz(branch_offset(L, false)>>2, ft);
}
void bc1nez(int16_t offset, FPURegister ft);
void bc1nez(Label* L, FPURegister ft) {
bc1nez(branch_offset(L, false)>>2, ft);
}
// Conditions and branches for non MIPSr6.
void c(FPUCondition cond, SecondaryField fmt,
FPURegister ft, FPURegister fs, uint16_t cc = 0);

View File

@ -1022,16 +1022,28 @@ void ICCompareStub::GenerateGeneric(MacroAssembler* masm) {
// Check if LESS condition is satisfied. If true, move conditionally
// result to v0.
__ c(OLT, D, f12, f14);
__ Movt(v0, t0);
// Use previous check to store conditionally to v0 oposite condition
// (GREATER). If rhs is equal to lhs, this will be corrected in next
// check.
__ Movf(v0, t1);
// Check if EQUAL condition is satisfied. If true, move conditionally
// result to v0.
__ c(EQ, D, f12, f14);
__ Movt(v0, t2);
if (!IsMipsArchVariant(kMips32r6)) {
__ c(OLT, D, f12, f14);
__ Movt(v0, t0);
// Use previous check to store conditionally to v0 oposite condition
// (GREATER). If rhs is equal to lhs, this will be corrected in next
// check.
__ Movf(v0, t1);
// Check if EQUAL condition is satisfied. If true, move conditionally
// result to v0.
__ c(EQ, D, f12, f14);
__ Movt(v0, t2);
} else {
Label skip;
__ BranchF(USE_DELAY_SLOT, &skip, NULL, lt, f12, f14);
__ mov(v0, t0); // Return LESS as result.
__ BranchF(USE_DELAY_SLOT, &skip, NULL, eq, f12, f14);
__ mov(v0, t2); // Return EQUAL as result.
__ mov(v0, t1); // Return GREATER as result.
__ bind(&skip);
}
__ Ret();

View File

@ -73,7 +73,8 @@ UnaryMathFunction CreateExpFunction() {
#if defined(V8_HOST_ARCH_MIPS)
MemCopyUint8Function CreateMemCopyUint8Function(MemCopyUint8Function stub) {
#if defined(USE_SIMULATOR)
#if defined(USE_SIMULATOR) || defined(_MIPS_ARCH_MIPS32R6) || \
defined(_MIPS_ARCH_MIPS32RX)
return stub;
#else
size_t actual_size;

View File

@ -278,6 +278,8 @@ Instruction::Type Instruction::InstructionType() const {
case COP1: // Coprocessor instructions.
switch (RsFieldRawNoAssert()) {
case BC1: // Branch on coprocessor condition.
case BC1EQZ:
case BC1NEZ:
return kImmediateType;
default:
return kRegisterType;
@ -292,6 +294,7 @@ Instruction::Type Instruction::InstructionType() const {
case BLEZ:
case BGTZ:
case ADDI:
case DADDI:
case ADDIU:
case SLTI:
case SLTIU:
@ -303,6 +306,8 @@ Instruction::Type Instruction::InstructionType() const {
case BNEL:
case BLEZL:
case BGTZL:
case BEQZC:
case BNEZC:
case LB:
case LH:
case LWL:

View File

@ -4,7 +4,7 @@
#ifndef V8_MIPS_CONSTANTS_H_
#define V8_MIPS_CONSTANTS_H_
#include "src/globals.h"
// UNIMPLEMENTED_ macro for MIPS.
#ifdef DEBUG
#define UNIMPLEMENTED_MIPS() \
@ -17,17 +17,25 @@
#define UNSUPPORTED_MIPS() v8::internal::PrintF("Unsupported instruction.\n")
enum ArchVariants {
kMips32r2,
kMips32r1,
kMips32r1 = v8::internal::MIPSr1,
kMips32r2 = v8::internal::MIPSr2,
kMips32r6 = v8::internal::MIPSr6,
kLoongson
};
#ifdef _MIPS_ARCH_MIPS32R2
static const ArchVariants kArchVariant = kMips32r2;
#elif _MIPS_ARCH_MIPS32R6
static const ArchVariants kArchVariant = kMips32r6;
#elif _MIPS_ARCH_LOONGSON
// The loongson flag refers to the LOONGSON architectures based on MIPS-III,
// which predates (and is a subset of) the mips32r2 and r1 architectures.
static const ArchVariants kArchVariant = kLoongson;
#elif _MIPS_ARCH_MIPS32RX
// This flags referred to compatibility mode that creates universal code that
// can run on any MIPS32 architecture revision. The dynamically generated code
// by v8 is specialized for the MIPS host detected in runtime probing.
static const ArchVariants kArchVariant = kMips32r1;
#else
static const ArchVariants kArchVariant = kMips32r1;
#endif
@ -45,6 +53,22 @@ enum Endianness {
#error Unknown endianness
#endif
enum FpuMode {
kFP32,
kFP64,
kFPXX
};
#if defined(FPU_MODE_FP32)
static const FpuMode kFpuMode = kFP32;
#elif defined(FPU_MODE_FP64)
static const FpuMode kFpuMode = kFP64;
#elif defined(FPU_MODE_FPXX)
static const FpuMode kFpuMode = kFPXX;
#else
static const FpuMode kFpuMode = kFP32;
#endif
#if(defined(__mips_hard_float) && __mips_hard_float != 0)
// Use floating-point coprocessor instructions. This flag is raised when
// -mhard-float is passed to the compiler.
@ -68,6 +92,26 @@ const uint32_t kHoleNanLower32Offset = 4;
#error Unknown endianness
#endif
#ifndef FPU_MODE_FPXX
#define IsFp64Mode() \
(kFpuMode == kFP64)
#else
#define IsFp64Mode() \
(CpuFeatures::IsSupported(FP64FPU))
#endif
#ifndef _MIPS_ARCH_MIPS32RX
#define IsMipsArchVariant(check) \
(kArchVariant == check)
#else
#define IsMipsArchVariant(check) \
(CpuFeatures::IsSupported(check))
#endif
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
// Defines constants and accessor classes to assemble, disassemble and
// simulate MIPS32 instructions.
//
@ -99,6 +143,8 @@ const int kInvalidFPURegister = -1;
const int kFCSRRegister = 31;
const int kInvalidFPUControlRegister = -1;
const uint32_t kFPUInvalidResult = static_cast<uint32_t>(1 << 31) - 1;
const uint64_t kFPU64InvalidResult =
static_cast<uint64_t>(static_cast<uint64_t>(1) << 63) - 1;
// FCSR constants.
const uint32_t kFCSRInexactFlagBit = 2;
@ -216,10 +262,14 @@ const int kLuiShift = 16;
const int kImm16Shift = 0;
const int kImm16Bits = 16;
const int kImm21Shift = 0;
const int kImm21Bits = 21;
const int kImm26Shift = 0;
const int kImm26Bits = 26;
const int kImm28Shift = 0;
const int kImm28Bits = 28;
const int kImm32Shift = 0;
const int kImm32Bits = 32;
// In branches and jumps immediate fields point to words, not bytes,
// and are therefore shifted by 2.
@ -278,14 +328,16 @@ enum Opcode {
ANDI = ((1 << 3) + 4) << kOpcodeShift,
ORI = ((1 << 3) + 5) << kOpcodeShift,
XORI = ((1 << 3) + 6) << kOpcodeShift,
LUI = ((1 << 3) + 7) << kOpcodeShift,
LUI = ((1 << 3) + 7) << kOpcodeShift, // LUI/AUI family.
BEQC = ((2 << 3) + 0) << kOpcodeShift,
COP1 = ((2 << 3) + 1) << kOpcodeShift, // Coprocessor 1 class.
BEQL = ((2 << 3) + 4) << kOpcodeShift,
BNEL = ((2 << 3) + 5) << kOpcodeShift,
BLEZL = ((2 << 3) + 6) << kOpcodeShift,
BGTZL = ((2 << 3) + 7) << kOpcodeShift,
DADDI = ((3 << 3) + 0) << kOpcodeShift, // This is also BNEC.
SPECIAL2 = ((3 << 3) + 4) << kOpcodeShift,
SPECIAL3 = ((3 << 3) + 7) << kOpcodeShift,
@ -304,11 +356,13 @@ enum Opcode {
LWC1 = ((6 << 3) + 1) << kOpcodeShift,
LDC1 = ((6 << 3) + 5) << kOpcodeShift,
BEQZC = ((6 << 3) + 6) << kOpcodeShift,
PREF = ((6 << 3) + 3) << kOpcodeShift,
SWC1 = ((7 << 3) + 1) << kOpcodeShift,
SDC1 = ((7 << 3) + 5) << kOpcodeShift,
BNEZC = ((7 << 3) + 6) << kOpcodeShift,
COP1X = ((1 << 4) + 3) << kOpcodeShift
};
@ -330,6 +384,8 @@ enum SecondaryField {
BREAK = ((1 << 3) + 5),
MFHI = ((2 << 3) + 0),
CLZ_R6 = ((2 << 3) + 0),
CLO_R6 = ((2 << 3) + 1),
MFLO = ((2 << 3) + 2),
MULT = ((3 << 3) + 0),
@ -354,7 +410,21 @@ enum SecondaryField {
TLT = ((6 << 3) + 2),
TLTU = ((6 << 3) + 3),
TEQ = ((6 << 3) + 4),
SELEQZ_S = ((6 << 3) + 5),
TNE = ((6 << 3) + 6),
SELNEZ_S = ((6 << 3) + 7),
// Multiply integers in r6.
MUL_MUH = ((3 << 3) + 0), // MUL, MUH.
MUL_MUH_U = ((3 << 3) + 1), // MUL_U, MUH_U.
MUL_OP = ((0 << 3) + 2),
MUH_OP = ((0 << 3) + 3),
DIV_OP = ((0 << 3) + 2),
MOD_OP = ((0 << 3) + 3),
DIV_MOD = ((3 << 3) + 2),
DIV_MOD_U = ((3 << 3) + 3),
// SPECIAL2 Encoding of Function Field.
MUL = ((0 << 3) + 2),
@ -370,6 +440,7 @@ enum SecondaryField {
BGEZ = ((0 << 3) + 1) << 16,
BLTZAL = ((2 << 3) + 0) << 16,
BGEZAL = ((2 << 3) + 1) << 16,
BGEZALL = ((2 << 3) + 3) << 16,
// COP1 Encoding of rs Field.
MFC1 = ((0 << 3) + 0) << 21,
@ -414,6 +485,10 @@ enum SecondaryField {
TRUNC_W_D = ((1 << 3) + 5),
CEIL_W_D = ((1 << 3) + 6),
FLOOR_W_D = ((1 << 3) + 7),
MIN = ((3 << 3) + 4),
MINA = ((3 << 3) + 5),
MAX = ((3 << 3) + 6),
MAXA = ((3 << 3) + 7),
CVT_S_D = ((4 << 3) + 0),
CVT_W_D = ((4 << 3) + 4),
CVT_L_D = ((4 << 3) + 5),
@ -430,6 +505,46 @@ enum SecondaryField {
CVT_D_W = ((4 << 3) + 1),
CVT_S_L = ((4 << 3) + 0),
CVT_D_L = ((4 << 3) + 1),
BC1EQZ = ((2 << 2) + 1) << 21,
BC1NEZ = ((3 << 2) + 1) << 21,
// COP1 CMP positive predicates Bit 5..4 = 00.
CMP_AF = ((0 << 3) + 0),
CMP_UN = ((0 << 3) + 1),
CMP_EQ = ((0 << 3) + 2),
CMP_UEQ = ((0 << 3) + 3),
CMP_LT = ((0 << 3) + 4),
CMP_ULT = ((0 << 3) + 5),
CMP_LE = ((0 << 3) + 6),
CMP_ULE = ((0 << 3) + 7),
CMP_SAF = ((1 << 3) + 0),
CMP_SUN = ((1 << 3) + 1),
CMP_SEQ = ((1 << 3) + 2),
CMP_SUEQ = ((1 << 3) + 3),
CMP_SSLT = ((1 << 3) + 4),
CMP_SSULT = ((1 << 3) + 5),
CMP_SLE = ((1 << 3) + 6),
CMP_SULE = ((1 << 3) + 7),
// COP1 CMP negative predicates Bit 5..4 = 01.
CMP_AT = ((2 << 3) + 0), // Reserved, not implemented.
CMP_OR = ((2 << 3) + 1),
CMP_UNE = ((2 << 3) + 2),
CMP_NE = ((2 << 3) + 3),
CMP_UGE = ((2 << 3) + 4), // Reserved, not implemented.
CMP_OGE = ((2 << 3) + 5), // Reserved, not implemented.
CMP_UGT = ((2 << 3) + 6), // Reserved, not implemented.
CMP_OGT = ((2 << 3) + 7), // Reserved, not implemented.
CMP_SAT = ((3 << 3) + 0), // Reserved, not implemented.
CMP_SOR = ((3 << 3) + 1),
CMP_SUNE = ((3 << 3) + 2),
CMP_SNE = ((3 << 3) + 3),
CMP_SUGE = ((3 << 3) + 4), // Reserved, not implemented.
CMP_SOGE = ((3 << 3) + 5), // Reserved, not implemented.
CMP_SUGT = ((3 << 3) + 6), // Reserved, not implemented.
CMP_SOGT = ((3 << 3) + 7), // Reserved, not implemented.
SEL = ((2 << 3) + 0),
SELEQZ_C = ((2 << 3) + 4), // COP1 on FPR registers.
SELNEZ_C = ((2 << 3) + 7), // COP1 on FPR registers.
// COP1 Encoding of Function Field When rs=PS.
// COP1X Encoding of Function Field.
MADD_D = ((4 << 3) + 1),
@ -775,6 +890,11 @@ class Instruction {
return Bits(kImm16Shift + kImm16Bits - 1, kImm16Shift);
}
inline int32_t Imm21Value() const {
DCHECK(InstructionType() == kImmediateType);
return Bits(kImm21Shift + kImm21Bits - 1, kImm21Shift);
}
inline int32_t Imm26Value() const {
DCHECK(InstructionType() == kJumpType);
return Bits(kImm26Shift + kImm26Bits - 1, kImm26Shift);

View File

@ -86,6 +86,7 @@ class Decoder {
void PrintUImm16(Instruction* instr);
void PrintSImm16(Instruction* instr);
void PrintXImm16(Instruction* instr);
void PrintXImm21(Instruction* instr);
void PrintXImm26(Instruction* instr);
void PrintCode(Instruction* instr); // For break and trap instructions.
// Printing of instruction name.
@ -246,6 +247,13 @@ void Decoder::PrintXImm16(Instruction* instr) {
}
// Print 21-bit immediate value.
void Decoder::PrintXImm21(Instruction* instr) {
uint32_t imm = instr->Imm21Value();
out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm);
}
// Print 26-bit immediate value.
void Decoder::PrintXImm26(Instruction* instr) {
uint32_t imm = instr->Imm26Value() << kImmFieldShift;
@ -360,7 +368,11 @@ int Decoder::FormatOption(Instruction* instr, const char* format) {
PrintXImm16(instr);
}
return 6;
} else {
} else if (format[3] == '2' && format[4] == '1') {
DCHECK(STRING_STARTS_WITH(format, "imm21x"));
PrintXImm21(instr);
return 6;
} else if (format[3] == '2' && format[4] == '6') {
DCHECK(STRING_STARTS_WITH(format, "imm26x"));
PrintXImm26(instr);
return 6;
@ -492,25 +504,15 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
case CVT_W_D:
Format(instr, "cvt.w.d 'fd, 'fs");
break;
case CVT_L_D: {
if (kArchVariant == kMips32r2) {
Format(instr, "cvt.l.d 'fd, 'fs");
} else {
Unknown(instr);
}
case CVT_L_D:
Format(instr, "cvt.l.d 'fd, 'fs");
break;
}
case TRUNC_W_D:
Format(instr, "trunc.w.d 'fd, 'fs");
break;
case TRUNC_L_D: {
if (kArchVariant == kMips32r2) {
Format(instr, "trunc.l.d 'fd, 'fs");
} else {
Unknown(instr);
}
case TRUNC_L_D:
Format(instr, "trunc.l.d 'fd, 'fs");
break;
}
case ROUND_W_D:
Format(instr, "round.w.d 'fd, 'fs");
break;
@ -569,22 +571,42 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
break;
case L:
switch (instr->FunctionFieldRaw()) {
case CVT_D_L: {
if (kArchVariant == kMips32r2) {
Format(instr, "cvt.d.l 'fd, 'fs");
} else {
Unknown(instr);
}
case CVT_D_L:
Format(instr, "cvt.d.l 'fd, 'fs");
break;
}
case CVT_S_L: {
if (kArchVariant == kMips32r2) {
Format(instr, "cvt.s.l 'fd, 'fs");
} else {
Unknown(instr);
}
case CVT_S_L:
Format(instr, "cvt.s.l 'fd, 'fs");
break;
case CMP_UN:
Format(instr, "cmp.un.d 'fd, 'fs, 'ft");
break;
case CMP_EQ:
Format(instr, "cmp.eq.d 'fd, 'fs, 'ft");
break;
case CMP_UEQ:
Format(instr, "cmp.ueq.d 'fd, 'fs, 'ft");
break;
case CMP_LT:
Format(instr, "cmp.lt.d 'fd, 'fs, 'ft");
break;
case CMP_ULT:
Format(instr, "cmp.ult.d 'fd, 'fs, 'ft");
break;
case CMP_LE:
Format(instr, "cmp.le.d 'fd, 'fs, 'ft");
break;
case CMP_ULE:
Format(instr, "cmp.ule.d 'fd, 'fs, 'ft");
break;
case CMP_OR:
Format(instr, "cmp.or.d 'fd, 'fs, 'ft");
break;
case CMP_UNE:
Format(instr, "cmp.une.d 'fd, 'fs, 'ft");
break;
case CMP_NE:
Format(instr, "cmp.ne.d 'fd, 'fs, 'ft");
break;
}
default:
UNREACHABLE();
}
@ -623,7 +645,7 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
if (instr->RsValue() == 0) {
Format(instr, "srl 'rd, 'rt, 'sa");
} else {
if (kArchVariant == kMips32r2) {
if (IsMipsArchVariant(kMips32r2)) {
Format(instr, "rotr 'rd, 'rt, 'sa");
} else {
Unknown(instr);
@ -640,7 +662,7 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
if (instr->SaValue() == 0) {
Format(instr, "srlv 'rd, 'rt, 'rs");
} else {
if (kArchVariant == kMips32r2) {
if (IsMipsArchVariant(kMips32r2)) {
Format(instr, "rotrv 'rd, 'rt, 'rs");
} else {
Unknown(instr);
@ -651,22 +673,64 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
Format(instr, "srav 'rd, 'rt, 'rs");
break;
case MFHI:
Format(instr, "mfhi 'rd");
if (instr->Bits(25, 16) == 0) {
Format(instr, "mfhi 'rd");
} else {
if ((instr->FunctionFieldRaw() == CLZ_R6)
&& (instr->FdValue() == 1)) {
Format(instr, "clz 'rd, 'rs");
} else if ((instr->FunctionFieldRaw() == CLO_R6)
&& (instr->FdValue() == 1)) {
Format(instr, "clo 'rd, 'rs");
}
}
break;
case MFLO:
Format(instr, "mflo 'rd");
break;
case MULT:
Format(instr, "mult 'rs, 'rt");
case MULT: // @Mips32r6 == MUL_MUH.
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "mult 'rs, 'rt");
} else {
if (instr->SaValue() == MUL_OP) {
Format(instr, "mul 'rd, 'rs, 'rt");
} else {
Format(instr, "muh 'rd, 'rs, 'rt");
}
}
break;
case MULTU:
Format(instr, "multu 'rs, 'rt");
case MULTU: // @Mips32r6 == MUL_MUH_U.
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "multu 'rs, 'rt");
} else {
if (instr->SaValue() == MUL_OP) {
Format(instr, "mulu 'rd, 'rs, 'rt");
} else {
Format(instr, "muhu 'rd, 'rs, 'rt");
}
}
break;
case DIV:
Format(instr, "div 'rs, 'rt");
case DIV: // @Mips32r6 == DIV_MOD.
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "div 'rs, 'rt");
} else {
if (instr->SaValue() == DIV_OP) {
Format(instr, "div 'rd, 'rs, 'rt");
} else {
Format(instr, "mod 'rd, 'rs, 'rt");
}
}
break;
case DIVU:
Format(instr, "divu 'rs, 'rt");
case DIVU: // @Mips32r6 == DIV_MOD_U.
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "divu 'rs, 'rt");
} else {
if (instr->SaValue() == DIV_OP) {
Format(instr, "divu 'rd, 'rs, 'rt");
} else {
Format(instr, "modu 'rd, 'rs, 'rt");
}
}
break;
case ADD:
Format(instr, "add 'rd, 'rs, 'rt");
@ -738,6 +802,12 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
Format(instr, "movf 'rd, 'rs, 'bc");
}
break;
case SELEQZ_S:
Format(instr, "seleqz 'rd, 'rs, 'rt");
break;
case SELNEZ_S:
Format(instr, "selnez 'rd, 'rs, 'rt");
break;
default:
UNREACHABLE();
}
@ -748,7 +818,9 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
Format(instr, "mul 'rd, 'rs, 'rt");
break;
case CLZ:
Format(instr, "clz 'rd, 'rs");
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "clz 'rd, 'rs");
}
break;
default:
UNREACHABLE();
@ -757,7 +829,7 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
case SPECIAL3:
switch (instr->FunctionFieldRaw()) {
case INS: {
if (kArchVariant == kMips32r2) {
if (IsMipsArchVariant(kMips32r2)) {
Format(instr, "ins 'rt, 'rs, 'sa, 'ss2");
} else {
Unknown(instr);
@ -765,7 +837,7 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
break;
}
case EXT: {
if (kArchVariant == kMips32r2) {
if (IsMipsArchVariant(kMips32r2)) {
Format(instr, "ext 'rt, 'rs, 'sa, 'ss1");
} else {
Unknown(instr);
@ -784,7 +856,6 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
void Decoder::DecodeTypeImmediate(Instruction* instr) {
switch (instr->OpcodeFieldRaw()) {
// ------------- REGIMM class.
case COP1:
switch (instr->RsFieldRaw()) {
case BC1:
@ -794,10 +865,150 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) {
Format(instr, "bc1f 'bc, 'imm16u");
}
break;
case BC1EQZ:
Format(instr, "bc1eqz 'ft, 'imm16u");
break;
case BC1NEZ:
Format(instr, "bc1nez 'ft, 'imm16u");
break;
case W: // CMP.S instruction.
switch (instr->FunctionValue()) {
case CMP_AF:
Format(instr, "cmp.af.S 'ft, 'fs, 'fd");
break;
case CMP_UN:
Format(instr, "cmp.un.S 'ft, 'fs, 'fd");
break;
case CMP_EQ:
Format(instr, "cmp.eq.S 'ft, 'fs, 'fd");
break;
case CMP_UEQ:
Format(instr, "cmp.ueq.S 'ft, 'fs, 'fd");
break;
case CMP_LT:
Format(instr, "cmp.lt.S 'ft, 'fs, 'fd");
break;
case CMP_ULT:
Format(instr, "cmp.ult.S 'ft, 'fs, 'fd");
break;
case CMP_LE:
Format(instr, "cmp.le.S 'ft, 'fs, 'fd");
break;
case CMP_ULE:
Format(instr, "cmp.ule.S 'ft, 'fs, 'fd");
break;
case CMP_OR:
Format(instr, "cmp.or.S 'ft, 'fs, 'fd");
break;
case CMP_UNE:
Format(instr, "cmp.une.S 'ft, 'fs, 'fd");
break;
case CMP_NE:
Format(instr, "cmp.ne.S 'ft, 'fs, 'fd");
break;
default:
UNREACHABLE();
}
break;
case L: // CMP.D instruction.
switch (instr->FunctionValue()) {
case CMP_AF:
Format(instr, "cmp.af.D 'ft, 'fs, 'fd");
break;
case CMP_UN:
Format(instr, "cmp.un.D 'ft, 'fs, 'fd");
break;
case CMP_EQ:
Format(instr, "cmp.eq.D 'ft, 'fs, 'fd");
break;
case CMP_UEQ:
Format(instr, "cmp.ueq.D 'ft, 'fs, 'fd");
break;
case CMP_LT:
Format(instr, "cmp.lt.D 'ft, 'fs, 'fd");
break;
case CMP_ULT:
Format(instr, "cmp.ult.D 'ft, 'fs, 'fd");
break;
case CMP_LE:
Format(instr, "cmp.le.D 'ft, 'fs, 'fd");
break;
case CMP_ULE:
Format(instr, "cmp.ule.D 'ft, 'fs, 'fd");
break;
case CMP_OR:
Format(instr, "cmp.or.D 'ft, 'fs, 'fd");
break;
case CMP_UNE:
Format(instr, "cmp.une.D 'ft, 'fs, 'fd");
break;
case CMP_NE:
Format(instr, "cmp.ne.D 'ft, 'fs, 'fd");
break;
default:
UNREACHABLE();
}
break;
case S:
switch (instr->FunctionValue()) {
case SEL:
Format(instr, "sel.S 'ft, 'fs, 'fd");
break;
case SELEQZ_C:
Format(instr, "seleqz.S 'ft, 'fs, 'fd");
break;
case SELNEZ_C:
Format(instr, "selnez.S 'ft, 'fs, 'fd");
break;
case MIN:
Format(instr, "min.S 'ft, 'fs, 'fd");
break;
case MINA:
Format(instr, "mina.S 'ft, 'fs, 'fd");
break;
case MAX:
Format(instr, "max.S 'ft, 'fs, 'fd");
break;
case MAXA:
Format(instr, "maxa.S 'ft, 'fs, 'fd");
break;
default:
UNREACHABLE();
}
break;
case D:
switch (instr->FunctionValue()) {
case SEL:
Format(instr, "sel.D 'ft, 'fs, 'fd");
break;
case SELEQZ_C:
Format(instr, "seleqz.D 'ft, 'fs, 'fd");
break;
case SELNEZ_C:
Format(instr, "selnez.D 'ft, 'fs, 'fd");
break;
case MIN:
Format(instr, "min.D 'ft, 'fs, 'fd");
break;
case MINA:
Format(instr, "mina.D 'ft, 'fs, 'fd");
break;
case MAX:
Format(instr, "max.D 'ft, 'fs, 'fd");
break;
case MAXA:
Format(instr, "maxa.D 'ft, 'fs, 'fd");
break;
default:
UNREACHABLE();
}
break;
default:
UNREACHABLE();
}
break; // Case COP1.
// ------------- REGIMM class.
case REGIMM:
switch (instr->RtFieldRaw()) {
case BLTZ:
@ -812,6 +1023,9 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) {
case BGEZAL:
Format(instr, "bgezal 'rs, 'imm16u");
break;
case BGEZALL:
Format(instr, "bgezall 'rs, 'imm16u");
break;
default:
UNREACHABLE();
}
@ -824,14 +1038,103 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) {
Format(instr, "bne 'rs, 'rt, 'imm16u");
break;
case BLEZ:
Format(instr, "blez 'rs, 'imm16u");
if ((instr->RtFieldRaw() == 0)
&& (instr->RsFieldRaw() != 0)) {
Format(instr, "blez 'rs, 'imm16u");
} else if ((instr->RtFieldRaw() != instr->RsFieldRaw())
&& (instr->RsFieldRaw() != 0) && (instr->RtFieldRaw() != 0)) {
Format(instr, "bgeuc 'rs, 'rt, 'imm16u");
} else if ((instr->RtFieldRaw() == instr->RsFieldRaw())
&& (instr->RtFieldRaw() != 0)) {
Format(instr, "bgezalc 'rs, 'imm16u");
} else if ((instr->RsFieldRaw() == 0)
&& (instr->RtFieldRaw() != 0)) {
Format(instr, "blezalc 'rs, 'imm16u");
} else {
UNREACHABLE();
}
break;
case BGTZ:
Format(instr, "bgtz 'rs, 'imm16u");
if ((instr->RtFieldRaw() == 0)
&& (instr->RsFieldRaw() != 0)) {
Format(instr, "bgtz 'rs, 'imm16u");
} else if ((instr->RtFieldRaw() != instr->RsFieldRaw())
&& (instr->RsFieldRaw() != 0) && (instr->RtFieldRaw() != 0)) {
Format(instr, "bltuc 'rs, 'rt, 'imm16u");
} else if ((instr->RtFieldRaw() == instr->RsFieldRaw())
&& (instr->RtFieldRaw() != 0)) {
Format(instr, "bltzalc 'rt, 'imm16u");
} else if ((instr->RsFieldRaw() == 0)
&& (instr->RtFieldRaw() != 0)) {
Format(instr, "bgtzalc 'rt, 'imm16u");
} else {
UNREACHABLE();
}
break;
case BLEZL:
if ((instr->RtFieldRaw() == instr->RsFieldRaw())
&& (instr->RtFieldRaw() != 0)) {
Format(instr, "bgezc 'rt, 'imm16u");
} else if ((instr->RtFieldRaw() != instr->RsFieldRaw())
&& (instr->RsFieldRaw() != 0) && (instr->RtFieldRaw() != 0)) {
Format(instr, "bgec 'rs, 'rt, 'imm16u");
} else if ((instr->RsFieldRaw() == 0)
&& (instr->RtFieldRaw() != 0)) {
Format(instr, "blezc 'rt, 'imm16u");
} else {
UNREACHABLE();
}
break;
case BGTZL:
if ((instr->RtFieldRaw() == instr->RsFieldRaw())
&& (instr->RtFieldRaw() != 0)) {
Format(instr, "bltzc 'rt, 'imm16u");
} else if ((instr->RtFieldRaw() != instr->RsFieldRaw())
&& (instr->RsFieldRaw() != 0) && (instr->RtFieldRaw() != 0)) {
Format(instr, "bltc 'rs, 'rt, 'imm16u");
} else if ((instr->RsFieldRaw() == 0)
&& (instr->RtFieldRaw() != 0)) {
Format(instr, "bgtzc 'rt, 'imm16u");
} else {
UNREACHABLE();
}
break;
case BEQZC:
if (instr->RsFieldRaw() != 0) {
Format(instr, "beqzc 'rs, 'imm21x");
}
break;
case BNEZC:
if (instr->RsFieldRaw() != 0) {
Format(instr, "bnezc 'rs, 'imm21x");
}
break;
// ------------- Arithmetic instructions.
case ADDI:
Format(instr, "addi 'rt, 'rs, 'imm16s");
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "addi 'rt, 'rs, 'imm16s");
} else {
// Check if BOVC or BEQC instruction.
if (instr->RsFieldRaw() >= instr->RtFieldRaw()) {
Format(instr, "bovc 'rs, 'rt, 'imm16s");
} else if (instr->RsFieldRaw() < instr->RtFieldRaw()) {
Format(instr, "beqc 'rs, 'rt, 'imm16s");
} else {
UNREACHABLE();
}
}
break;
case DADDI:
if (IsMipsArchVariant(kMips32r6)) {
// Check if BNVC or BNEC instruction.
if (instr->RsFieldRaw() >= instr->RtFieldRaw()) {
Format(instr, "bnvc 'rs, 'rt, 'imm16s");
} else if (instr->RsFieldRaw() < instr->RtFieldRaw()) {
Format(instr, "bnec 'rs, 'rt, 'imm16s");
} else {
UNREACHABLE();
}
}
break;
case ADDIU:
Format(instr, "addiu 'rt, 'rs, 'imm16s");
@ -852,7 +1155,15 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) {
Format(instr, "xori 'rt, 'rs, 'imm16x");
break;
case LUI:
Format(instr, "lui 'rt, 'imm16x");
if (!IsMipsArchVariant(kMips32r6)) {
Format(instr, "lui 'rt, 'imm16x");
} else {
if (instr->RsValue() != 0) {
Format(instr, "aui 'rt, 'imm16x");
} else {
Format(instr, "lui 'rt, 'imm16x");
}
}
break;
// ------------- Memory instructions.
case LB:
@ -907,6 +1218,7 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) {
Format(instr, "sdc1 'ft, 'imm16s('rs)");
break;
default:
printf("a 0x%x \n", instr->OpcodeFieldRaw());
UNREACHABLE();
break;
}

View File

@ -2369,12 +2369,9 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
break;
case Token::MUL: {
__ SmiUntag(scratch1, right);
__ Mult(left, scratch1);
__ mflo(scratch1);
__ mfhi(scratch2);
__ sra(scratch1, scratch1, 31);
__ Mul(scratch2, v0, left, scratch1);
__ sra(scratch1, v0, 31);
__ Branch(&stub_call, ne, scratch1, Operand(scratch2));
__ mflo(v0);
__ Branch(&done, ne, v0, Operand(zero_reg));
__ Addu(scratch2, right, left);
__ Branch(&stub_call, lt, scratch2, Operand(zero_reg));
@ -3943,12 +3940,10 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(CallRuntime* expr) {
// smi but the other values are, so the result is a smi.
__ lw(scratch1, FieldMemOperand(separator, SeqOneByteString::kLengthOffset));
__ Subu(string_length, string_length, Operand(scratch1));
__ Mult(array_length, scratch1);
__ Mul(scratch3, scratch2, array_length, scratch1);
// Check for smi overflow. No overflow if higher 33 bits of 64-bit result are
// zero.
__ mfhi(scratch2);
__ Branch(&bailout, ne, scratch2, Operand(zero_reg));
__ mflo(scratch2);
__ Branch(&bailout, ne, scratch3, Operand(zero_reg));
__ And(scratch3, scratch2, Operand(0x80000000));
__ Branch(&bailout, ne, scratch3, Operand(zero_reg));
__ AdduAndCheckForOverflow(string_length, string_length, scratch2, scratch3);

View File

@ -1164,7 +1164,7 @@ void LCodeGen::DoModI(LModI* instr) {
const Register result_reg = ToRegister(instr->result());
// div runs in the background while we check for special cases.
__ div(left_reg, right_reg);
__ Mod(result_reg, left_reg, right_reg);
Label done;
// Check for x % 0, we have to deopt in this case because we can't return a
@ -1189,8 +1189,7 @@ void LCodeGen::DoModI(LModI* instr) {
}
// If we care about -0, test if the dividend is <0 and the result is 0.
__ Branch(USE_DELAY_SLOT, &done, ge, left_reg, Operand(zero_reg));
__ mfhi(result_reg);
__ Branch(&done, ge, left_reg, Operand(zero_reg));
if (hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
DeoptimizeIf(eq, instr->environment(), result_reg, Operand(zero_reg));
}
@ -1276,10 +1275,11 @@ void LCodeGen::DoDivI(LDivI* instr) {
Register dividend = ToRegister(instr->dividend());
Register divisor = ToRegister(instr->divisor());
const Register result = ToRegister(instr->result());
Register remainder = ToRegister(instr->temp());
// On MIPS div is asynchronous - it will run in the background while we
// check for special cases.
__ div(dividend, divisor);
__ Div(remainder, result, dividend, divisor);
// Check for x / 0.
if (hdiv->CheckFlag(HValue::kCanBeDivByZero)) {
@ -1304,11 +1304,7 @@ void LCodeGen::DoDivI(LDivI* instr) {
}
if (!hdiv->CheckFlag(HValue::kAllUsesTruncatingToInt32)) {
__ mfhi(result);
DeoptimizeIf(ne, instr->environment(), result, Operand(zero_reg));
__ mflo(result);
} else {
__ mflo(result);
DeoptimizeIf(ne, instr->environment(), remainder, Operand(zero_reg));
}
}
@ -1433,10 +1429,10 @@ void LCodeGen::DoFlooringDivI(LFlooringDivI* instr) {
Register dividend = ToRegister(instr->dividend());
Register divisor = ToRegister(instr->divisor());
const Register result = ToRegister(instr->result());
Register remainder = scratch0();
// On MIPS div is asynchronous - it will run in the background while we
// check for special cases.
__ div(dividend, divisor);
__ Div(remainder, result, dividend, divisor);
// Check for x / 0.
if (hdiv->CheckFlag(HValue::kCanBeDivByZero)) {
@ -1462,9 +1458,6 @@ void LCodeGen::DoFlooringDivI(LFlooringDivI* instr) {
// We performed a truncating division. Correct the result if necessary.
Label done;
Register remainder = scratch0();
__ mfhi(remainder);
__ mflo(result);
__ Branch(&done, eq, remainder, Operand(zero_reg), USE_DELAY_SLOT);
__ Xor(remainder, remainder, Operand(divisor));
__ Branch(&done, ge, remainder, Operand(zero_reg));
@ -1553,13 +1546,9 @@ void LCodeGen::DoMulI(LMulI* instr) {
// hi:lo = left * right.
if (instr->hydrogen()->representation().IsSmi()) {
__ SmiUntag(result, left);
__ mult(result, right);
__ mfhi(scratch);
__ mflo(result);
__ Mul(scratch, result, result, right);
} else {
__ mult(left, right);
__ mfhi(scratch);
__ mflo(result);
__ Mul(scratch, result, left, right);
}
__ sra(at, result, 31);
DeoptimizeIf(ne, instr->environment(), scratch, Operand(at));
@ -3740,7 +3729,7 @@ void LCodeGen::DoMathFloor(LMathFloor* instr) {
// Test for -0.
Label done;
__ Branch(&done, ne, result, Operand(zero_reg));
__ mfc1(scratch1, input.high());
__ Mfhc1(scratch1, input);
__ And(scratch1, scratch1, Operand(HeapNumber::kSignMask));
DeoptimizeIf(ne, instr->environment(), scratch1, Operand(zero_reg));
__ bind(&done);
@ -3756,7 +3745,7 @@ void LCodeGen::DoMathRound(LMathRound* instr) {
Label done, check_sign_on_zero;
// Extract exponent bits.
__ mfc1(result, input.high());
__ Mfhc1(result, input);
__ Ext(scratch,
result,
HeapNumber::kExponentShift,
@ -3786,7 +3775,7 @@ void LCodeGen::DoMathRound(LMathRound* instr) {
// Check sign of the result: if the sign changed, the input
// value was in ]0.5, 0[ and the result should be -0.
__ mfc1(result, double_scratch0().high());
__ Mfhc1(result, double_scratch0());
__ Xor(result, result, Operand(scratch));
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
// ARM uses 'mi' here, which is 'lt'
@ -3816,7 +3805,7 @@ void LCodeGen::DoMathRound(LMathRound* instr) {
// Test for -0.
__ Branch(&done, ne, result, Operand(zero_reg));
__ bind(&check_sign_on_zero);
__ mfc1(scratch, input.high());
__ Mfhc1(scratch, input);
__ And(scratch, scratch, Operand(HeapNumber::kSignMask));
DeoptimizeIf(ne, instr->environment(), scratch, Operand(zero_reg));
}
@ -4843,7 +4832,7 @@ void LCodeGen::EmitNumberUntagD(Register input_reg,
if (deoptimize_on_minus_zero) {
__ mfc1(at, result_reg.low());
__ Branch(&done, ne, at, Operand(zero_reg));
__ mfc1(scratch, result_reg.high());
__ Mfhc1(scratch, result_reg);
DeoptimizeIf(eq, env, scratch, Operand(HeapNumber::kSignMask));
}
__ Branch(&done);
@ -4941,7 +4930,7 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
__ Branch(&done, ne, input_reg, Operand(zero_reg));
__ mfc1(scratch1, double_scratch.high());
__ Mfhc1(scratch1, double_scratch);
__ And(scratch1, scratch1, Operand(HeapNumber::kSignMask));
DeoptimizeIf(ne, instr->environment(), scratch1, Operand(zero_reg));
}
@ -5029,7 +5018,7 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
Label done;
__ Branch(&done, ne, result_reg, Operand(zero_reg));
__ mfc1(scratch1, double_input.high());
__ Mfhc1(scratch1, double_input);
__ And(scratch1, scratch1, Operand(HeapNumber::kSignMask));
DeoptimizeIf(ne, instr->environment(), scratch1, Operand(zero_reg));
__ bind(&done);
@ -5062,7 +5051,7 @@ void LCodeGen::DoDoubleToSmi(LDoubleToSmi* instr) {
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
Label done;
__ Branch(&done, ne, result_reg, Operand(zero_reg));
__ mfc1(scratch1, double_input.high());
__ Mfhc1(scratch1, double_input);
__ And(scratch1, scratch1, Operand(HeapNumber::kSignMask));
DeoptimizeIf(ne, instr->environment(), scratch1, Operand(zero_reg));
__ bind(&done);

View File

@ -1325,8 +1325,9 @@ LInstruction* LChunkBuilder::DoDivI(HDiv* instr) {
DCHECK(instr->right()->representation().Equals(instr->representation()));
LOperand* dividend = UseRegister(instr->left());
LOperand* divisor = UseRegister(instr->right());
LOperand* temp = TempRegister();
LInstruction* result =
DefineAsRegister(new(zone()) LDivI(dividend, divisor));
DefineAsRegister(new(zone()) LDivI(dividend, divisor, temp));
if (instr->CheckFlag(HValue::kCanBeDivByZero) ||
instr->CheckFlag(HValue::kBailoutOnMinusZero) ||
(instr->CheckFlag(HValue::kCanOverflow) &&
@ -1511,7 +1512,7 @@ LInstruction* LChunkBuilder::DoMul(HMul* instr) {
return DefineAsRegister(mul);
} else if (instr->representation().IsDouble()) {
if (kArchVariant == kMips32r2) {
if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) {
if (instr->HasOneUse() && instr->uses().value()->IsAdd()) {
HAdd* add = HAdd::cast(instr->uses().value());
if (instr == add->left()) {
@ -1584,7 +1585,7 @@ LInstruction* LChunkBuilder::DoAdd(HAdd* instr) {
LInstruction* result = DefineAsRegister(add);
return result;
} else if (instr->representation().IsDouble()) {
if (kArchVariant == kMips32r2) {
if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) {
if (instr->left()->IsMul())
return DoMultiplyAdd(HMul::cast(instr->left()), instr->right());

View File

@ -695,15 +695,17 @@ class LDivByConstI V8_FINAL : public LTemplateInstruction<1, 1, 0> {
};
class LDivI V8_FINAL : public LTemplateInstruction<1, 2, 0> {
class LDivI V8_FINAL : public LTemplateInstruction<1, 2, 1> {
public:
LDivI(LOperand* dividend, LOperand* divisor) {
LDivI(LOperand* dividend, LOperand* divisor, LOperand* temp) {
inputs_[0] = dividend;
inputs_[1] = divisor;
temps_[0] = temp;
}
LOperand* dividend() { return inputs_[0]; }
LOperand* divisor() { return inputs_[1]; }
LOperand* temp() { return temps_[0]; }
DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i")
DECLARE_HYDROGEN_ACCESSOR(BinaryOperation)

View File

@ -641,7 +641,7 @@ void MacroAssembler::Subu(Register rd, Register rs, const Operand& rt) {
void MacroAssembler::Mul(Register rd, Register rs, const Operand& rt) {
if (rt.is_reg()) {
if (kArchVariant == kLoongson) {
if (IsMipsArchVariant(kLoongson)) {
mult(rs, rt.rm());
mflo(rd);
} else {
@ -651,7 +651,7 @@ void MacroAssembler::Mul(Register rd, Register rs, const Operand& rt) {
// li handles the relocation.
DCHECK(!rs.is(at));
li(at, rt);
if (kArchVariant == kLoongson) {
if (IsMipsArchVariant(kLoongson)) {
mult(rs, at);
mflo(rd);
} else {
@ -661,6 +661,71 @@ void MacroAssembler::Mul(Register rd, Register rs, const Operand& rt) {
}
void MacroAssembler::Mul(Register rd_hi, Register rd_lo,
Register rs, const Operand& rt) {
if (rt.is_reg()) {
if (!IsMipsArchVariant(kMips32r6)) {
mult(rs, rt.rm());
mflo(rd_lo);
mfhi(rd_hi);
} else {
if (rd_lo.is(rs)) {
DCHECK(!rd_hi.is(rs));
DCHECK(!rd_hi.is(rt.rm()) && !rd_lo.is(rt.rm()));
muh(rd_hi, rs, rt.rm());
mul(rd_lo, rs, rt.rm());
} else {
DCHECK(!rd_hi.is(rt.rm()) && !rd_lo.is(rt.rm()));
mul(rd_lo, rs, rt.rm());
muh(rd_hi, rs, rt.rm());
}
}
} else {
// li handles the relocation.
DCHECK(!rs.is(at));
li(at, rt);
if (!IsMipsArchVariant(kMips32r6)) {
mult(rs, at);
mflo(rd_lo);
mfhi(rd_hi);
} else {
if (rd_lo.is(rs)) {
DCHECK(!rd_hi.is(rs));
DCHECK(!rd_hi.is(at) && !rd_lo.is(at));
muh(rd_hi, rs, at);
mul(rd_lo, rs, at);
} else {
DCHECK(!rd_hi.is(at) && !rd_lo.is(at));
mul(rd_lo, rs, at);
muh(rd_hi, rs, at);
}
}
}
}
void MacroAssembler::Mulh(Register rd, Register rs, const Operand& rt) {
if (rt.is_reg()) {
if (!IsMipsArchVariant(kMips32r6)) {
mult(rs, rt.rm());
mfhi(rd);
} else {
muh(rd, rs, rt.rm());
}
} else {
// li handles the relocation.
DCHECK(!rs.is(at));
li(at, rt);
if (!IsMipsArchVariant(kMips32r6)) {
mult(rs, at);
mfhi(rd);
} else {
muh(rd, rs, at);
}
}
}
void MacroAssembler::Mult(Register rs, const Operand& rt) {
if (rt.is_reg()) {
mult(rs, rt.rm());
@ -697,6 +762,55 @@ void MacroAssembler::Div(Register rs, const Operand& rt) {
}
void MacroAssembler::Div(Register rem, Register res,
Register rs, const Operand& rt) {
if (rt.is_reg()) {
if (!IsMipsArchVariant(kMips32r6)) {
div(rs, rt.rm());
mflo(res);
mfhi(rem);
} else {
div(res, rs, rt.rm());
mod(rem, rs, rt.rm());
}
} else {
// li handles the relocation.
DCHECK(!rs.is(at));
li(at, rt);
if (!IsMipsArchVariant(kMips32r6)) {
div(rs, at);
mflo(res);
mfhi(rem);
} else {
div(res, rs, at);
mod(rem, rs, at);
}
}
}
void MacroAssembler::Mod(Register rd, Register rs, const Operand& rt) {
if (rt.is_reg()) {
if (!IsMipsArchVariant(kMips32r6)) {
div(rs, rt.rm());
mfhi(rd);
} else {
mod(rd, rs, rt.rm());
}
} else {
// li handles the relocation.
DCHECK(!rs.is(at));
li(at, rt);
if (!IsMipsArchVariant(kMips32r6)) {
div(rs, at);
mfhi(rd);
} else {
mod(rd, rs, at);
}
}
}
void MacroAssembler::Divu(Register rs, const Operand& rt) {
if (rt.is_reg()) {
divu(rs, rt.rm());
@ -811,7 +925,7 @@ void MacroAssembler::Sltu(Register rd, Register rs, const Operand& rt) {
void MacroAssembler::Ror(Register rd, Register rs, const Operand& rt) {
if (kArchVariant == kMips32r2) {
if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) {
if (rt.is_reg()) {
rotrv(rd, rs, rt.rm());
} else {
@ -837,7 +951,7 @@ void MacroAssembler::Ror(Register rd, Register rs, const Operand& rt) {
void MacroAssembler::Pref(int32_t hint, const MemOperand& rs) {
if (kArchVariant == kLoongson) {
if (IsMipsArchVariant(kLoongson)) {
lw(zero_reg, rs);
} else {
pref(hint, rs);
@ -1033,7 +1147,7 @@ void MacroAssembler::Ext(Register rt,
DCHECK(pos < 32);
DCHECK(pos + size < 33);
if (kArchVariant == kMips32r2) {
if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) {
ext_(rt, rs, pos, size);
} else {
// Move rs to rt and shift it left then right to get the
@ -1057,7 +1171,7 @@ void MacroAssembler::Ins(Register rt,
DCHECK(pos + size <= 32);
DCHECK(size != 0);
if (kArchVariant == kMips32r2) {
if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) {
ins_(rt, rs, pos, size);
} else {
DCHECK(!rt.is(t8) && !rs.is(t8));
@ -1111,8 +1225,8 @@ void MacroAssembler::Cvt_d_uw(FPURegister fd,
// Load 2^31 into f20 as its float representation.
li(at, 0x41E00000);
mtc1(at, FPURegister::from_code(scratch.code() + 1));
mtc1(zero_reg, scratch);
Mthc1(at, scratch);
// Add it to fd.
add_d(fd, fd, scratch);
@ -1129,10 +1243,10 @@ void MacroAssembler::Trunc_uw_d(FPURegister fd,
void MacroAssembler::Trunc_w_d(FPURegister fd, FPURegister fs) {
if (kArchVariant == kLoongson && fd.is(fs)) {
mfc1(t8, FPURegister::from_code(fs.code() + 1));
if (IsMipsArchVariant(kLoongson) && fd.is(fs)) {
Mfhc1(t8, fs);
trunc_w_d(fd, fs);
mtc1(t8, FPURegister::from_code(fs.code() + 1));
Mthc1(t8, fs);
} else {
trunc_w_d(fd, fs);
}
@ -1140,10 +1254,10 @@ void MacroAssembler::Trunc_w_d(FPURegister fd, FPURegister fs) {
void MacroAssembler::Round_w_d(FPURegister fd, FPURegister fs) {
if (kArchVariant == kLoongson && fd.is(fs)) {
mfc1(t8, FPURegister::from_code(fs.code() + 1));
if (IsMipsArchVariant(kLoongson) && fd.is(fs)) {
Mfhc1(t8, fs);
round_w_d(fd, fs);
mtc1(t8, FPURegister::from_code(fs.code() + 1));
Mthc1(t8, fs);
} else {
round_w_d(fd, fs);
}
@ -1151,10 +1265,10 @@ void MacroAssembler::Round_w_d(FPURegister fd, FPURegister fs) {
void MacroAssembler::Floor_w_d(FPURegister fd, FPURegister fs) {
if (kArchVariant == kLoongson && fd.is(fs)) {
mfc1(t8, FPURegister::from_code(fs.code() + 1));
if (IsMipsArchVariant(kLoongson) && fd.is(fs)) {
Mfhc1(t8, fs);
floor_w_d(fd, fs);
mtc1(t8, FPURegister::from_code(fs.code() + 1));
Mthc1(t8, fs);
} else {
floor_w_d(fd, fs);
}
@ -1162,10 +1276,10 @@ void MacroAssembler::Floor_w_d(FPURegister fd, FPURegister fs) {
void MacroAssembler::Ceil_w_d(FPURegister fd, FPURegister fs) {
if (kArchVariant == kLoongson && fd.is(fs)) {
mfc1(t8, FPURegister::from_code(fs.code() + 1));
if (IsMipsArchVariant(kLoongson) && fd.is(fs)) {
Mfhc1(t8, fs);
ceil_w_d(fd, fs);
mtc1(t8, FPURegister::from_code(fs.code() + 1));
Mthc1(t8, fs);
} else {
ceil_w_d(fd, fs);
}
@ -1180,8 +1294,8 @@ void MacroAssembler::Trunc_uw_d(FPURegister fd,
// Load 2^31 into scratch as its float representation.
li(at, 0x41E00000);
mtc1(at, FPURegister::from_code(scratch.code() + 1));
mtc1(zero_reg, scratch);
Mthc1(at, scratch);
// Test if scratch > fd.
// If fd < 2^31 we can convert it normally.
Label simple_convert;
@ -1205,6 +1319,24 @@ void MacroAssembler::Trunc_uw_d(FPURegister fd,
}
void MacroAssembler::Mthc1(Register rt, FPURegister fs) {
if (IsFp64Mode()) {
mthc1(rt, fs);
} else {
mtc1(rt, fs.high());
}
}
void MacroAssembler::Mfhc1(Register rt, FPURegister fs) {
if (IsFp64Mode()) {
mfhc1(rt, fs);
} else {
mfc1(rt, fs.high());
}
}
void MacroAssembler::BranchF(Label* target,
Label* nan,
Condition cc,
@ -1220,49 +1352,103 @@ void MacroAssembler::BranchF(Label* target,
DCHECK(nan || target);
// Check for unordered (NaN) cases.
if (nan) {
c(UN, D, cmp1, cmp2);
bc1t(nan);
if (!IsMipsArchVariant(kMips32r6)) {
c(UN, D, cmp1, cmp2);
bc1t(nan);
} else {
// Use kDoubleCompareReg for comparison result. It has to be unavailable
// to lithium register allocator.
DCHECK(!cmp1.is(kDoubleCompareReg) && !cmp2.is(kDoubleCompareReg));
cmp(UN, L, kDoubleCompareReg, cmp1, cmp2);
bc1nez(nan, kDoubleCompareReg);
}
}
if (target) {
// Here NaN cases were either handled by this function or are assumed to
// have been handled by the caller.
// Unsigned conditions are treated as their signed counterpart.
switch (cc) {
case lt:
c(OLT, D, cmp1, cmp2);
bc1t(target);
break;
case gt:
c(ULE, D, cmp1, cmp2);
bc1f(target);
break;
case ge:
c(ULT, D, cmp1, cmp2);
bc1f(target);
break;
case le:
c(OLE, D, cmp1, cmp2);
bc1t(target);
break;
case eq:
c(EQ, D, cmp1, cmp2);
bc1t(target);
break;
case ueq:
c(UEQ, D, cmp1, cmp2);
bc1t(target);
break;
case ne:
c(EQ, D, cmp1, cmp2);
bc1f(target);
break;
case nue:
c(UEQ, D, cmp1, cmp2);
bc1f(target);
break;
default:
CHECK(0);
if (!IsMipsArchVariant(kMips32r6)) {
if (target) {
// Here NaN cases were either handled by this function or are assumed to
// have been handled by the caller.
switch (cc) {
case lt:
c(OLT, D, cmp1, cmp2);
bc1t(target);
break;
case gt:
c(ULE, D, cmp1, cmp2);
bc1f(target);
break;
case ge:
c(ULT, D, cmp1, cmp2);
bc1f(target);
break;
case le:
c(OLE, D, cmp1, cmp2);
bc1t(target);
break;
case eq:
c(EQ, D, cmp1, cmp2);
bc1t(target);
break;
case ueq:
c(UEQ, D, cmp1, cmp2);
bc1t(target);
break;
case ne:
c(EQ, D, cmp1, cmp2);
bc1f(target);
break;
case nue:
c(UEQ, D, cmp1, cmp2);
bc1f(target);
break;
default:
CHECK(0);
}
}
} else {
if (target) {
// Here NaN cases were either handled by this function or are assumed to
// have been handled by the caller.
// Unsigned conditions are treated as their signed counterpart.
// Use kDoubleCompareReg for comparison result, it is
// valid in fp64 (FR = 1) mode which is implied for mips32r6.
DCHECK(!cmp1.is(kDoubleCompareReg) && !cmp2.is(kDoubleCompareReg));
switch (cc) {
case lt:
cmp(OLT, L, kDoubleCompareReg, cmp1, cmp2);
bc1nez(target, kDoubleCompareReg);
break;
case gt:
cmp(ULE, L, kDoubleCompareReg, cmp1, cmp2);
bc1eqz(target, kDoubleCompareReg);
break;
case ge:
cmp(ULT, L, kDoubleCompareReg, cmp1, cmp2);
bc1eqz(target, kDoubleCompareReg);
break;
case le:
cmp(OLE, L, kDoubleCompareReg, cmp1, cmp2);
bc1nez(target, kDoubleCompareReg);
break;
case eq:
cmp(EQ, L, kDoubleCompareReg, cmp1, cmp2);
bc1nez(target, kDoubleCompareReg);
break;
case ueq:
cmp(UEQ, L, kDoubleCompareReg, cmp1, cmp2);
bc1nez(target, kDoubleCompareReg);
break;
case ne:
cmp(EQ, L, kDoubleCompareReg, cmp1, cmp2);
bc1eqz(target, kDoubleCompareReg);
break;
case nue:
cmp(UEQ, L, kDoubleCompareReg, cmp1, cmp2);
bc1eqz(target, kDoubleCompareReg);
break;
default:
CHECK(0);
}
}
}
@ -1297,16 +1483,16 @@ void MacroAssembler::Move(FPURegister dst, double imm) {
// register of FPU register pair.
if (hi != 0) {
li(at, Operand(hi));
mtc1(at, dst.high());
Mthc1(at, dst);
} else {
mtc1(zero_reg, dst.high());
Mthc1(zero_reg, dst);
}
}
}
void MacroAssembler::Movz(Register rd, Register rs, Register rt) {
if (kArchVariant == kLoongson) {
if (IsMipsArchVariant(kLoongson) || IsMipsArchVariant(kMips32r6)) {
Label done;
Branch(&done, ne, rt, Operand(zero_reg));
mov(rd, rs);
@ -1318,7 +1504,7 @@ void MacroAssembler::Movz(Register rd, Register rs, Register rt) {
void MacroAssembler::Movn(Register rd, Register rs, Register rt) {
if (kArchVariant == kLoongson) {
if (IsMipsArchVariant(kLoongson) || IsMipsArchVariant(kMips32r6)) {
Label done;
Branch(&done, eq, rt, Operand(zero_reg));
mov(rd, rs);
@ -1330,7 +1516,7 @@ void MacroAssembler::Movn(Register rd, Register rs, Register rt) {
void MacroAssembler::Movt(Register rd, Register rs, uint16_t cc) {
if (kArchVariant == kLoongson) {
if (IsMipsArchVariant(kLoongson)) {
// Tests an FP condition code and then conditionally move rs to rd.
// We do not currently use any FPU cc bit other than bit 0.
DCHECK(cc == 0);
@ -1356,7 +1542,7 @@ void MacroAssembler::Movt(Register rd, Register rs, uint16_t cc) {
void MacroAssembler::Movf(Register rd, Register rs, uint16_t cc) {
if (kArchVariant == kLoongson) {
if (IsMipsArchVariant(kLoongson)) {
// Tests an FP condition code and then conditionally move rs to rd.
// We do not currently use any FPU cc bit other than bit 0.
DCHECK(cc == 0);
@ -1382,7 +1568,7 @@ void MacroAssembler::Movf(Register rd, Register rs, uint16_t cc) {
void MacroAssembler::Clz(Register rd, Register rs) {
if (kArchVariant == kLoongson) {
if (IsMipsArchVariant(kLoongson)) {
DCHECK(!(rd.is(t8) || rd.is(t9)) && !(rs.is(t8) || rs.is(t9)));
Register mask = t8;
Register scratch = t9;
@ -2244,7 +2430,7 @@ void MacroAssembler::BranchAndLinkShort(int16_t offset, Condition cond,
li(r2, rt);
}
{
if (!IsMipsArchVariant(kMips32r6)) {
BlockTrampolinePoolScope block_trampoline_pool(this);
switch (cond) {
case cc_always:
@ -2308,7 +2494,88 @@ void MacroAssembler::BranchAndLinkShort(int16_t offset, Condition cond,
default:
UNREACHABLE();
}
} else {
BlockTrampolinePoolScope block_trampoline_pool(this);
switch (cond) {
case cc_always:
bal(offset);
break;
case eq:
bne(rs, r2, 2);
nop();
bal(offset);
break;
case ne:
beq(rs, r2, 2);
nop();
bal(offset);
break;
// Signed comparison.
case greater:
// rs > rt
slt(scratch, r2, rs);
beq(scratch, zero_reg, 2);
nop();
bal(offset);
break;
case greater_equal:
// rs >= rt
slt(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
bal(offset);
break;
case less:
// rs < r2
slt(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
bal(offset);
break;
case less_equal:
// rs <= r2
slt(scratch, r2, rs);
bne(scratch, zero_reg, 2);
nop();
bal(offset);
break;
// Unsigned comparison.
case Ugreater:
// rs > rt
sltu(scratch, r2, rs);
beq(scratch, zero_reg, 2);
nop();
bal(offset);
break;
case Ugreater_equal:
// rs >= rt
sltu(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
bal(offset);
break;
case Uless:
// rs < r2
sltu(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
bal(offset);
break;
case Uless_equal:
// rs <= r2
sltu(scratch, r2, rs);
bne(scratch, zero_reg, 2);
nop();
bal(offset);
break;
default:
UNREACHABLE();
}
}
// Emit a nop in the branch delay slot if required.
if (bdslot == PROTECT)
nop();
@ -2339,7 +2606,7 @@ void MacroAssembler::BranchAndLinkShort(Label* L, Condition cond, Register rs,
li(r2, rt);
}
{
if (!IsMipsArchVariant(kMips32r6)) {
BlockTrampolinePoolScope block_trampoline_pool(this);
switch (cond) {
case cc_always:
@ -2411,10 +2678,103 @@ void MacroAssembler::BranchAndLinkShort(Label* L, Condition cond, Register rs,
bltzal(scratch, offset);
break;
default:
UNREACHABLE();
}
} else {
BlockTrampolinePoolScope block_trampoline_pool(this);
switch (cond) {
case cc_always:
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case eq:
bne(rs, r2, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case ne:
beq(rs, r2, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
// Signed comparison.
case greater:
// rs > rt
slt(scratch, r2, rs);
beq(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case greater_equal:
// rs >= rt
slt(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case less:
// rs < r2
slt(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case less_equal:
// rs <= r2
slt(scratch, r2, rs);
bne(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
// Unsigned comparison.
case Ugreater:
// rs > rt
sltu(scratch, r2, rs);
beq(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case Ugreater_equal:
// rs >= rt
sltu(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case Uless:
// rs < r2
sltu(scratch, rs, r2);
bne(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
case Uless_equal:
// rs <= r2
sltu(scratch, r2, rs);
bne(scratch, zero_reg, 2);
nop();
offset = shifted_branch_offset(L, false);
bal(offset);
break;
default:
UNREACHABLE();
}
}
// Check that offset could actually hold on an int16_t.
DCHECK(is_int16(offset));
@ -5748,8 +6108,7 @@ void MacroAssembler::TruncatingDiv(Register result,
DCHECK(!result.is(at));
MultiplierAndShift ms(divisor);
li(at, Operand(ms.multiplier()));
Mult(dividend, Operand(at));
mfhi(result);
Mulh(result, dividend, Operand(at));
if (divisor > 0 && ms.multiplier() < 0) {
Addu(result, result, Operand(dividend));
}

View File

@ -234,11 +234,11 @@ class MacroAssembler: public Assembler {
inline void Move(Register dst_low, Register dst_high, FPURegister src) {
mfc1(dst_low, src);
mfc1(dst_high, FPURegister::from_code(src.code() + 1));
Mfhc1(dst_high, src);
}
inline void FmoveHigh(Register dst_high, FPURegister src) {
mfc1(dst_high, FPURegister::from_code(src.code() + 1));
Mfhc1(dst_high, src);
}
inline void FmoveLow(Register dst_low, FPURegister src) {
@ -247,7 +247,7 @@ class MacroAssembler: public Assembler {
inline void Move(FPURegister dst, Register src_low, Register src_high) {
mtc1(src_low, dst);
mtc1(src_high, FPURegister::from_code(dst.code() + 1));
Mthc1(src_high, dst);
}
// Conditional move.
@ -582,14 +582,28 @@ class MacroAssembler: public Assembler {
instr(rs, Operand(j)); \
}
#define DEFINE_INSTRUCTION3(instr) \
void instr(Register rd_hi, Register rd_lo, Register rs, const Operand& rt); \
void instr(Register rd_hi, Register rd_lo, Register rs, Register rt) { \
instr(rd_hi, rd_lo, rs, Operand(rt)); \
} \
void instr(Register rd_hi, Register rd_lo, Register rs, int32_t j) { \
instr(rd_hi, rd_lo, rs, Operand(j)); \
}
DEFINE_INSTRUCTION(Addu);
DEFINE_INSTRUCTION(Subu);
DEFINE_INSTRUCTION(Mul);
DEFINE_INSTRUCTION(Mod);
DEFINE_INSTRUCTION(Mulh);
DEFINE_INSTRUCTION2(Mult);
DEFINE_INSTRUCTION2(Multu);
DEFINE_INSTRUCTION2(Div);
DEFINE_INSTRUCTION2(Divu);
DEFINE_INSTRUCTION3(Div);
DEFINE_INSTRUCTION3(Mul);
DEFINE_INSTRUCTION(And);
DEFINE_INSTRUCTION(Or);
DEFINE_INSTRUCTION(Xor);
@ -742,6 +756,20 @@ class MacroAssembler: public Assembler {
void Round_w_d(FPURegister fd, FPURegister fs);
void Floor_w_d(FPURegister fd, FPURegister fs);
void Ceil_w_d(FPURegister fd, FPURegister fs);
// FP32 mode: Move the general purpose register into
// the high part of the double-register pair.
// FP64 mode: Move the general-purpose register into
// the higher 32 bits of the 64-bit coprocessor register,
// while leaving the low bits unchanged.
void Mthc1(Register rt, FPURegister fs);
// FP32 mode: move the high part of the double-register pair into
// general purpose register.
// FP64 mode: Move the higher 32 bits of the 64-bit coprocessor register into
// general-purpose register.
void Mfhc1(Register rt, FPURegister fs);
// Wrapper function for the different cmp/branch types.
void BranchF(Label* target,
Label* nan,

View File

@ -67,11 +67,12 @@ class MipsDebugger {
Simulator* sim_;
int32_t GetRegisterValue(int regnum);
int32_t GetFPURegisterValueInt(int regnum);
int64_t GetFPURegisterValueLong(int regnum);
int32_t GetFPURegisterValue32(int regnum);
int64_t GetFPURegisterValue64(int regnum);
float GetFPURegisterValueFloat(int regnum);
double GetFPURegisterValueDouble(int regnum);
bool GetValue(const char* desc, int32_t* value);
bool GetValue(const char* desc, int64_t* value);
// Set or delete a breakpoint. Returns true if successful.
bool SetBreakpoint(Instruction* breakpc);
@ -160,20 +161,20 @@ int32_t MipsDebugger::GetRegisterValue(int regnum) {
}
int32_t MipsDebugger::GetFPURegisterValueInt(int regnum) {
int32_t MipsDebugger::GetFPURegisterValue32(int regnum) {
if (regnum == kNumFPURegisters) {
return sim_->get_pc();
} else {
return sim_->get_fpu_register(regnum);
return sim_->get_fpu_register_word(regnum);
}
}
int64_t MipsDebugger::GetFPURegisterValueLong(int regnum) {
int64_t MipsDebugger::GetFPURegisterValue64(int regnum) {
if (regnum == kNumFPURegisters) {
return sim_->get_pc();
} else {
return sim_->get_fpu_register_long(regnum);
return sim_->get_fpu_register(regnum);
}
}
@ -204,7 +205,7 @@ bool MipsDebugger::GetValue(const char* desc, int32_t* value) {
*value = GetRegisterValue(regnum);
return true;
} else if (fpuregnum != kInvalidFPURegister) {
*value = GetFPURegisterValueInt(fpuregnum);
*value = GetFPURegisterValue32(fpuregnum);
return true;
} else if (strncmp(desc, "0x", 2) == 0) {
return SScanF(desc, "%x", reinterpret_cast<uint32_t*>(value)) == 1;
@ -215,6 +216,26 @@ bool MipsDebugger::GetValue(const char* desc, int32_t* value) {
}
bool MipsDebugger::GetValue(const char* desc, int64_t* value) {
int regnum = Registers::Number(desc);
int fpuregnum = FPURegisters::Number(desc);
if (regnum != kInvalidRegister) {
*value = GetRegisterValue(regnum);
return true;
} else if (fpuregnum != kInvalidFPURegister) {
*value = GetFPURegisterValue64(fpuregnum);
return true;
} else if (strncmp(desc, "0x", 2) == 0) {
return SScanF(desc + 2, "%" SCNx64,
reinterpret_cast<uint64_t*>(value)) == 1;
} else {
return SScanF(desc, "%" SCNu64, reinterpret_cast<uint64_t*>(value)) == 1;
}
return false;
}
bool MipsDebugger::SetBreakpoint(Instruction* breakpc) {
// Check if a breakpoint can be set. If not return without any side-effects.
if (sim_->break_pc_ != NULL) {
@ -295,34 +316,76 @@ void MipsDebugger::PrintAllRegs() {
void MipsDebugger::PrintAllRegsIncludingFPU() {
#define FPU_REG_INFO(n) FPURegisters::Name(n), FPURegisters::Name(n+1), \
GetFPURegisterValueInt(n+1), \
GetFPURegisterValueInt(n), \
GetFPURegisterValueDouble(n)
#define FPU_REG_INFO32(n) FPURegisters::Name(n), FPURegisters::Name(n+1), \
GetFPURegisterValue32(n+1), \
GetFPURegisterValue32(n), \
GetFPURegisterValueDouble(n)
#define FPU_REG_INFO64(n) FPURegisters::Name(n), \
GetFPURegisterValue64(n), \
GetFPURegisterValueDouble(n)
PrintAllRegs();
PrintF("\n\n");
// f0, f1, f2, ... f31.
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(0) );
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(2) );
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(4) );
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(6) );
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(8) );
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(10));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(12));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(14));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(16));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(18));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(20));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(22));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(24));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(26));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(28));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(30));
// This must be a compile-time switch,
// compiler will throw out warnings otherwise.
if (kFpuMode == kFP64) {
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(0) );
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(1) );
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(2) );
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(3) );
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(4) );
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(5) );
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(6) );
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(7) );
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(8) );
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(9) );
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(10));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(11));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(12));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(13));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(14));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(15));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(16));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(17));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(18));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(19));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(20));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(21));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(22));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(23));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(24));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(25));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(26));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(27));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(28));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(29));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(30));
PrintF("%3s: 0x%016llx %16.4e\n", FPU_REG_INFO64(31));
} else {
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(0) );
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(2) );
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(4) );
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(6) );
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(8) );
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(10));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(12));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(14));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(16));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(18));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(20));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(22));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(24));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(26));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(28));
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO32(30));
}
#undef REG_INFO
#undef FPU_REG_INFO
#undef FPU_REG_INFO32
#undef FPU_REG_INFO64
}
@ -397,8 +460,6 @@ void MipsDebugger::Debug() {
done = true;
} else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
if (argc == 2) {
int32_t value;
float fvalue;
if (strcmp(arg1, "all") == 0) {
PrintAllRegs();
} else if (strcmp(arg1, "allf") == 0) {
@ -408,24 +469,36 @@ void MipsDebugger::Debug() {
int fpuregnum = FPURegisters::Number(arg1);
if (regnum != kInvalidRegister) {
int32_t value;
value = GetRegisterValue(regnum);
PrintF("%s: 0x%08x %d \n", arg1, value, value);
} else if (fpuregnum != kInvalidFPURegister) {
if (fpuregnum % 2 == 1) {
value = GetFPURegisterValueInt(fpuregnum);
fvalue = GetFPURegisterValueFloat(fpuregnum);
PrintF("%s: 0x%08x %11.4e\n", arg1, value, fvalue);
if (IsFp64Mode()) {
int64_t value;
double dvalue;
value = GetFPURegisterValue64(fpuregnum);
dvalue = GetFPURegisterValueDouble(fpuregnum);
PrintF("%3s: 0x%016llx %16.4e\n",
FPURegisters::Name(fpuregnum), value, dvalue);
} else {
double dfvalue;
int32_t lvalue1 = GetFPURegisterValueInt(fpuregnum);
int32_t lvalue2 = GetFPURegisterValueInt(fpuregnum + 1);
dfvalue = GetFPURegisterValueDouble(fpuregnum);
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n",
FPURegisters::Name(fpuregnum+1),
FPURegisters::Name(fpuregnum),
lvalue1,
lvalue2,
dfvalue);
if (fpuregnum % 2 == 1) {
int32_t value;
float fvalue;
value = GetFPURegisterValue32(fpuregnum);
fvalue = GetFPURegisterValueFloat(fpuregnum);
PrintF("%s: 0x%08x %11.4e\n", arg1, value, fvalue);
} else {
double dfvalue;
int32_t lvalue1 = GetFPURegisterValue32(fpuregnum);
int32_t lvalue2 = GetFPURegisterValue32(fpuregnum + 1);
dfvalue = GetFPURegisterValueDouble(fpuregnum);
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n",
FPURegisters::Name(fpuregnum+1),
FPURegisters::Name(fpuregnum),
lvalue1,
lvalue2,
dfvalue);
}
}
} else {
PrintF("%s unrecognized\n", arg1);
@ -439,7 +512,7 @@ void MipsDebugger::Debug() {
int fpuregnum = FPURegisters::Number(arg1);
if (fpuregnum != kInvalidFPURegister) {
value = GetFPURegisterValueInt(fpuregnum);
value = GetFPURegisterValue32(fpuregnum);
fvalue = GetFPURegisterValueFloat(fpuregnum);
PrintF("%s: 0x%08x %11.4e\n", arg1, value, fvalue);
} else {
@ -489,15 +562,28 @@ void MipsDebugger::Debug() {
next_arg++;
}
int32_t words;
if (argc == next_arg) {
words = 10;
} else {
if (!GetValue(argv[next_arg], &words)) {
// TODO(palfia): optimize this.
if (IsFp64Mode()) {
int64_t words;
if (argc == next_arg) {
words = 10;
} else {
if (!GetValue(argv[next_arg], &words)) {
words = 10;
}
}
end = cur + words;
} else {
int32_t words;
if (argc == next_arg) {
words = 10;
} else {
if (!GetValue(argv[next_arg], &words)) {
words = 10;
}
}
end = cur + words;
}
end = cur + words;
while (cur < end) {
PrintF(" 0x%08x: 0x%08x %10d",
@ -1012,12 +1098,31 @@ void Simulator::set_dw_register(int reg, const int* dbl) {
}
void Simulator::set_fpu_register(int fpureg, int32_t value) {
void Simulator::set_fpu_register(int fpureg, int64_t value) {
DCHECK(IsFp64Mode());
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters));
FPUregisters_[fpureg] = value;
}
void Simulator::set_fpu_register_word(int fpureg, int32_t value) {
// Set ONLY lower 32-bits, leaving upper bits untouched.
// TODO(plind): big endian issue.
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters));
int32_t *pword = reinterpret_cast<int32_t*>(&FPUregisters_[fpureg]);
*pword = value;
}
void Simulator::set_fpu_register_hi_word(int fpureg, int32_t value) {
// Set ONLY upper 32-bits, leaving lower bits untouched.
// TODO(plind): big endian issue.
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters));
int32_t *phiword = (reinterpret_cast<int32_t*>(&FPUregisters_[fpureg])) + 1;
*phiword = value;
}
void Simulator::set_fpu_register_float(int fpureg, float value) {
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters));
*BitCast<float*>(&FPUregisters_[fpureg]) = value;
@ -1025,8 +1130,15 @@ void Simulator::set_fpu_register_float(int fpureg, float value) {
void Simulator::set_fpu_register_double(int fpureg, double value) {
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters) && ((fpureg % 2) == 0));
*BitCast<double*>(&FPUregisters_[fpureg]) = value;
if (IsFp64Mode()) {
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters));
*BitCast<double*>(&FPUregisters_[fpureg]) = value;
} else {
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters) && ((fpureg % 2) == 0));
int64_t i64 = BitCast<int64_t>(value);
set_fpu_register_word(fpureg, i64 & 0xffffffff);
set_fpu_register_word(fpureg + 1, i64 >> 32);
}
}
@ -1042,6 +1154,7 @@ int32_t Simulator::get_register(int reg) const {
double Simulator::get_double_from_register_pair(int reg) {
// TODO(plind): bad ABI stuff, refactor or remove.
DCHECK((reg >= 0) && (reg < kNumSimuRegisters) && ((reg % 2) == 0));
double dm_val = 0.0;
@ -1054,29 +1167,49 @@ double Simulator::get_double_from_register_pair(int reg) {
}
int32_t Simulator::get_fpu_register(int fpureg) const {
int64_t Simulator::get_fpu_register(int fpureg) const {
DCHECK(IsFp64Mode());
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters));
return FPUregisters_[fpureg];
}
int64_t Simulator::get_fpu_register_long(int fpureg) const {
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters) && ((fpureg % 2) == 0));
return *BitCast<int64_t*>(
const_cast<int32_t*>(&FPUregisters_[fpureg]));
int32_t Simulator::get_fpu_register_word(int fpureg) const {
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters));
return static_cast<int32_t>(FPUregisters_[fpureg] & 0xffffffff);
}
int32_t Simulator::get_fpu_register_signed_word(int fpureg) const {
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters));
return static_cast<int32_t>(FPUregisters_[fpureg] & 0xffffffff);
}
int32_t Simulator::get_fpu_register_hi_word(int fpureg) const {
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters));
return static_cast<int32_t>((FPUregisters_[fpureg] >> 32) & 0xffffffff);
}
float Simulator::get_fpu_register_float(int fpureg) const {
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters));
return *BitCast<float*>(
const_cast<int32_t*>(&FPUregisters_[fpureg]));
const_cast<int64_t*>(&FPUregisters_[fpureg]));
}
double Simulator::get_fpu_register_double(int fpureg) const {
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters) && ((fpureg % 2) == 0));
return *BitCast<double*>(const_cast<int32_t*>(&FPUregisters_[fpureg]));
if (IsFp64Mode()) {
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters));
return *BitCast<double*>(&FPUregisters_[fpureg]);
} else {
DCHECK((fpureg >= 0) && (fpureg < kNumFPURegisters) && ((fpureg % 2) == 0));
int64_t i64;
i64 = static_cast<uint32_t>(get_fpu_register_word(fpureg));
i64 |= static_cast<uint64_t>(get_fpu_register_word(fpureg + 1)) << 32;
return BitCast<double>(i64);
}
}
@ -1089,6 +1222,7 @@ void Simulator::GetFpArgs(double* x, double* y, int32_t* z) {
*y = get_fpu_register_double(14);
*z = get_register(a2);
} else {
// TODO(plind): bad ABI stuff, refactor or remove.
// We use a char buffer to get around the strict-aliasing rules which
// otherwise allow the compiler to optimize away the copy.
char buffer[sizeof(*x)];
@ -1143,6 +1277,8 @@ bool Simulator::test_fcsr_bit(uint32_t cc) {
// Returns true if the operation was invalid.
bool Simulator::set_fcsr_round_error(double original, double rounded) {
bool ret = false;
double max_int32 = std::numeric_limits<int32_t>::max();
double min_int32 = std::numeric_limits<int32_t>::min();
if (!std::isfinite(original) || !std::isfinite(rounded)) {
set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
@ -1158,7 +1294,7 @@ bool Simulator::set_fcsr_round_error(double original, double rounded) {
ret = true;
}
if (rounded > INT_MAX || rounded < INT_MIN) {
if (rounded > max_int32 || rounded < min_int32) {
set_fcsr_bit(kFCSROverflowFlagBit, true);
// The reference is not really clear but it seems this is required:
set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
@ -1420,18 +1556,35 @@ void Simulator::SoftwareInterrupt(Instruction* instr) {
switch (redirection->type()) {
case ExternalReference::BUILTIN_FP_FP_CALL:
case ExternalReference::BUILTIN_COMPARE_CALL:
arg0 = get_fpu_register(f12);
arg1 = get_fpu_register(f13);
arg2 = get_fpu_register(f14);
arg3 = get_fpu_register(f15);
if (IsFp64Mode()) {
arg0 = get_fpu_register_word(f12);
arg1 = get_fpu_register_hi_word(f12);
arg2 = get_fpu_register_word(f14);
arg3 = get_fpu_register_hi_word(f14);
} else {
arg0 = get_fpu_register_word(f12);
arg1 = get_fpu_register_word(f13);
arg2 = get_fpu_register_word(f14);
arg3 = get_fpu_register_word(f15);
}
break;
case ExternalReference::BUILTIN_FP_CALL:
arg0 = get_fpu_register(f12);
arg1 = get_fpu_register(f13);
if (IsFp64Mode()) {
arg0 = get_fpu_register_word(f12);
arg1 = get_fpu_register_hi_word(f12);
} else {
arg0 = get_fpu_register_word(f12);
arg1 = get_fpu_register_word(f13);
}
break;
case ExternalReference::BUILTIN_FP_INT_CALL:
arg0 = get_fpu_register(f12);
arg1 = get_fpu_register(f13);
if (IsFp64Mode()) {
arg0 = get_fpu_register_word(f12);
arg1 = get_fpu_register_hi_word(f12);
} else {
arg0 = get_fpu_register_word(f12);
arg1 = get_fpu_register_word(f13);
}
arg2 = get_register(a2);
break;
default:
@ -1735,25 +1888,20 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
switch (op) {
case COP1: // Coprocessor instructions.
switch (instr->RsFieldRaw()) {
case BC1: // Handled in DecodeTypeImmed, should never come here.
UNREACHABLE();
break;
case CFC1:
// At the moment only FCSR is supported.
DCHECK(fs_reg == kFCSRRegister);
*alu_out = FCSR_;
break;
case MFC1:
*alu_out = get_fpu_register(fs_reg);
*alu_out = get_fpu_register_word(fs_reg);
break;
case MFHC1:
UNIMPLEMENTED_MIPS();
*alu_out = get_fpu_register_hi_word(fs_reg);
break;
case CTC1:
case MTC1:
case MTHC1:
// Do the store in the execution step.
break;
case S:
case D:
case W:
@ -1762,7 +1910,8 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
// Do everything in the execution step.
break;
default:
UNIMPLEMENTED_MIPS();
// BC1 BC1EQZ BC1NEZ handled in DecodeTypeImmed, should never come here.
UNREACHABLE();
}
break;
case COP1X:
@ -1810,17 +1959,53 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
case SRAV:
*alu_out = rt >> rs;
break;
case MFHI:
*alu_out = get_register(HI);
case MFHI: // MFHI == CLZ on R6.
if (!IsMipsArchVariant(kMips32r6)) {
DCHECK(instr->SaValue() == 0);
*alu_out = get_register(HI);
} else {
// MIPS spec: If no bits were set in GPR rs, the result written to
// GPR rd is 32.
// GCC __builtin_clz: If input is 0, the result is undefined.
DCHECK(instr->SaValue() == 1);
*alu_out =
rs_u == 0 ? 32 : CompilerIntrinsics::CountLeadingZeros(rs_u);
}
break;
case MFLO:
*alu_out = get_register(LO);
break;
case MULT:
*i64hilo = static_cast<int64_t>(rs) * static_cast<int64_t>(rt);
case MULT: // MULT == MUL_MUH.
if (!IsMipsArchVariant(kMips32r6)) {
*i64hilo = static_cast<int64_t>(rs) * static_cast<int64_t>(rt);
} else {
switch (instr->SaValue()) {
case MUL_OP:
case MUH_OP:
*i64hilo = static_cast<int64_t>(rs) * static_cast<int64_t>(rt);
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
}
break;
case MULTU:
*u64hilo = static_cast<uint64_t>(rs_u) * static_cast<uint64_t>(rt_u);
case MULTU: // MULTU == MUL_MUH_U.
if (!IsMipsArchVariant(kMips32r6)) {
*u64hilo = static_cast<uint64_t>(rs_u) *
static_cast<uint64_t>(rt_u);
} else {
switch (instr->SaValue()) {
case MUL_OP:
case MUH_OP:
*u64hilo = static_cast<uint64_t>(rs_u) *
static_cast<uint64_t>(rt_u);
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
}
break;
case ADD:
if (HaveSameSign(rs, rt)) {
@ -1998,16 +2183,14 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
switch (op) {
case COP1:
switch (instr->RsFieldRaw()) {
case BC1: // Branch on coprocessor condition.
UNREACHABLE();
break;
case CFC1:
set_register(rt_reg, alu_out);
break;
case MFC1:
set_register(rt_reg, alu_out);
break;
case MFHC1:
UNIMPLEMENTED_MIPS();
set_register(rt_reg, alu_out);
break;
case CTC1:
// At the moment only FCSR is supported.
@ -2015,10 +2198,12 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
FCSR_ = registers_[rt_reg];
break;
case MTC1:
FPUregisters_[fs_reg] = registers_[rt_reg];
// Hardware writes upper 32-bits to zero on mtc1.
set_fpu_register_hi_word(fs_reg, 0);
set_fpu_register_word(fs_reg, registers_[rt_reg]);
break;
case MTHC1:
UNIMPLEMENTED_MIPS();
set_fpu_register_hi_word(fs_reg, registers_[rt_reg]);
break;
case S:
float f;
@ -2027,20 +2212,9 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
f = get_fpu_register_float(fs_reg);
set_fpu_register_double(fd_reg, static_cast<double>(f));
break;
case CVT_W_S:
case CVT_L_S:
case TRUNC_W_S:
case TRUNC_L_S:
case ROUND_W_S:
case ROUND_L_S:
case FLOOR_W_S:
case FLOOR_L_S:
case CEIL_W_S:
case CEIL_L_S:
case CVT_PS_S:
UNIMPLEMENTED_MIPS();
break;
default:
// CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S
// CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented.
UNREACHABLE();
}
break;
@ -2114,7 +2288,7 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
// round to the even one.
result--;
}
set_fpu_register(fd_reg, result);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPUInvalidResult);
}
@ -2124,7 +2298,7 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
{
double rounded = trunc(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register(fd_reg, result);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPUInvalidResult);
}
@ -2134,7 +2308,7 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
{
double rounded = std::floor(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register(fd_reg, result);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPUInvalidResult);
}
@ -2144,7 +2318,7 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
{
double rounded = std::ceil(fs);
int32_t result = static_cast<int32_t>(rounded);
set_fpu_register(fd_reg, result);
set_fpu_register_word(fd_reg, result);
if (set_fcsr_round_error(fs, rounded)) {
set_fpu_register(fd_reg, kFPUInvalidResult);
}
@ -2156,34 +2330,54 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
case CVT_L_D: { // Mips32r2: Truncate double to 64-bit long-word.
double rounded = trunc(fs);
i64 = static_cast<int64_t>(rounded);
set_fpu_register(fd_reg, i64 & 0xffffffff);
set_fpu_register(fd_reg + 1, i64 >> 32);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
}
break;
}
case TRUNC_L_D: { // Mips32r2 instruction.
double rounded = trunc(fs);
i64 = static_cast<int64_t>(rounded);
set_fpu_register(fd_reg, i64 & 0xffffffff);
set_fpu_register(fd_reg + 1, i64 >> 32);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
}
break;
}
case ROUND_L_D: { // Mips32r2 instruction.
double rounded =
fs > 0 ? std::floor(fs + 0.5) : std::ceil(fs - 0.5);
i64 = static_cast<int64_t>(rounded);
set_fpu_register(fd_reg, i64 & 0xffffffff);
set_fpu_register(fd_reg + 1, i64 >> 32);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
}
break;
}
case FLOOR_L_D: // Mips32r2 instruction.
i64 = static_cast<int64_t>(std::floor(fs));
set_fpu_register(fd_reg, i64 & 0xffffffff);
set_fpu_register(fd_reg + 1, i64 >> 32);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
}
break;
case CEIL_L_D: // Mips32r2 instruction.
i64 = static_cast<int64_t>(std::ceil(fs));
set_fpu_register(fd_reg, i64 & 0xffffffff);
set_fpu_register(fd_reg + 1, i64 >> 32);
if (IsFp64Mode()) {
set_fpu_register(fd_reg, i64);
} else {
set_fpu_register_word(fd_reg, i64 & 0xffffffff);
set_fpu_register_word(fd_reg + 1, i64 >> 32);
}
break;
case C_F_D:
UNIMPLEMENTED_MIPS();
@ -2195,35 +2389,92 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
case W:
switch (instr->FunctionFieldRaw()) {
case CVT_S_W: // Convert word to float (single).
alu_out = get_fpu_register(fs_reg);
alu_out = get_fpu_register_signed_word(fs_reg);
set_fpu_register_float(fd_reg, static_cast<float>(alu_out));
break;
case CVT_D_W: // Convert word to double.
alu_out = get_fpu_register(fs_reg);
alu_out = get_fpu_register_signed_word(fs_reg);
set_fpu_register_double(fd_reg, static_cast<double>(alu_out));
break;
default:
default: // Mips64r6 CMP.S instructions unimplemented.
UNREACHABLE();
}
break;
case L:
fs = get_fpu_register_double(fs_reg);
ft = get_fpu_register_double(ft_reg);
switch (instr->FunctionFieldRaw()) {
case CVT_D_L: // Mips32r2 instruction.
// Watch the signs here, we want 2 32-bit vals
// to make a sign-64.
i64 = static_cast<uint32_t>(get_fpu_register(fs_reg));
i64 |= static_cast<int64_t>(get_fpu_register(fs_reg + 1)) << 32;
if (IsFp64Mode()) {
i64 = get_fpu_register(fs_reg);
} else {
i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg));
i64 |= static_cast<int64_t>(
get_fpu_register_word(fs_reg + 1)) << 32;
}
set_fpu_register_double(fd_reg, static_cast<double>(i64));
break;
case CVT_S_L:
UNIMPLEMENTED_MIPS();
break;
default:
case CMP_AF: // Mips64r6 CMP.D instructions.
UNIMPLEMENTED_MIPS();
break;
case CMP_UN:
if (std::isnan(fs) || std::isnan(ft)) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_EQ:
if (fs == ft) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_UEQ:
if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_LT:
if (fs < ft) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_ULT:
if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_LE:
if (fs <= ft) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
case CMP_ULE:
if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) {
set_fpu_register(fd_reg, -1);
} else {
set_fpu_register(fd_reg, 0);
}
break;
default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED.
UNREACHABLE();
}
break;
case PS:
break;
default:
UNREACHABLE();
}
@ -2263,30 +2514,100 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
}
// Instructions using HI and LO registers.
case MULT:
set_register(LO, static_cast<int32_t>(i64hilo & 0xffffffff));
set_register(HI, static_cast<int32_t>(i64hilo >> 32));
if (!IsMipsArchVariant(kMips32r6)) {
set_register(LO, static_cast<int32_t>(i64hilo & 0xffffffff));
set_register(HI, static_cast<int32_t>(i64hilo >> 32));
} else {
switch (instr->SaValue()) {
case MUL_OP:
set_register(rd_reg,
static_cast<int32_t>(i64hilo & 0xffffffff));
break;
case MUH_OP:
set_register(rd_reg, static_cast<int32_t>(i64hilo >> 32));
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
}
break;
case MULTU:
set_register(LO, static_cast<int32_t>(u64hilo & 0xffffffff));
set_register(HI, static_cast<int32_t>(u64hilo >> 32));
if (!IsMipsArchVariant(kMips32r6)) {
set_register(LO, static_cast<int32_t>(u64hilo & 0xffffffff));
set_register(HI, static_cast<int32_t>(u64hilo >> 32));
} else {
switch (instr->SaValue()) {
case MUL_OP:
set_register(rd_reg,
static_cast<int32_t>(u64hilo & 0xffffffff));
break;
case MUH_OP:
set_register(rd_reg, static_cast<int32_t>(u64hilo >> 32));
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
}
break;
case DIV:
// Divide by zero and overflow was not checked in the configuration
// step - div and divu do not raise exceptions. On division by 0
// the result will be UNPREDICTABLE. On overflow (INT_MIN/-1),
// return INT_MIN which is what the hardware does.
if (rs == INT_MIN && rt == -1) {
set_register(LO, INT_MIN);
set_register(HI, 0);
} else if (rt != 0) {
set_register(LO, rs / rt);
set_register(HI, rs % rt);
if (IsMipsArchVariant(kMips32r6)) {
switch (instr->SaValue()) {
case DIV_OP:
if (rs == INT_MIN && rt == -1) {
set_register(rd_reg, INT_MIN);
} else if (rt != 0) {
set_register(rd_reg, rs / rt);
}
break;
case MOD_OP:
if (rs == INT_MIN && rt == -1) {
set_register(rd_reg, 0);
} else if (rt != 0) {
set_register(rd_reg, rs % rt);
}
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
} else {
// Divide by zero and overflow was not checked in the
// configuration step - div and divu do not raise exceptions. On
// division by 0 the result will be UNPREDICTABLE. On overflow
// (INT_MIN/-1), return INT_MIN which is what the hardware does.
if (rs == INT_MIN && rt == -1) {
set_register(LO, INT_MIN);
set_register(HI, 0);
} else if (rt != 0) {
set_register(LO, rs / rt);
set_register(HI, rs % rt);
}
}
break;
case DIVU:
if (rt_u != 0) {
set_register(LO, rs_u / rt_u);
set_register(HI, rs_u % rt_u);
if (IsMipsArchVariant(kMips32r6)) {
switch (instr->SaValue()) {
case DIV_OP:
if (rt_u != 0) {
set_register(rd_reg, rs_u / rt_u);
}
break;
case MOD_OP:
if (rt_u != 0) {
set_register(rd_reg, rs_u % rt_u);
}
break;
default:
UNIMPLEMENTED_MIPS();
break;
}
} else {
if (rt_u != 0) {
set_register(LO, rs_u / rt_u);
set_register(HI, rs_u % rt_u);
}
}
break;
// Break and trap instructions.
@ -2368,6 +2689,7 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
int16_t imm16 = instr->Imm16Value();
int32_t ft_reg = instr->FtValue(); // Destination register.
int64_t ft;
// Zero extended immediate.
uint32_t oe_imm16 = 0xffff & imm16;
@ -2412,6 +2734,28 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
next_pc = current_pc + kBranchReturnOffset;
}
break;
case BC1EQZ:
ft = get_fpu_register(ft_reg);
do_branch = (ft & 0x1) ? false : true;
execute_branch_delay_instruction = true;
// Set next_pc.
if (do_branch) {
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
} else {
next_pc = current_pc + kBranchReturnOffset;
}
break;
case BC1NEZ:
ft = get_fpu_register(ft_reg);
do_branch = (ft & 0x1) ? true : false;
execute_branch_delay_instruction = true;
// Set next_pc.
if (do_branch) {
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
} else {
next_pc = current_pc + kBranchReturnOffset;
}
break;
default:
UNREACHABLE();
}
@ -2646,14 +2990,15 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
WriteW(addr, mem_value, instr);
break;
case LWC1:
set_fpu_register(ft_reg, alu_out);
set_fpu_register_hi_word(ft_reg, 0);
set_fpu_register_word(ft_reg, alu_out);
break;
case LDC1:
set_fpu_register_double(ft_reg, fp_out);
break;
case SWC1:
addr = rs + se_imm16;
WriteW(addr, get_fpu_register(ft_reg), instr);
WriteW(addr, get_fpu_register_word(ft_reg), instr);
break;
case SDC1:
addr = rs + se_imm16;

View File

@ -162,11 +162,15 @@ class Simulator {
int32_t get_register(int reg) const;
double get_double_from_register_pair(int reg);
// Same for FPURegisters.
void set_fpu_register(int fpureg, int32_t value);
void set_fpu_register(int fpureg, int64_t value);
void set_fpu_register_word(int fpureg, int32_t value);
void set_fpu_register_hi_word(int fpureg, int32_t value);
void set_fpu_register_float(int fpureg, float value);
void set_fpu_register_double(int fpureg, double value);
int32_t get_fpu_register(int fpureg) const;
int64_t get_fpu_register_long(int fpureg) const;
int64_t get_fpu_register(int fpureg) const;
int32_t get_fpu_register_word(int fpureg) const;
int32_t get_fpu_register_signed_word(int fpureg) const;
int32_t get_fpu_register_hi_word(int fpureg) const;
float get_fpu_register_float(int fpureg) const;
double get_fpu_register_double(int fpureg) const;
void set_fcsr_bit(uint32_t cc, bool value);
@ -338,7 +342,9 @@ class Simulator {
// Registers.
int32_t registers_[kNumSimuRegisters];
// Coprocessor Registers.
int32_t FPUregisters_[kNumFPURegisters];
// Note: FP32 mode uses only the lower 32-bit part of each element,
// the upper 32-bit is unpredictable.
int64_t FPUregisters_[kNumFPURegisters];
// FPU control register.
uint32_t FCSR_;

View File

@ -170,7 +170,7 @@ TEST(MIPS2) {
__ Branch(&error, ne, v0, Operand(0x1));
__ nop();
__ sltu(v0, t7, t3);
__ Branch(&error, ne, v0, Operand(0x0));
__ Branch(&error, ne, v0, Operand(zero_reg));
__ nop();
// End of SPECIAL class.
@ -185,7 +185,7 @@ TEST(MIPS2) {
__ slti(v0, t1, 0x00002000); // 0x1
__ slti(v0, v0, 0xffff8000); // 0x0
__ Branch(&error, ne, v0, Operand(0x0));
__ Branch(&error, ne, v0, Operand(zero_reg));
__ nop();
__ sltiu(v0, t1, 0x00002000); // 0x1
__ sltiu(v0, v0, 0x00008000); // 0x1
@ -293,7 +293,7 @@ TEST(MIPS3) {
__ sdc1(f14, MemOperand(a0, OFFSET_OF(T, g)) );
// g = sqrt(f) = 10.97451593465515908537
if (kArchVariant == kMips32r2) {
if (IsMipsArchVariant(kMips32r2)) {
__ ldc1(f4, MemOperand(a0, OFFSET_OF(T, h)) );
__ ldc1(f6, MemOperand(a0, OFFSET_OF(T, i)) );
__ madd_d(f14, f6, f4, f6);
@ -325,7 +325,7 @@ TEST(MIPS3) {
CHECK_EQ(1.8066e16, t.e);
CHECK_EQ(120.44, t.f);
CHECK_EQ(10.97451593465515908537, t.g);
if (kArchVariant == kMips32r2) {
if (IsMipsArchVariant(kMips32r2)) {
CHECK_EQ(6.875, t.h);
}
}
@ -351,16 +351,28 @@ TEST(MIPS4) {
__ ldc1(f6, MemOperand(a0, OFFSET_OF(T, b)) );
// Swap f4 and f6, by using four integer registers, t0-t3.
__ mfc1(t0, f4);
__ mfc1(t1, f5);
__ mfc1(t2, f6);
__ mfc1(t3, f7);
if (!IsFp64Mode()) {
__ mfc1(t0, f4);
__ mfc1(t1, f5);
__ mfc1(t2, f6);
__ mfc1(t3, f7);
__ mtc1(t0, f6);
__ mtc1(t1, f7);
__ mtc1(t2, f4);
__ mtc1(t3, f5);
__ mtc1(t0, f6);
__ mtc1(t1, f7);
__ mtc1(t2, f4);
__ mtc1(t3, f5);
} else {
DCHECK(!IsMipsArchVariant(kMips32r1) && !IsMipsArchVariant(kLoongson));
__ mfc1(t0, f4);
__ mfhc1(t1, f4);
__ mfc1(t2, f6);
__ mfhc1(t3, f6);
__ mtc1(t0, f6);
__ mthc1(t1, f6);
__ mtc1(t2, f4);
__ mthc1(t3, f4);
}
// Store the swapped f4 and f5 back to memory.
__ sdc1(f4, MemOperand(a0, OFFSET_OF(T, a)) );
__ sdc1(f6, MemOperand(a0, OFFSET_OF(T, c)) );
@ -554,21 +566,30 @@ TEST(MIPS7) {
__ ldc1(f4, MemOperand(a0, OFFSET_OF(T, a)) );
__ ldc1(f6, MemOperand(a0, OFFSET_OF(T, b)) );
if (!IsMipsArchVariant(kMips32r6)) {
__ c(UN, D, f4, f6);
__ bc1f(&neither_is_nan);
} else {
__ cmp(UN, L, f2, f4, f6);
__ bc1eqz(&neither_is_nan, f2);
}
__ nop();
__ sw(zero_reg, MemOperand(a0, OFFSET_OF(T, result)) );
__ Branch(&outa_here);
__ bind(&neither_is_nan);
if (kArchVariant == kLoongson) {
if (IsMipsArchVariant(kLoongson)) {
__ c(OLT, D, f6, f4);
__ bc1t(&less_than);
} else if (IsMipsArchVariant(kMips32r6)) {
__ cmp(OLT, L, f2, f6, f4);
__ bc1nez(&less_than, f2);
} else {
__ c(OLT, D, f6, f4, 2);
__ bc1t(&less_than, 2);
}
__ nop();
__ sw(zero_reg, MemOperand(a0, OFFSET_OF(T, result)) );
__ Branch(&outa_here);
@ -716,7 +737,7 @@ TEST(MIPS9) {
MacroAssembler assm(isolate, NULL, 0);
Label exit, exit2, exit3;
__ Branch(&exit, ge, a0, Operand(0x00000000));
__ Branch(&exit, ge, a0, Operand(zero_reg));
__ Branch(&exit2, ge, a0, Operand(0x00001FFF));
__ Branch(&exit3, ge, a0, Operand(0x0001FFFF));
@ -753,50 +774,52 @@ TEST(MIPS10) {
Assembler assm(isolate, NULL, 0);
Label L, C;
if (kArchVariant == kMips32r2) {
// Load all structure elements to registers.
__ ldc1(f0, MemOperand(a0, OFFSET_OF(T, a)));
if (!IsMipsArchVariant(kMips32r2)) return;
// Save the raw bits of the double.
__ mfc1(t0, f0);
__ mfc1(t1, f1);
__ sw(t0, MemOperand(a0, OFFSET_OF(T, dbl_mant)));
__ sw(t1, MemOperand(a0, OFFSET_OF(T, dbl_exp)));
// Load all structure elements to registers.
__ ldc1(f0, MemOperand(a0, OFFSET_OF(T, a)));
// Convert double in f0 to long, save hi/lo parts.
__ cvt_w_d(f0, f0);
__ mfc1(t0, f0); // f0 has a 32-bits word.
__ sw(t0, MemOperand(a0, OFFSET_OF(T, word)));
// Save the raw bits of the double.
__ mfc1(t0, f0);
__ mfc1(t1, f1);
__ sw(t0, MemOperand(a0, OFFSET_OF(T, dbl_mant)));
__ sw(t1, MemOperand(a0, OFFSET_OF(T, dbl_exp)));
// Convert the b long integers to double b.
__ lw(t0, MemOperand(a0, OFFSET_OF(T, b_word)));
__ mtc1(t0, f8); // f8 has a 32-bits word.
__ cvt_d_w(f10, f8);
__ sdc1(f10, MemOperand(a0, OFFSET_OF(T, b)));
// Convert double in f0 to long, save hi/lo parts.
__ cvt_w_d(f0, f0);
__ mfc1(t0, f0); // f0 has a 32-bits word.
__ sw(t0, MemOperand(a0, OFFSET_OF(T, word)));
__ jr(ra);
__ nop();
// Convert the b long integers to double b.
__ lw(t0, MemOperand(a0, OFFSET_OF(T, b_word)));
__ mtc1(t0, f8); // f8 has a 32-bits word.
__ cvt_d_w(f10, f8);
__ sdc1(f10, MemOperand(a0, OFFSET_OF(T, b)));
CodeDesc desc;
assm.GetCode(&desc);
Handle<Code> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
F3 f = FUNCTION_CAST<F3>(code->entry());
t.a = 2.147483646e+09; // 0x7FFFFFFE -> 0xFF80000041DFFFFF as double.
t.b_word = 0x0ff00ff0; // 0x0FF00FF0 -> 0x as double.
Object* dummy = CALL_GENERATED_CODE(f, &t, 0, 0, 0, 0);
USE(dummy);
__ jr(ra);
__ nop();
CHECK_EQ(0x41DFFFFF, t.dbl_exp);
CHECK_EQ(0xFF800000, t.dbl_mant);
CHECK_EQ(0X7FFFFFFE, t.word);
// 0x0FF00FF0 -> 2.6739096+e08
CHECK_EQ(2.6739096e08, t.b);
}
CodeDesc desc;
assm.GetCode(&desc);
Handle<Code> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
F3 f = FUNCTION_CAST<F3>(code->entry());
t.a = 2.147483646e+09; // 0x7FFFFFFE -> 0xFF80000041DFFFFF as double.
t.b_word = 0x0ff00ff0; // 0x0FF00FF0 -> 0x as double.
Object* dummy = CALL_GENERATED_CODE(f, &t, 0, 0, 0, 0);
USE(dummy);
CHECK_EQ(0x41DFFFFF, t.dbl_exp);
CHECK_EQ(0xFF800000, t.dbl_mant);
CHECK_EQ(0X7FFFFFFE, t.word);
// 0x0FF00FF0 -> 2.6739096+e08
CHECK_EQ(2.6739096e08, t.b);
}
TEST(MIPS11) {
// Do not run test on MIPS32r6, as these instructions are removed.
if (IsMipsArchVariant(kMips32r6)) return;
// Test LWL, LWR, SWL and SWR instructions.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();

View File

@ -110,41 +110,127 @@ TEST(Type0) {
COMPARE(subu(v0, v1, s0),
"00701023 subu v0, v1, s0");
COMPARE(mult(a0, a1),
"00850018 mult a0, a1");
COMPARE(mult(t2, t3),
"014b0018 mult t2, t3");
COMPARE(mult(v0, v1),
"00430018 mult v0, v1");
if (!IsMipsArchVariant(kMips32r6)) {
COMPARE(mult(a0, a1),
"00850018 mult a0, a1");
COMPARE(mult(t2, t3),
"014b0018 mult t2, t3");
COMPARE(mult(v0, v1),
"00430018 mult v0, v1");
COMPARE(multu(a0, a1),
"00850019 multu a0, a1");
COMPARE(multu(t2, t3),
"014b0019 multu t2, t3");
COMPARE(multu(v0, v1),
"00430019 multu v0, v1");
COMPARE(multu(a0, a1),
"00850019 multu a0, a1");
COMPARE(multu(t2, t3),
"014b0019 multu t2, t3");
COMPARE(multu(v0, v1),
"00430019 multu v0, v1");
COMPARE(div(a0, a1),
"0085001a div a0, a1");
COMPARE(div(t2, t3),
"014b001a div t2, t3");
COMPARE(div(v0, v1),
"0043001a div v0, v1");
COMPARE(div(a0, a1),
"0085001a div a0, a1");
COMPARE(div(t2, t3),
"014b001a div t2, t3");
COMPARE(div(v0, v1),
"0043001a div v0, v1");
COMPARE(divu(a0, a1),
"0085001b divu a0, a1");
COMPARE(divu(t2, t3),
"014b001b divu t2, t3");
COMPARE(divu(v0, v1),
"0043001b divu v0, v1");
COMPARE(divu(a0, a1),
"0085001b divu a0, a1");
COMPARE(divu(t2, t3),
"014b001b divu t2, t3");
COMPARE(divu(v0, v1),
"0043001b divu v0, v1");
if (kArchVariant != kLoongson) {
if (!IsMipsArchVariant(kLoongson)) {
COMPARE(mul(a0, a1, a2),
"70a62002 mul a0, a1, a2");
COMPARE(mul(t2, t3, t4),
"716c5002 mul t2, t3, t4");
COMPARE(mul(v0, v1, s0),
"70701002 mul v0, v1, s0");
}
} else { // MIPS32r6.
COMPARE(mul(a0, a1, a2),
"70a62002 mul a0, a1, a2");
COMPARE(mul(t2, t3, t4),
"716c5002 mul t2, t3, t4");
COMPARE(mul(v0, v1, s0),
"70701002 mul v0, v1, s0");
"00a62098 mul a0, a1, a2");
COMPARE(muh(a0, a1, a2),
"00a620d8 muh a0, a1, a2");
COMPARE(mul(t1, t2, t3),
"014b4898 mul t1, t2, t3");
COMPARE(muh(t1, t2, t3),
"014b48d8 muh t1, t2, t3");
COMPARE(mul(v0, v1, a0),
"00641098 mul v0, v1, a0");
COMPARE(muh(v0, v1, a0),
"006410d8 muh v0, v1, a0");
COMPARE(mulu(a0, a1, a2),
"00a62099 mulu a0, a1, a2");
COMPARE(muhu(a0, a1, a2),
"00a620d9 muhu a0, a1, a2");
COMPARE(mulu(t1, t2, t3),
"014b4899 mulu t1, t2, t3");
COMPARE(muhu(t1, t2, t3),
"014b48d9 muhu t1, t2, t3");
COMPARE(mulu(v0, v1, a0),
"00641099 mulu v0, v1, a0");
COMPARE(muhu(v0, v1, a0),
"006410d9 muhu v0, v1, a0");
COMPARE(div(a0, a1, a2),
"00a6209a div a0, a1, a2");
COMPARE(mod(a0, a1, a2),
"00a620da mod a0, a1, a2");
COMPARE(div(t1, t2, t3),
"014b489a div t1, t2, t3");
COMPARE(mod(t1, t2, t3),
"014b48da mod t1, t2, t3");
COMPARE(div(v0, v1, a0),
"0064109a div v0, v1, a0");
COMPARE(mod(v0, v1, a0),
"006410da mod v0, v1, a0");
COMPARE(divu(a0, a1, a2),
"00a6209b divu a0, a1, a2");
COMPARE(modu(a0, a1, a2),
"00a620db modu a0, a1, a2");
COMPARE(divu(t1, t2, t3),
"014b489b divu t1, t2, t3");
COMPARE(modu(t1, t2, t3),
"014b48db modu t1, t2, t3");
COMPARE(divu(v0, v1, a0),
"0064109b divu v0, v1, a0");
COMPARE(modu(v0, v1, a0),
"006410db modu v0, v1, a0");
COMPARE(bovc(a0, a0, static_cast<int16_t>(0)),
"20840000 bovc a0, a0, 0");
COMPARE(bovc(a1, a0, static_cast<int16_t>(0)),
"20a40000 bovc a1, a0, 0");
COMPARE(bovc(a1, a0, 32767),
"20a47fff bovc a1, a0, 32767");
COMPARE(bovc(a1, a0, -32768),
"20a48000 bovc a1, a0, -32768");
COMPARE(bnvc(a0, a0, static_cast<int16_t>(0)),
"60840000 bnvc a0, a0, 0");
COMPARE(bnvc(a1, a0, static_cast<int16_t>(0)),
"60a40000 bnvc a1, a0, 0");
COMPARE(bnvc(a1, a0, 32767),
"60a47fff bnvc a1, a0, 32767");
COMPARE(bnvc(a1, a0, -32768),
"60a48000 bnvc a1, a0, -32768");
COMPARE(beqzc(a0, 0),
"d8800000 beqzc a0, 0x0");
COMPARE(beqzc(a0, 0xfffff), // 0x0fffff == 1048575.
"d88fffff beqzc a0, 0xfffff");
COMPARE(beqzc(a0, 0x100000), // 0x100000 == -1048576.
"d8900000 beqzc a0, 0x100000");
COMPARE(bnezc(a0, 0),
"f8800000 bnezc a0, 0x0");
COMPARE(bnezc(a0, 0xfffff), // 0x0fffff == 1048575.
"f88fffff bnezc a0, 0xfffff");
COMPARE(bnezc(a0, 0x100000), // 0x100000 == -1048576.
"f8900000 bnezc a0, 0x100000");
}
COMPARE(addiu(a0, a1, 0x0),
@ -266,7 +352,7 @@ TEST(Type0) {
COMPARE(srav(v0, v1, fp),
"03c31007 srav v0, v1, fp");
if (kArchVariant == kMips32r2) {
if (IsMipsArchVariant(kMips32r2)) {
COMPARE(rotr(a0, a1, 0),
"00252002 rotr a0, a1, 0");
COMPARE(rotr(s0, s1, 8),
@ -369,7 +455,7 @@ TEST(Type0) {
COMPARE(sltiu(v0, v1, -1),
"2c62ffff sltiu v0, v1, -1");
if (kArchVariant != kLoongson) {
if (!IsMipsArchVariant(kLoongson)) {
COMPARE(movz(a0, a1, a2),
"00a6200a movz a0, a1, a2");
COMPARE(movz(s0, s1, s2),
@ -404,15 +490,24 @@ TEST(Type0) {
COMPARE(movf(v0, v1, 6),
"00781001 movf v0, v1, 6");
COMPARE(clz(a0, a1),
"70a42020 clz a0, a1");
COMPARE(clz(s6, s7),
"72f6b020 clz s6, s7");
COMPARE(clz(v0, v1),
"70621020 clz v0, v1");
if (IsMipsArchVariant(kMips32r6)) {
COMPARE(clz(a0, a1),
"00a02050 clz a0, a1");
COMPARE(clz(s6, s7),
"02e0b050 clz s6, s7");
COMPARE(clz(v0, v1),
"00601050 clz v0, v1");
} else {
COMPARE(clz(a0, a1),
"70a42020 clz a0, a1");
COMPARE(clz(s6, s7),
"72f6b020 clz s6, s7");
COMPARE(clz(v0, v1),
"70621020 clz v0, v1");
}
}
if (kArchVariant == kMips32r2) {
if (IsMipsArchVariant(kMips32r2)) {
COMPARE(ins_(a0, a1, 31, 1),
"7ca4ffc4 ins a0, a1, 31, 1");
COMPARE(ins_(s6, s7, 30, 2),