| Index: src/mips/simulator-mips.cc
|
| ===================================================================
|
| --- src/mips/simulator-mips.cc (revision 4259)
|
| +++ src/mips/simulator-mips.cc (working copy)
|
| @@ -51,7 +51,7 @@
|
|
|
| // Utils functions
|
| bool HaveSameSign(int32_t a, int32_t b) {
|
| - return ((a ^ b) > 0);
|
| + return ((a ^ b) >= 0);
|
| }
|
|
|
|
|
| @@ -284,9 +284,11 @@
|
| "%" XSTR(ARG_SIZE) "s",
|
| cmd, arg1, arg2);
|
| if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) {
|
| - if (!(reinterpret_cast<Instruction*>(sim_->get_pc())->IsTrap())) {
|
| + Instruction* instr = reinterpret_cast<Instruction*>(sim_->get_pc());
|
| + if (!(instr->IsTrap()) ||
|
| + instr->InstructionBits() == rtCallRedirInstr) {
|
| sim_->InstructionDecode(
|
| - reinterpret_cast<Instruction*>(sim_->get_pc()));
|
| + reinterpret_cast<Instruction*>(sim_->get_pc()));
|
| } else {
|
| // Allow si to jump over generated breakpoints.
|
| PrintF("/!\\ Jumping over generated breakpoint.\n");
|
| @@ -500,6 +502,10 @@
|
| for (int i = 0; i < kNumSimuRegisters; i++) {
|
| registers_[i] = 0;
|
| }
|
| + for (int i = 0; i < kNumFPURegisters; i++) {
|
| + FPUregisters_[i] = 0;
|
| + }
|
| + FPUccr_ = 0;
|
|
|
| // The sp is initialized to point to the bottom (high address) of the
|
| // allocated stack area. To be safe in potential stack underflows we leave
|
| @@ -629,6 +635,22 @@
|
| const_cast<int32_t*>(&FPUregisters_[fpureg]));
|
| }
|
|
|
| +// Helper functions for setting and testing the FPU condition code bits.
|
| +void Simulator::set_fpu_ccr_bit(uint32_t cc, bool value) {
|
| + ASSERT(is_uint3(cc));
|
| + if (value) {
|
| + FPUccr_ |= (1 << cc);
|
| + } else {
|
| + FPUccr_ &= ~(1 << cc);
|
| + }
|
| +}
|
| +
|
| +bool Simulator::test_fpu_ccr_bit(uint32_t cc) {
|
| + ASSERT(is_uint3(cc));
|
| + return FPUccr_ & (1 << cc);
|
| +}
|
| +
|
| +
|
| // Raw access to the PC register.
|
| void Simulator::set_pc(int32_t value) {
|
| pc_modified_ = true;
|
| @@ -676,7 +698,7 @@
|
| double* ptr = reinterpret_cast<double*>(addr);
|
| return *ptr;
|
| }
|
| - PrintF("Unaligned read at 0x%08x, pc=%p\n", addr, instr);
|
| + PrintF("Unaligned (double) read at 0x%08x, pc=%p\n", addr, instr);
|
| OS::Abort();
|
| return 0;
|
| }
|
| @@ -688,7 +710,7 @@
|
| *ptr = value;
|
| return;
|
| }
|
| - PrintF("Unaligned write at 0x%08x, pc=%p\n", addr, instr);
|
| + PrintF("Unaligned (double) write at 0x%08x, pc=%p\n", addr, instr);
|
| OS::Abort();
|
| }
|
|
|
| @@ -745,7 +767,7 @@
|
|
|
| int32_t Simulator::ReadB(int32_t addr) {
|
| int8_t* ptr = reinterpret_cast<int8_t*>(addr);
|
| - return ((*ptr << 24) >> 24) & 0xff;
|
| + return *ptr;
|
| }
|
|
|
|
|
| @@ -781,16 +803,17 @@
|
| // Note: To be able to return two values from some calls the code in runtime.cc
|
| // uses the ObjectPair which is essentially two 32-bit values stuffed into a
|
| // 64-bit value. With the code below we assume that all runtime calls return
|
| -// 64 bits of result. If they don't, the r1 result register contains a bogus
|
| +// 64 bits of result. If they don't, the v1 result register contains a bogus
|
| // value, which is fine because it is caller-saved.
|
| typedef int64_t (*SimulatorRuntimeCall)(int32_t arg0,
|
| int32_t arg1,
|
| int32_t arg2,
|
| int32_t arg3);
|
| -typedef double (*SimulatorRuntimeFPCall)(double fparg0,
|
| - double fparg1);
|
| +typedef double (*SimulatorRuntimeFPCall)(int32_t arg0,
|
| + int32_t arg1,
|
| + int32_t arg2,
|
| + int32_t arg3);
|
|
|
| -
|
| // Software interrupt instructions are used by the simulator to call into the
|
| // C-based V8 runtime.
|
| void Simulator::SoftwareInterrupt(Instruction* instr) {
|
| @@ -801,47 +824,54 @@
|
| int32_t arg1 = get_register(a1);
|
| int32_t arg2 = get_register(a2);
|
| int32_t arg3 = get_register(a3);
|
| - // fp args are (not always) in f12 and f14.
|
| - // See MIPS conventions for more details.
|
| - double fparg0 = get_fpu_register_double(f12);
|
| - double fparg1 = get_fpu_register_double(f14);
|
| + int32_t result_l, result_h;
|
| // This is dodgy but it works because the C entry stubs are never moved.
|
| // See comment in codegen-arm.cc and bug 1242173.
|
| int32_t saved_ra = get_register(ra);
|
| +
|
| + intptr_t external =
|
| + reinterpret_cast<int32_t>(redirection->external_function());
|
| + SimulatorRuntimeCall target =
|
| + reinterpret_cast<SimulatorRuntimeCall>(external);
|
| +
|
| + if (::v8::internal::FLAG_trace_sim) {
|
| + PrintF(
|
| + "Call to host function at %p with args %08x, %08x, %08x, %08x\n",
|
| + FUNCTION_ADDR(target),
|
| + arg0,
|
| + arg1,
|
| + arg2,
|
| + arg3);
|
| + }
|
| +
|
| + // Based on CpuFeatures::IsSupported(FPU), Mips will use either hardware
|
| + // FPU, or gcc soft-float routines. Hardware FPU is simulated in this
|
| + // simulator. Soft-float has additional abstraction of ExternalReference,
|
| + // to support serialization. Finally, when simulated on x86 host, the
|
| + // x86 softfloat routines are used, and this Redirection infrastructure
|
| + // lets simulated-mips make calls into x86 C code.
|
| + // When doing that, the 'double' return type must be handled differently
|
| + // than the usual int64_t return. The data is returned in different
|
| + // registers and cannot be cast from one type to the other. However, the
|
| + // calling arguments are passed the same way in both cases.
|
| if (redirection->fp_return()) {
|
| - intptr_t external =
|
| - reinterpret_cast<intptr_t>(redirection->external_function());
|
| SimulatorRuntimeFPCall target =
|
| reinterpret_cast<SimulatorRuntimeFPCall>(external);
|
| - if (::v8::internal::FLAG_trace_sim) {
|
| - PrintF("Call to host function at %p with args %f, %f\n",
|
| - FUNCTION_ADDR(target), fparg0, fparg1);
|
| - }
|
| - double result = target(fparg0, fparg1);
|
| - set_fpu_register_double(f0, result);
|
| + double result = target(arg0, arg1, arg2, arg3);
|
| + uint64_t u64;
|
| + u64 = *v8i::BitCast<uint64_t*, double*>(const_cast<double*>(&result));
|
| + result_h = static_cast<uint32_t>(u64 >> 32);
|
| + result_l = static_cast<uint32_t>(u64 & 0xffffffff);
|
| } else {
|
| - intptr_t external =
|
| - reinterpret_cast<int32_t>(redirection->external_function());
|
| - SimulatorRuntimeCall target =
|
| - reinterpret_cast<SimulatorRuntimeCall>(external);
|
| - if (::v8::internal::FLAG_trace_sim) {
|
| - PrintF(
|
| - "Call to host function at %p with args %08x, %08x, %08x, %08x\n",
|
| - FUNCTION_ADDR(target),
|
| - arg0,
|
| - arg1,
|
| - arg2,
|
| - arg3);
|
| - }
|
| int64_t result = target(arg0, arg1, arg2, arg3);
|
| - int32_t lo_res = static_cast<int32_t>(result);
|
| - int32_t hi_res = static_cast<int32_t>(result >> 32);
|
| - if (::v8::internal::FLAG_trace_sim) {
|
| - PrintF("Returned %08x\n", lo_res);
|
| - }
|
| - set_register(v0, lo_res);
|
| - set_register(v1, hi_res);
|
| + result_l = static_cast<int32_t>(result);
|
| + result_h = static_cast<int32_t>(result >> 32);
|
| }
|
| + set_register(v0, result_l);
|
| + set_register(v1, result_h);
|
| + if (::v8::internal::FLAG_trace_sim) {
|
| + PrintF("Returned %08x : %08x\n", result_h, result_l);
|
| + }
|
| set_register(ra, saved_ra);
|
| set_pc(get_register(ra));
|
| } else {
|
| @@ -860,7 +890,7 @@
|
|
|
| // Handle execution based on instruction types.
|
| void Simulator::DecodeTypeRegister(Instruction* instr) {
|
| - // Instruction fields
|
| + // Instruction fields.
|
| Opcode op = instr->OpcodeFieldRaw();
|
| int32_t rs_reg = instr->RsField();
|
| int32_t rs = get_register(rs_reg);
|
| @@ -871,14 +901,16 @@
|
| int32_t rd_reg = instr->RdField();
|
| uint32_t sa = instr->SaField();
|
|
|
| - int32_t fs_reg= instr->FsField();
|
| + int32_t fs_reg = instr->FsField();
|
| + int32_t ft_reg = instr->FtField();
|
| + int32_t fd_reg = instr->FdField();
|
| + int64_t i64hilo = 0;
|
| + uint64_t u64hilo = 0;
|
|
|
| // ALU output
|
| - // It should not be used as is. Instructions using it should always initialize
|
| - // it first.
|
| + // It should not be used as is. Instructions using it should always
|
| + // initialize it first.
|
| int32_t alu_out = 0x12345678;
|
| - // Output or temporary for floating point.
|
| - double fp_out = 0.0;
|
|
|
| // For break and trap instructions.
|
| bool do_interrupt = false;
|
| @@ -893,15 +925,14 @@
|
| switch (op) {
|
| case COP1: // Coprocessor instructions
|
| switch (instr->RsFieldRaw()) {
|
| - case BC1: // branch on coprocessor condition
|
| + case BC1: // Handled in DecodeTypeImmed, should never come here.
|
| UNREACHABLE();
|
| break;
|
| case MFC1:
|
| alu_out = get_fpu_register(fs_reg);
|
| break;
|
| case MFHC1:
|
| - fp_out = get_fpu_register_double(fs_reg);
|
| - alu_out = *v8i::BitCast<int32_t*, double*>(&fp_out);
|
| + UNIMPLEMENTED_MIPS();
|
| break;
|
| case MTC1:
|
| case MTHC1:
|
| @@ -949,10 +980,10 @@
|
| alu_out = get_register(LO);
|
| break;
|
| case MULT:
|
| - UNIMPLEMENTED_MIPS();
|
| + i64hilo = static_cast<int64_t>(rs) * static_cast<int64_t>(rt);
|
| break;
|
| case MULTU:
|
| - UNIMPLEMENTED_MIPS();
|
| + u64hilo = static_cast<uint64_t>(rs_u) * static_cast<uint64_t>(rt_u);
|
| break;
|
| case DIV:
|
| case DIVU:
|
| @@ -1024,6 +1055,10 @@
|
| case TNE:
|
| do_interrupt = rs != rt;
|
| break;
|
| + case MOVN:
|
| + case MOVZ:
|
| + // No action taken on decode.
|
| + break;
|
| default:
|
| UNREACHABLE();
|
| };
|
| @@ -1033,10 +1068,39 @@
|
| case MUL:
|
| alu_out = rs_u * rt_u; // Only the lower 32 bits are kept.
|
| break;
|
| + case CLZ:
|
| + alu_out = __builtin_clz(rs_u);
|
| + break;
|
| default:
|
| UNREACHABLE();
|
| - }
|
| + };
|
| break;
|
| + case SPECIAL3:
|
| + switch (instr->FunctionFieldRaw()) {
|
| + case INS: { // mips32r2 instruction.
|
| + // Interpret Rd field as 5-bit msb of insert.
|
| + uint16_t msb = rd_reg;
|
| + // Interpret sa field as 5-bit lsb of insert.
|
| + uint16_t lsb = sa;
|
| + uint16_t size = msb - lsb + 1;
|
| + uint16_t mask = (1 << size) - 1;
|
| + alu_out = (rt_u & ~(mask << lsb)) | ((rs_u & mask) << lsb);
|
| + }
|
| + break;
|
| + case EXT: { // mips32r2 instruction.
|
| + // Interpret Rd field as 5-bit msb of extract.
|
| + uint16_t msb = rd_reg;
|
| + // Interpret sa field as 5-bit lsb of extract.
|
| + uint16_t lsb = sa;
|
| + uint16_t size = msb - lsb + 1;
|
| + uint16_t mask = (1 << size) - 1;
|
| + alu_out = (rs_u & (mask << lsb)) >> lsb;
|
| + }
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| + };
|
| + break;
|
| default:
|
| UNREACHABLE();
|
| };
|
| @@ -1052,18 +1116,16 @@
|
| UNREACHABLE();
|
| break;
|
| case MFC1:
|
| - case MFHC1:
|
| set_register(rt_reg, alu_out);
|
| break;
|
| + case MFHC1:
|
| + UNIMPLEMENTED_MIPS();
|
| + break;
|
| case MTC1:
|
| - // We don't need to set the higher bits to 0, because MIPS ISA says
|
| - // they are in an unpredictable state after executing MTC1.
|
| FPUregisters_[fs_reg] = registers_[rt_reg];
|
| - FPUregisters_[fs_reg+1] = Unpredictable;
|
| break;
|
| case MTHC1:
|
| - // Here we need to keep the lower bits unchanged.
|
| - FPUregisters_[fs_reg+1] = registers_[rt_reg];
|
| + UNIMPLEMENTED_MIPS();
|
| break;
|
| case S:
|
| switch (instr->FunctionFieldRaw()) {
|
| @@ -1078,9 +1140,59 @@
|
| }
|
| break;
|
| case D:
|
| + double ft, fs;
|
| + uint32_t cc;
|
| + fs = get_fpu_register_double(fs_reg);
|
| + ft = get_fpu_register_double(ft_reg);
|
| + cc = instr->FCccField();
|
| switch (instr->FunctionFieldRaw()) {
|
| + case ADD_D:
|
| + set_fpu_register_double(fd_reg, fs + ft);
|
| + break;
|
| + case SUB_D:
|
| + set_fpu_register_double(fd_reg, fs - ft);
|
| + break;
|
| + case MUL_D:
|
| + set_fpu_register_double(fd_reg, fs * ft);
|
| + break;
|
| + case DIV_D:
|
| + set_fpu_register_double(fd_reg, fs / ft);
|
| + break;
|
| + case ABS_D:
|
| + set_fpu_register_double(fd_reg, fs < 0 ? -fs : fs);
|
| + break;
|
| + case MOV_D:
|
| + set_fpu_register_double(fd_reg, fs);
|
| + break;
|
| + case NEG_D:
|
| + set_fpu_register_double(fd_reg, -fs);
|
| + break;
|
| + case CVT_W_D: // Convert double to word
|
| + set_fpu_register(fd_reg, static_cast<int32_t>(fs));
|
| + break;
|
| + case C_UN_D:
|
| + set_fpu_ccr_bit(cc, isnan(fs) || isnan(ft));
|
| + break;
|
| + case C_EQ_D:
|
| + set_fpu_ccr_bit(cc, (fs == ft));
|
| + break;
|
| + case C_UEQ_D:
|
| + set_fpu_ccr_bit(cc, (fs == ft) || (isnan(fs) || isnan(ft)));
|
| + break;
|
| + case C_OLT_D:
|
| + set_fpu_ccr_bit(cc, (fs < ft));
|
| + break;
|
| + case C_ULT_D:
|
| + set_fpu_ccr_bit(cc, (fs < ft) || (isnan(fs) || isnan(ft)));
|
| + break;
|
| + case C_OLE_D:
|
| + set_fpu_ccr_bit(cc, (fs <= ft));
|
| + break;
|
| + case C_ULE_D:
|
| + set_fpu_ccr_bit(cc, (fs <= ft) || (isnan(fs) || isnan(ft)));
|
| + break;
|
| + case C_F_D:
|
| case CVT_S_D:
|
| - case CVT_W_D:
|
| case CVT_L_D:
|
| UNIMPLEMENTED_MIPS();
|
| break;
|
| @@ -1094,7 +1206,8 @@
|
| UNIMPLEMENTED_MIPS();
|
| break;
|
| case CVT_D_W: // Convert word to double.
|
| - set_fpu_register(rd_reg, static_cast<double>(rs));
|
| + alu_out = get_fpu_register(fs_reg);
|
| + set_fpu_register_double(fd_reg, static_cast<double>(alu_out));
|
| break;
|
| default:
|
| UNREACHABLE();
|
| @@ -1137,7 +1250,12 @@
|
| }
|
| // 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));
|
| + break;
|
| case MULTU:
|
| + set_register(LO, static_cast<int32_t>(u64hilo & 0xffffffff));
|
| + set_register(HI, static_cast<int32_t>(u64hilo >> 32));
|
| break;
|
| case DIV:
|
| // Divide by zero was checked in the configuration step.
|
| @@ -1148,7 +1266,7 @@
|
| set_register(LO, rs_u / rt_u);
|
| set_register(HI, rs_u % rt_u);
|
| break;
|
| - // Break and trap instructions
|
| + // Break and trap instructions.
|
| case BREAK:
|
| case TGE:
|
| case TGEU:
|
| @@ -1160,6 +1278,13 @@
|
| SoftwareInterrupt(instr);
|
| }
|
| break;
|
| + // Conditional moves.
|
| + case MOVN:
|
| + if (rt) set_register(rd_reg, rs);
|
| + break;
|
| + case MOVZ:
|
| + if (!rt) set_register(rd_reg, rs);
|
| + break;
|
| default: // For other special opcodes we do the default operation.
|
| set_register(rd_reg, alu_out);
|
| };
|
| @@ -1172,9 +1297,23 @@
|
| set_register(LO, Unpredictable);
|
| set_register(HI, Unpredictable);
|
| break;
|
| + default: // For other special2 opcodes we do the default operation.
|
| + set_register(rd_reg, alu_out);
|
| + }
|
| + break;
|
| + case SPECIAL3:
|
| + switch (instr->FunctionFieldRaw()) {
|
| + case INS:
|
| + // Ins instr leaves result in Rt, rather than Rd.
|
| + set_register(rt_reg, alu_out);
|
| + break;
|
| + case EXT:
|
| + // Ext instr leaves result in Rt, rather than Rd.
|
| + set_register(rt_reg, alu_out);
|
| + break;
|
| default:
|
| UNREACHABLE();
|
| - }
|
| + };
|
| break;
|
| // Unimplemented opcodes raised an error in the configuration step before,
|
| // so we can use the default here to set the destination register in common
|
| @@ -1186,7 +1325,7 @@
|
|
|
| // Type 2: instructions using a 16 bytes immediate. (eg: addi, beq)
|
| void Simulator::DecodeTypeImmediate(Instruction* instr) {
|
| - // Instruction fields
|
| + // Instruction fields.
|
| Opcode op = instr->OpcodeFieldRaw();
|
| int32_t rs = get_register(instr->RsField());
|
| uint32_t rs_u = static_cast<uint32_t>(rs);
|
| @@ -1195,11 +1334,10 @@
|
| int16_t imm16 = instr->Imm16Field();
|
|
|
| int32_t ft_reg = instr->FtField(); // destination register
|
| - int32_t ft = get_register(ft_reg);
|
|
|
| - // zero extended immediate
|
| + // Zero extended immediate.
|
| uint32_t oe_imm16 = 0xffff & imm16;
|
| - // sign extended immediate
|
| + // Sign extended immediate.
|
| int32_t se_imm16 = imm16;
|
|
|
| // Get current pc.
|
| @@ -1207,25 +1345,35 @@
|
| // Next pc.
|
| int32_t next_pc = bad_ra;
|
|
|
| - // Used for conditional branch instructions
|
| + // Used for conditional branch instructions.
|
| bool do_branch = false;
|
| bool execute_branch_delay_instruction = false;
|
|
|
| - // Used for arithmetic instructions
|
| + // Used for arithmetic instructions.
|
| int32_t alu_out = 0;
|
| - // Floating point
|
| + // Floating point.
|
| double fp_out = 0.0;
|
| + uint32_t cc, cc_value;
|
|
|
| - // Used for memory instructions
|
| + // Used for memory instructions.
|
| int32_t addr = 0x0;
|
|
|
| // ---------- Configuration (and execution for REGIMM)
|
| switch (op) {
|
| - // ------------- COP1. Coprocessor instructions
|
| + // ------------- COP1. Coprocessor instructions.
|
| case COP1:
|
| switch (instr->RsFieldRaw()) {
|
| - case BC1: // branch on coprocessor condition
|
| - UNIMPLEMENTED_MIPS();
|
| + case BC1: // Branch on coprocessor condition.
|
| + cc = instr->FBccField();
|
| + cc_value = test_fpu_ccr_bit(cc);
|
| + do_branch = (instr->FBtrueField()) ? cc_value : !cc_value;
|
| + execute_branch_delay_instruction = true;
|
| + // Set next_pc
|
| + if (do_branch) {
|
| + next_pc = current_pc + (imm16 << 2) + Instruction::kInstructionSize;
|
| + } else {
|
| + next_pc = current_pc + kBranchReturnOffset;
|
| + }
|
| break;
|
| default:
|
| UNREACHABLE();
|
| @@ -1322,6 +1470,10 @@
|
| addr = rs + se_imm16;
|
| alu_out = ReadB(addr);
|
| break;
|
| + case LH:
|
| + addr = rs + se_imm16;
|
| + alu_out = ReadH(addr, instr);
|
| + break;
|
| case LW:
|
| addr = rs + se_imm16;
|
| alu_out = ReadW(addr, instr);
|
| @@ -1330,9 +1482,16 @@
|
| addr = rs + se_imm16;
|
| alu_out = ReadBU(addr);
|
| break;
|
| + case LHU:
|
| + addr = rs + se_imm16;
|
| + alu_out = ReadHU(addr, instr);
|
| + break;
|
| case SB:
|
| addr = rs + se_imm16;
|
| break;
|
| + case SH:
|
| + addr = rs + se_imm16;
|
| + break;
|
| case SW:
|
| addr = rs + se_imm16;
|
| break;
|
| @@ -1387,13 +1546,18 @@
|
| break;
|
| // ------------- Memory instructions
|
| case LB:
|
| + case LH:
|
| case LW:
|
| case LBU:
|
| + case LHU:
|
| set_register(rt_reg, alu_out);
|
| break;
|
| case SB:
|
| WriteB(addr, static_cast<int8_t>(rt));
|
| break;
|
| + case SH:
|
| + WriteH(addr, static_cast<uint16_t>(rt), instr);
|
| + break;
|
| case SW:
|
| WriteW(addr, rt, instr);
|
| break;
|
| @@ -1409,7 +1573,7 @@
|
| break;
|
| case SDC1:
|
| addr = rs + se_imm16;
|
| - WriteD(addr, ft, instr);
|
| + WriteD(addr, get_fpu_register_double(ft_reg), instr);
|
| break;
|
| default:
|
| break;
|
| @@ -1537,7 +1701,7 @@
|
| int original_stack = get_register(sp);
|
| // Compute position of stack on entry to generated code.
|
| int entry_stack = (original_stack - (argument_count - 4) * sizeof(int32_t)
|
| - - kArgsSlotsSize);
|
| + - kCArgsSlotsSize);
|
| if (OS::ActivationFrameAlignment() != 0) {
|
| entry_stack &= -OS::ActivationFrameAlignment();
|
| }
|
|
|