: print infos about number \n"); PrintF(" or all stop(s).\n"); PrintF(" stop enable/disable all/ : enables / disables\n"); PrintF(" all or number stop(s)\n"); PrintF(" stop unstop\n"); PrintF(" ignore the stop instruction at the current location\n"); PrintF(" from now on\n"); } else { PrintF("Unknown command: %s\n", cmd); } } } // Add all the breakpoints back to stop execution and enter the debugger // shell when hit. RedoBreakpoints(); #undef COMMAND_SIZE #undef ARG_SIZE #undef STR #undef XSTR } bool Simulator::ICacheMatch(void* one, void* two) { DCHECK_EQ(reinterpret_cast(one) & CachePage::kPageMask, 0); DCHECK_EQ(reinterpret_cast(two) & CachePage::kPageMask, 0); return one == two; } static uint32_t ICacheHash(void* key) { return static_cast(reinterpret_cast(key)) >> 2; } static bool AllOnOnePage(uintptr_t start, int size) { intptr_t start_page = (start & ~CachePage::kPageMask); intptr_t end_page = ((start + size) & ~CachePage::kPageMask); return start_page == end_page; } void Simulator::set_last_debugger_input(char* input) { DeleteArray(last_debugger_input_); last_debugger_input_ = input; } void Simulator::SetRedirectInstruction(Instruction* instruction) { instruction->SetInstructionBits(al | (0xF * B24) | kCallRtRedirected); } void Simulator::FlushICache(base::CustomMatcherHashMap* i_cache, void* start_addr, size_t size) { intptr_t start = reinterpret_cast(start_addr); int intra_line = (start & CachePage::kLineMask); start -= intra_line; size += intra_line; size = ((size - 1) | CachePage::kLineMask) + 1; int offset = (start & CachePage::kPageMask); while (!AllOnOnePage(start, size - 1)) { int bytes_to_flush = CachePage::kPageSize - offset; FlushOnePage(i_cache, start, bytes_to_flush); start += bytes_to_flush; size -= bytes_to_flush; DCHECK_EQ(0, start & CachePage::kPageMask); offset = 0; } if (size != 0) { FlushOnePage(i_cache, start, size); } } CachePage* Simulator::GetCachePage(base::CustomMatcherHashMap* i_cache, void* page) { base::HashMap::Entry* entry = i_cache->LookupOrInsert(page, ICacheHash(page)); if (entry->value == nullptr) { CachePage* new_page = new CachePage(); entry->value = new_page; } return reinterpret_cast(entry->value); } // Flush from start up to and not including start + size. void Simulator::FlushOnePage(base::CustomMatcherHashMap* i_cache, intptr_t start, int size) { DCHECK_LE(size, CachePage::kPageSize); DCHECK(AllOnOnePage(start, size - 1)); DCHECK_EQ(start & CachePage::kLineMask, 0); DCHECK_EQ(size & CachePage::kLineMask, 0); void* page = reinterpret_cast(start & (~CachePage::kPageMask)); int offset = (start & CachePage::kPageMask); CachePage* cache_page = GetCachePage(i_cache, page); char* valid_bytemap = cache_page->ValidityByte(offset); memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift); } void Simulator::CheckICache(base::CustomMatcherHashMap* i_cache, Instruction* instr) { intptr_t address = reinterpret_cast(instr); void* page = reinterpret_cast(address & (~CachePage::kPageMask)); void* line = reinterpret_cast(address & (~CachePage::kLineMask)); int offset = (address & CachePage::kPageMask); CachePage* cache_page = GetCachePage(i_cache, page); char* cache_valid_byte = cache_page->ValidityByte(offset); bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID); char* cached_line = cache_page->CachedData(offset & ~CachePage::kLineMask); if (cache_hit) { // Check that the data in memory matches the contents of the I-cache. CHECK_EQ(0, memcmp(reinterpret_cast(instr), cache_page->CachedData(offset), kInstrSize)); } else { // Cache miss. Load memory into the cache. memcpy(cached_line, line, CachePage::kLineLength); *cache_valid_byte = CachePage::LINE_VALID; } } Simulator::Simulator(Isolate* isolate) : isolate_(isolate) { // Set up simulator support first. Some of this information is needed to // setup the architecture state. size_t stack_size = 1 * 1024*1024; // allocate 1MB for stack stack_ = reinterpret_cast(malloc(stack_size)); pc_modified_ = false; icount_ = 0; break_pc_ = nullptr; break_instr_ = 0; // Set up architecture state. // All registers are initialized to zero to start with. for (int i = 0; i < num_registers; i++) { registers_[i] = 0; } n_flag_ = false; z_flag_ = false; c_flag_ = false; v_flag_ = false; // Initializing VFP registers. // All registers are initialized to zero to start with // even though s_registers_ & d_registers_ share the same // physical registers in the target. for (int i = 0; i < num_d_registers * 2; i++) { vfp_registers_[i] = 0; } n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = false; v_flag_FPSCR_ = false; FPSCR_rounding_mode_ = RN; FPSCR_default_NaN_mode_ = false; inv_op_vfp_flag_ = false; div_zero_vfp_flag_ = false; overflow_vfp_flag_ = false; underflow_vfp_flag_ = false; inexact_vfp_flag_ = false; // 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 // some buffer below. registers_[sp] = reinterpret_cast(stack_) + stack_size - 64; // The lr and pc are initialized to a known bad value that will cause an // access violation if the simulator ever tries to execute it. registers_[pc] = bad_lr; registers_[lr] = bad_lr; last_debugger_input_ = nullptr; } Simulator::~Simulator() { global_monitor_.Pointer()->RemoveProcessor(&global_monitor_processor_); free(stack_); } // Get the active Simulator for the current thread. Simulator* Simulator::current(Isolate* isolate) { v8::internal::Isolate::PerIsolateThreadData* isolate_data = isolate->FindOrAllocatePerThreadDataForThisThread(); DCHECK_NOT_NULL(isolate_data); Simulator* sim = isolate_data->simulator(); if (sim == nullptr) { // TODO(146): delete the simulator object when a thread/isolate goes away. sim = new Simulator(isolate); isolate_data->set_simulator(sim); } return sim; } // Sets the register in the architecture state. It will also deal with updating // Simulator internal state for special registers such as PC. void Simulator::set_register(int reg, int32_t value) { DCHECK((reg >= 0) && (reg < num_registers)); if (reg == pc) { pc_modified_ = true; } registers_[reg] = value; } // Get the register from the architecture state. This function does handle // the special case of accessing the PC register. int32_t Simulator::get_register(int reg) const { DCHECK((reg >= 0) && (reg < num_registers)); // Stupid code added to avoid bug in GCC. // See: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949 if (reg >= num_registers) return 0; // End stupid code. return registers_[reg] + ((reg == pc) ? Instruction::kPcLoadDelta : 0); } double Simulator::get_double_from_register_pair(int reg) { DCHECK((reg >= 0) && (reg < num_registers) && ((reg % 2) == 0)); double dm_val = 0.0; // Read the bits from the unsigned integer register_[] array // into the double precision floating point value and return it. char buffer[2 * sizeof(vfp_registers_[0])]; memcpy(buffer, ®isters_[reg], 2 * sizeof(registers_[0])); memcpy(&dm_val, buffer, 2 * sizeof(registers_[0])); return(dm_val); } void Simulator::set_register_pair_from_double(int reg, double* value) { DCHECK((reg >= 0) && (reg < num_registers) && ((reg % 2) == 0)); memcpy(registers_ + reg, value, sizeof(*value)); } void Simulator::set_dw_register(int dreg, const int* dbl) { DCHECK((dreg >= 0) && (dreg < num_d_registers)); registers_[dreg] = dbl[0]; registers_[dreg + 1] = dbl[1]; } void Simulator::get_d_register(int dreg, uint64_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value)); } void Simulator::set_d_register(int dreg, const uint64_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value)); } void Simulator::get_d_register(int dreg, uint32_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value) * 2); } void Simulator::set_d_register(int dreg, const uint32_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value) * 2); } template void Simulator::get_neon_register(int reg, T (&value)[SIZE / sizeof(T)]) { DCHECK(SIZE == kSimd128Size || SIZE == kDoubleSize); DCHECK_LE(0, reg); DCHECK_GT(SIZE == kSimd128Size ? num_q_registers : num_d_registers, reg); memcpy(value, vfp_registers_ + reg * (SIZE / 4), SIZE); } template void Simulator::set_neon_register(int reg, const T (&value)[SIZE / sizeof(T)]) { DCHECK(SIZE == kSimd128Size || SIZE == kDoubleSize); DCHECK_LE(0, reg); DCHECK_GT(SIZE == kSimd128Size ? num_q_registers : num_d_registers, reg); memcpy(vfp_registers_ + reg * (SIZE / 4), value, SIZE); } // Raw access to the PC register. void Simulator::set_pc(int32_t value) { pc_modified_ = true; registers_[pc] = value; } bool Simulator::has_bad_pc() const { return ((registers_[pc] == bad_lr) || (registers_[pc] == end_sim_pc)); } // Raw access to the PC register without the special adjustment when reading. int32_t Simulator::get_pc() const { return registers_[pc]; } // Getting from and setting into VFP registers. void Simulator::set_s_register(int sreg, unsigned int value) { DCHECK((sreg >= 0) && (sreg < num_s_registers)); vfp_registers_[sreg] = value; } unsigned int Simulator::get_s_register(int sreg) const { DCHECK((sreg >= 0) && (sreg < num_s_registers)); return vfp_registers_[sreg]; } template void Simulator::SetVFPRegister(int reg_index, const InputType& value) { unsigned bytes = register_size * sizeof(vfp_registers_[0]); DCHECK_EQ(sizeof(InputType), bytes); DCHECK_GE(reg_index, 0); if (register_size == 1) DCHECK(reg_index < num_s_registers); if (register_size == 2) DCHECK(reg_index < DwVfpRegister::NumRegisters()); memcpy(&vfp_registers_[reg_index * register_size], &value, bytes); } template ReturnType Simulator::GetFromVFPRegister(int reg_index) { unsigned bytes = register_size * sizeof(vfp_registers_[0]); DCHECK_EQ(sizeof(ReturnType), bytes); DCHECK_GE(reg_index, 0); if (register_size == 1) DCHECK(reg_index < num_s_registers); if (register_size == 2) DCHECK(reg_index < DwVfpRegister::NumRegisters()); ReturnType value; memcpy(&value, &vfp_registers_[register_size * reg_index], bytes); return value; } void Simulator::SetSpecialRegister(SRegisterFieldMask reg_and_mask, uint32_t value) { // Only CPSR_f is implemented. Of that, only N, Z, C and V are implemented. if ((reg_and_mask == CPSR_f) && ((value & ~kSpecialCondition) == 0)) { n_flag_ = ((value & (1 << 31)) != 0); z_flag_ = ((value & (1 << 30)) != 0); c_flag_ = ((value & (1 << 29)) != 0); v_flag_ = ((value & (1 << 28)) != 0); } else { UNIMPLEMENTED(); } } uint32_t Simulator::GetFromSpecialRegister(SRegister reg) { uint32_t result = 0; // Only CPSR_f is implemented. if (reg == CPSR) { if (n_flag_) result |= (1 << 31); if (z_flag_) result |= (1 << 30); if (c_flag_) result |= (1 << 29); if (v_flag_) result |= (1 << 28); } else { UNIMPLEMENTED(); } return result; } // Runtime FP routines take: // - two double arguments // - one double argument and zero or one integer arguments. // All are consructed here from r0-r3 or d0, d1 and r0. void Simulator::GetFpArgs(double* x, double* y, int32_t* z) { if (use_eabi_hardfloat()) { *x = get_double_from_d_register(0).get_scalar(); *y = get_double_from_d_register(1).get_scalar(); *z = get_register(0); } else { // Registers 0 and 1 -> x. *x = get_double_from_register_pair(0); // Register 2 and 3 -> y. *y = get_double_from_register_pair(2); // Register 2 -> z *z = get_register(2); } } // The return value is either in r0/r1 or d0. void Simulator::SetFpResult(const double& result) { if (use_eabi_hardfloat()) { char buffer[2 * sizeof(vfp_registers_[0])]; memcpy(buffer, &result, sizeof(buffer)); // Copy result to d0. memcpy(vfp_registers_, buffer, sizeof(buffer)); } else { char buffer[2 * sizeof(registers_[0])]; memcpy(buffer, &result, sizeof(buffer)); // Copy result to r0 and r1. memcpy(registers_, buffer, sizeof(buffer)); } } void Simulator::TrashCallerSaveRegisters() { // We don't trash the registers with the return value. registers_[2] = 0x50BAD4U; registers_[3] = 0x50BAD4U; registers_[12] = 0x50BAD4U; } int Simulator::ReadW(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); intptr_t* ptr = reinterpret_cast(addr); return *ptr; } int Simulator::ReadExW(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::Word); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); intptr_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::WriteW(int32_t addr, int value) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); intptr_t* ptr = reinterpret_cast(addr); *ptr = value; } int Simulator::WriteExW(int32_t addr, int value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::Word) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { intptr_t* ptr = reinterpret_cast(addr); *ptr = value; return 0; } else { return 1; } } uint16_t Simulator::ReadHU(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); uint16_t* ptr = reinterpret_cast(addr); return *ptr; } int16_t Simulator::ReadH(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); int16_t* ptr = reinterpret_cast(addr); return *ptr; } uint16_t Simulator::ReadExHU(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::HalfWord); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); uint16_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::WriteH(int32_t addr, uint16_t value) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); uint16_t* ptr = reinterpret_cast(addr); *ptr = value; } void Simulator::WriteH(int32_t addr, int16_t value) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); int16_t* ptr = reinterpret_cast(addr); *ptr = value; } int Simulator::WriteExH(int32_t addr, uint16_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::HalfWord) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { uint16_t* ptr = reinterpret_cast(addr); *ptr = value; return 0; } else { return 1; } } uint8_t Simulator::ReadBU(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); uint8_t* ptr = reinterpret_cast(addr); return *ptr; } int8_t Simulator::ReadB(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); int8_t* ptr = reinterpret_cast(addr); return *ptr; } uint8_t Simulator::ReadExBU(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::Byte); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); uint8_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::WriteB(int32_t addr, uint8_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); uint8_t* ptr = reinterpret_cast(addr); *ptr = value; } void Simulator::WriteB(int32_t addr, int8_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); int8_t* ptr = reinterpret_cast(addr); *ptr = value; } int Simulator::WriteExB(int32_t addr, uint8_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::Byte) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { uint8_t* ptr = reinterpret_cast(addr); *ptr = value; return 0; } else { return 1; } } int32_t* Simulator::ReadDW(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); int32_t* ptr = reinterpret_cast(addr); return ptr; } int32_t* Simulator::ReadExDW(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::DoubleWord); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); int32_t* ptr = reinterpret_cast(addr); return ptr; } void Simulator::WriteDW(int32_t addr, int32_t value1, int32_t value2) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); int32_t* ptr = reinterpret_cast(addr); *ptr++ = value1; *ptr = value2; } int Simulator::WriteExDW(int32_t addr, int32_t value1, int32_t value2) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::DoubleWord) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { intptr_t* ptr = reinterpret_cast(addr); *ptr++ = value1; *ptr = value2; return 0; } else { return 1; } } // Returns the limit of the stack area to enable checking for stack overflows. uintptr_t Simulator::StackLimit(uintptr_t c_limit) const { // The simulator uses a separate JS stack. If we have exhausted the C stack, // we also drop down the JS limit to reflect the exhaustion on the JS stack. if (GetCurrentStackPosition() < c_limit) { return reinterpret_cast(get_sp()); } // Otherwise the limit is the JS stack. Leave a safety margin of 1024 bytes // to prevent overrunning the stack when pushing values. return reinterpret_cast(stack_) + 1024; } // Unsupported instructions use Format to print an error and stop execution. void Simulator::Format(Instruction* instr, const char* format) { PrintF("Simulator found unsupported instruction:\n 0x%08" V8PRIxPTR ": %s\n", reinterpret_cast(instr), format); UNIMPLEMENTED(); } // Checks if the current instruction should be executed based on its // condition bits. bool Simulator::ConditionallyExecute(Instruction* instr) { switch (instr->ConditionField()) { case eq: return z_flag_; case ne: return !z_flag_; case cs: return c_flag_; case cc: return !c_flag_; case mi: return n_flag_; case pl: return !n_flag_; case vs: return v_flag_; case vc: return !v_flag_; case hi: return c_flag_ && !z_flag_; case ls: return !c_flag_ || z_flag_; case ge: return n_flag_ == v_flag_; case lt: return n_flag_ != v_flag_; case gt: return !z_flag_ && (n_flag_ == v_flag_); case le: return z_flag_ || (n_flag_ != v_flag_); case al: return true; default: UNREACHABLE(); } return false; } // Calculate and set the Negative and Zero flags. void Simulator::SetNZFlags(int32_t val) { n_flag_ = (val < 0); z_flag_ = (val == 0); } // Set the Carry flag. void Simulator::SetCFlag(bool val) { c_flag_ = val; } // Set the oVerflow flag. void Simulator::SetVFlag(bool val) { v_flag_ = val; } // Calculate C flag value for additions. bool Simulator::CarryFrom(int32_t left, int32_t right, int32_t carry) { uint32_t uleft = static_cast(left); uint32_t uright = static_cast(right); uint32_t urest = 0xFFFFFFFFU - uleft; return (uright > urest) || (carry && (((uright + 1) > urest) || (uright > (urest - 1)))); } // Calculate C flag value for subtractions. bool Simulator::BorrowFrom(int32_t left, int32_t right, int32_t carry) { uint32_t uleft = static_cast(left); uint32_t uright = static_cast(right); return (uright > uleft) || (!carry && (((uright + 1) > uleft) || (uright > (uleft - 1)))); } // Calculate V flag value for additions and subtractions. bool Simulator::OverflowFrom(int32_t alu_out, int32_t left, int32_t right, bool addition) { bool overflow; if (addition) { // operands have the same sign overflow = ((left >= 0 && right >= 0) || (left < 0 && right < 0)) // and operands and result have different sign && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); } else { // operands have different signs overflow = ((left < 0 && right >= 0) || (left >= 0 && right < 0)) // and first operand and result have different signs && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); } return overflow; } // Support for VFP comparisons. void Simulator::Compute_FPSCR_Flags(float val1, float val2) { if (std::isnan(val1) || std::isnan(val2)) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = true; // All non-NaN cases. } else if (val1 == val2) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = true; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } else if (val1 < val2) { n_flag_FPSCR_ = true; z_flag_FPSCR_ = false; c_flag_FPSCR_ = false; v_flag_FPSCR_ = false; } else { // Case when (val1 > val2). n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } } void Simulator::Compute_FPSCR_Flags(double val1, double val2) { if (std::isnan(val1) || std::isnan(val2)) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = true; // All non-NaN cases. } else if (val1 == val2) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = true; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } else if (val1 < val2) { n_flag_FPSCR_ = true; z_flag_FPSCR_ = false; c_flag_FPSCR_ = false; v_flag_FPSCR_ = false; } else { // Case when (val1 > val2). n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } } void Simulator::Copy_FPSCR_to_APSR() { n_flag_ = n_flag_FPSCR_; z_flag_ = z_flag_FPSCR_; c_flag_ = c_flag_FPSCR_; v_flag_ = v_flag_FPSCR_; } // Addressing Mode 1 - Data-processing operands: // Get the value based on the shifter_operand with register. int32_t Simulator::GetShiftRm(Instruction* instr, bool* carry_out) { ShiftOp shift = instr->ShiftField(); int shift_amount = instr->ShiftAmountValue(); int32_t result = get_register(instr->RmValue()); if (instr->Bit(4) == 0) { // by immediate if ((shift == ROR) && (shift_amount == 0)) { UNIMPLEMENTED(); return result; } else if (((shift == LSR) || (shift == ASR)) && (shift_amount == 0)) { shift_amount = 32; } switch (shift) { case ASR: { if (shift_amount == 0) { if (result < 0) { result = 0xFFFFFFFF; *carry_out = true; } else { result = 0; *carry_out = false; } } else { result >>= (shift_amount - 1); *carry_out = (result & 1) == 1; result >>= 1; } break; } case LSL: { if (shift_amount == 0) { *carry_out = c_flag_; } else { result <<= (shift_amount - 1); *carry_out = (result < 0); result <<= 1; } break; } case LSR: { if (shift_amount == 0) { result = 0; *carry_out = c_flag_; } else { uint32_t uresult = static_cast(result); uresult >>= (shift_amount - 1); *carry_out = (uresult & 1) == 1; uresult >>= 1; result = static_cast(uresult); } break; } case ROR: { if (shift_amount == 0) { *carry_out = c_flag_; } else { uint32_t left = static_cast(result) >> shift_amount; uint32_t right = static_cast(result) << (32 - shift_amount); result = right | left; *carry_out = (static_cast(result) >> 31) != 0; } break; } default: { UNREACHABLE(); break; } } } else { // by register int rs = instr->RsValue(); shift_amount = get_register(rs) & 0xFF; switch (shift) { case ASR: { if (shift_amount == 0) { *carry_out = c_flag_; } else if (shift_amount < 32) { result >>= (shift_amount - 1); *carry_out = (result & 1) == 1; result >>= 1; } else { DCHECK_GE(shift_amount, 32); if (result < 0) { *carry_out = true; result = 0xFFFFFFFF; } else { *carry_out = false; result = 0; } } break; } case LSL: { if (shift_amount == 0) { *carry_out = c_flag_; } else if (shift_amount < 32) { result <<= (shift_amount - 1); *carry_out = (result < 0); result <<= 1; } else if (shift_amount == 32) { *carry_out = (result & 1) == 1; result = 0; } else { DCHECK_GT(shift_amount, 32); *carry_out = false; result = 0; } break; } case LSR: { if (shift_amount == 0) { *carry_out = c_flag_; } else if (shift_amount < 32) { uint32_t uresult = static_cast(result); uresult >>= (shift_amount - 1); *carry_out = (uresult & 1) == 1; uresult >>= 1; result = static_cast(uresult); } else if (shift_amount == 32) { *carry_out = (result < 0); result = 0; } else { *carry_out = false; result = 0; } break; } case ROR: { if (shift_amount == 0) { *carry_out = c_flag_; } else { uint32_t left = static_cast(result) >> shift_amount; uint32_t right = static_cast(result) << (32 - shift_amount); result = right | left; *carry_out = (static_cast(result) >> 31) != 0; } break; } default: { UNREACHABLE(); break; } } } return result; } // Addressing Mode 1 - Data-processing operands: // Get the value based on the shifter_operand with immediate. int32_t Simulator::GetImm(Instruction* instr, bool* carry_out) { int rotate = instr->RotateValue() * 2; int immed8 = instr->Immed8Value(); int imm = base::bits::RotateRight32(immed8, rotate); *carry_out = (rotate == 0) ? c_flag_ : (imm < 0); return imm; } static int count_bits(int bit_vector) { int count = 0; while (bit_vector != 0) { if ((bit_vector & 1) != 0) { count++; } bit_vector >>= 1; } return count; } int32_t Simulator::ProcessPU(Instruction* instr, int num_regs, int reg_size, intptr_t* start_address, intptr_t* end_address) { int rn = instr->RnValue(); int32_t rn_val = get_register(rn); switch (instr->PUField()) { case da_x: { UNIMPLEMENTED(); break; } case ia_x: { *start_address = rn_val; *end_address = rn_val + (num_regs * reg_size) - reg_size; rn_val = rn_val + (num_regs * reg_size); break; } case db_x: { *start_address = rn_val - (num_regs * reg_size); *end_address = rn_val - reg_size; rn_val = *start_address; break; } case ib_x: { *start_address = rn_val + reg_size; *end_address = rn_val + (num_regs * reg_size); rn_val = *end_address; break; } default: { UNREACHABLE(); break; } } return rn_val; } // Addressing Mode 4 - Load and Store Multiple void Simulator::HandleRList(Instruction* instr, bool load) { int rlist = instr->RlistValue(); int num_regs = count_bits(rlist); intptr_t start_address = 0; intptr_t end_address = 0; int32_t rn_val = ProcessPU(instr, num_regs, kPointerSize, &start_address, &end_address); intptr_t* address = reinterpret_cast(start_address); // Catch null pointers a little earlier. DCHECK(start_address > 8191 || start_address < 0); int reg = 0; while (rlist != 0) { if ((rlist & 1) != 0) { if (load) { set_register(reg, *address); } else { *address = get_register(reg); } address += 1; } reg++; rlist >>= 1; } DCHECK(end_address == ((intptr_t)address) - 4); if (instr->HasW()) { set_register(instr->RnValue(), rn_val); } } // Addressing Mode 6 - Load and Store Multiple Coprocessor registers. void Simulator::HandleVList(Instruction* instr) { VFPRegPrecision precision = (instr->SzValue() == 0) ? kSinglePrecision : kDoublePrecision; int operand_size = (precision == kSinglePrecision) ? 4 : 8; bool load = (instr->VLValue() == 0x1); int vd; int num_regs; vd = instr->VFPDRegValue(precision); if (precision == kSinglePrecision) { num_regs = instr->Immed8Value(); } else { num_regs = instr->Immed8Value() / 2; } intptr_t start_address = 0; intptr_t end_address = 0; int32_t rn_val = ProcessPU(instr, num_regs, operand_size, &start_address, &end_address); intptr_t* address = reinterpret_cast(start_address); for (int reg = vd; reg < vd + num_regs; reg++) { if (precision == kSinglePrecision) { if (load) { set_s_register_from_sinteger(reg, ReadW(reinterpret_cast(address))); } else { WriteW(reinterpret_cast(address), get_sinteger_from_s_register(reg)); } address += 1; } else { if (load) { int32_t data[] = {ReadW(reinterpret_cast(address)), ReadW(reinterpret_cast(address + 1))}; set_d_register(reg, reinterpret_cast(data)); } else { uint32_t data[2]; get_d_register(reg, data); WriteW(reinterpret_cast(address), data[0]); WriteW(reinterpret_cast(address + 1), data[1]); } address += 2; } } DCHECK(reinterpret_cast(address) - operand_size == end_address); if (instr->HasW()) { set_register(instr->RnValue(), rn_val); } } // Calls into the V8 runtime are based on this very simple interface. // 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 // 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, int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7, int32_t arg8); // These prototypes handle the four types of FP calls. typedef int64_t (*SimulatorRuntimeCompareCall)(double darg0, double darg1); typedef double (*SimulatorRuntimeFPFPCall)(double darg0, double darg1); typedef double (*SimulatorRuntimeFPCall)(double darg0); typedef double (*SimulatorRuntimeFPIntCall)(double darg0, int32_t arg0); // This signature supports direct call in to API function native callback // (refer to InvocationCallback in v8.h). typedef void (*SimulatorRuntimeDirectApiCall)(int32_t arg0); typedef void (*SimulatorRuntimeProfilingApiCall)(int32_t arg0, void* arg1); // This signature supports direct call to accessor getter callback. typedef void (*SimulatorRuntimeDirectGetterCall)(int32_t arg0, int32_t arg1); typedef void (*SimulatorRuntimeProfilingGetterCall)( int32_t arg0, int32_t arg1, void* arg2); // Software interrupt instructions are used by the simulator to call into the // C-based V8 runtime. void Simulator::SoftwareInterrupt(Instruction* instr) { int svc = instr->SvcValue(); switch (svc) { case kCallRtRedirected: { // Check if stack is aligned. Error if not aligned is reported below to // include information on the function called. bool stack_aligned = (get_register(sp) & (::v8::internal::FLAG_sim_stack_alignment - 1)) == 0; Redirection* redirection = Redirection::FromInstruction(instr); int32_t arg0 = get_register(r0); int32_t arg1 = get_register(r1); int32_t arg2 = get_register(r2); int32_t arg3 = get_register(r3); int32_t* stack_pointer = reinterpret_cast(get_register(sp)); int32_t arg4 = stack_pointer[0]; int32_t arg5 = stack_pointer[1]; int32_t arg6 = stack_pointer[2]; int32_t arg7 = stack_pointer[3]; int32_t arg8 = stack_pointer[4]; STATIC_ASSERT(kMaxCParameters == 9); bool fp_call = (redirection->type() == ExternalReference::BUILTIN_FP_FP_CALL) || (redirection->type() == ExternalReference::BUILTIN_COMPARE_CALL) || (redirection->type() == ExternalReference::BUILTIN_FP_CALL) || (redirection->type() == ExternalReference::BUILTIN_FP_INT_CALL); // 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_lr = get_register(lr); intptr_t external = reinterpret_cast(redirection->external_function()); if (fp_call) { double dval0, dval1; // one or two double parameters int32_t ival; // zero or one integer parameters int64_t iresult = 0; // integer return value double dresult = 0; // double return value GetFpArgs(&dval0, &dval1, &ival); if (::v8::internal::FLAG_trace_sim || !stack_aligned) { SimulatorRuntimeCall generic_target = reinterpret_cast(external); switch (redirection->type()) { case ExternalReference::BUILTIN_FP_FP_CALL: case ExternalReference::BUILTIN_COMPARE_CALL: PrintF("Call to host function at %p with args %f, %f", reinterpret_cast(FUNCTION_ADDR(generic_target)), dval0, dval1); break; case ExternalReference::BUILTIN_FP_CALL: PrintF("Call to host function at %p with arg %f", reinterpret_cast(FUNCTION_ADDR(generic_target)), dval0); break; case ExternalReference::BUILTIN_FP_INT_CALL: PrintF("Call to host function at %p with args %f, %d", reinterpret_cast(FUNCTION_ADDR(generic_target)), dval0, ival); break; default: UNREACHABLE(); break; } if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); switch (redirection->type()) { case ExternalReference::BUILTIN_COMPARE_CALL: { SimulatorRuntimeCompareCall target = reinterpret_cast(external); iresult = target(dval0, dval1); set_register(r0, static_cast(iresult)); set_register(r1, static_cast(iresult >> 32)); break; } case ExternalReference::BUILTIN_FP_FP_CALL: { SimulatorRuntimeFPFPCall target = reinterpret_cast(external); dresult = target(dval0, dval1); SetFpResult(dresult); break; } case ExternalReference::BUILTIN_FP_CALL: { SimulatorRuntimeFPCall target = reinterpret_cast(external); dresult = target(dval0); SetFpResult(dresult); break; } case ExternalReference::BUILTIN_FP_INT_CALL: { SimulatorRuntimeFPIntCall target = reinterpret_cast(external); dresult = target(dval0, ival); SetFpResult(dresult); break; } default: UNREACHABLE(); break; } if (::v8::internal::FLAG_trace_sim || !stack_aligned) { switch (redirection->type()) { case ExternalReference::BUILTIN_COMPARE_CALL: PrintF("Returned %08x\n", static_cast(iresult)); break; case ExternalReference::BUILTIN_FP_FP_CALL: case ExternalReference::BUILTIN_FP_CALL: case ExternalReference::BUILTIN_FP_INT_CALL: PrintF("Returned %f\n", dresult); break; default: UNREACHABLE(); break; } } } else if (redirection->type() == ExternalReference::DIRECT_API_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x", reinterpret_cast(external), arg0); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeDirectApiCall target = reinterpret_cast(external); target(arg0); } else if ( redirection->type() == ExternalReference::PROFILING_API_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x %08x", reinterpret_cast(external), arg0, arg1); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeProfilingApiCall target = reinterpret_cast(external); target(arg0, Redirection::ReverseRedirection(arg1)); } else if ( redirection->type() == ExternalReference::DIRECT_GETTER_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x %08x", reinterpret_cast(external), arg0, arg1); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeDirectGetterCall target = reinterpret_cast(external); target(arg0, arg1); } else if ( redirection->type() == ExternalReference::PROFILING_GETTER_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x %08x %08x", reinterpret_cast(external), arg0, arg1, arg2); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeProfilingGetterCall target = reinterpret_cast( external); target(arg0, arg1, Redirection::ReverseRedirection(arg2)); } else { // builtin call. DCHECK(redirection->type() == ExternalReference::BUILTIN_CALL || redirection->type() == ExternalReference::BUILTIN_CALL_PAIR); SimulatorRuntimeCall target = reinterpret_cast(external); if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF( "Call to host function at %p " "args %08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x", reinterpret_cast(FUNCTION_ADDR(target)), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); int32_t lo_res = static_cast(result); int32_t hi_res = static_cast(result >> 32); if (::v8::internal::FLAG_trace_sim) { PrintF("Returned %08x\n", lo_res); } set_register(r0, lo_res); set_register(r1, hi_res); } set_register(lr, saved_lr); set_pc(get_register(lr)); break; } case kBreakpoint: { ArmDebugger dbg(this); dbg.Debug(); break; } // stop uses all codes greater than 1 << 23. default: { if (svc >= (1 << 23)) { uint32_t code = svc & kStopCodeMask; if (isWatchedStop(code)) { IncreaseStopCounter(code); } // Stop if it is enabled, otherwise go on jumping over the stop // and the message address. if (isEnabledStop(code)) { ArmDebugger dbg(this); dbg.Stop(instr); } } else { // This is not a valid svc code. UNREACHABLE(); break; } } } } float Simulator::canonicalizeNaN(float value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr uint32_t kDefaultNaN = 0x7FC00000u; if (FPSCR_default_NaN_mode_ && std::isnan(value)) { value = bit_cast(kDefaultNaN); } return value; } Float32 Simulator::canonicalizeNaN(Float32 value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr Float32 kDefaultNaN = Float32::FromBits(0x7FC00000u); return FPSCR_default_NaN_mode_ && value.is_nan() ? kDefaultNaN : value; } double Simulator::canonicalizeNaN(double value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr uint64_t kDefaultNaN = uint64_t{0x7FF8000000000000}; if (FPSCR_default_NaN_mode_ && std::isnan(value)) { value = bit_cast(kDefaultNaN); } return value; } Float64 Simulator::canonicalizeNaN(Float64 value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr Float64 kDefaultNaN = Float64::FromBits(uint64_t{0x7FF8000000000000}); return FPSCR_default_NaN_mode_ && value.is_nan() ? kDefaultNaN : value; } // Stop helper functions. bool Simulator::isStopInstruction(Instruction* instr) { return (instr->Bits(27, 24) == 0xF) && (instr->SvcValue() >= kStopCode); } bool Simulator::isWatchedStop(uint32_t code) { DCHECK_LE(code, kMaxStopCode); return code < kNumOfWatchedStops; } bool Simulator::isEnabledStop(uint32_t code) { DCHECK_LE(code, kMaxStopCode); // Unwatched stops are always enabled. return !isWatchedStop(code) || !(watched_stops_[code].count & kStopDisabledBit); } void Simulator::EnableStop(uint32_t code) { DCHECK(isWatchedStop(code)); if (!isEnabledStop(code)) { watched_stops_[code].count &= ~kStopDisabledBit; } } void Simulator::DisableStop(uint32_t code) { DCHECK(isWatchedStop(code)); if (isEnabledStop(code)) { watched_stops_[code].count |= kStopDisabledBit; } } void Simulator::IncreaseStopCounter(uint32_t code) { DCHECK_LE(code, kMaxStopCode); DCHECK(isWatchedStop(code)); if ((watched_stops_[code].count & ~(1 << 31)) == 0x7FFFFFFF) { PrintF("Stop counter for code %i has overflowed.\n" "Enabling this code and reseting the counter to 0.\n", code); watched_stops_[code].count = 0; EnableStop(code); } else { watched_stops_[code].count++; } } // Print a stop status. void Simulator::PrintStopInfo(uint32_t code) { DCHECK_LE(code, kMaxStopCode); if (!isWatchedStop(code)) { PrintF("Stop not watched."); } else { const char* state = isEnabledStop(code) ? "Enabled" : "Disabled"; int32_t count = watched_stops_[code].count & ~kStopDisabledBit; // Don't print the state of unused breakpoints. if (count != 0) { if (watched_stops_[code].desc) { PrintF("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n", code, code, state, count, watched_stops_[code].desc); } else { PrintF("stop %i - 0x%x: \t%s, \tcounter = %i\n", code, code, state, count); } } } } // Handle execution based on instruction types. // Instruction types 0 and 1 are both rolled into one function because they // only differ in the handling of the shifter_operand. void Simulator::DecodeType01(Instruction* instr) { int type = instr->TypeValue(); if ((type == 0) && instr->IsSpecialType0()) { // multiply instruction or extra loads and stores if (instr->Bits(7, 4) == 9) { if (instr->Bit(24) == 0) { // Raw field decoding here. Multiply instructions have their Rd in // funny places. int rn = instr->RnValue(); int rm = instr->RmValue(); int rs = instr->RsValue(); int32_t rs_val = get_register(rs); int32_t rm_val = get_register(rm); if (instr->Bit(23) == 0) { if (instr->Bit(21) == 0) { // The MUL instruction description (A 4.1.33) refers to Rd as being // the destination for the operation, but it confusingly uses the // Rn field to encode it. // Format(instr, "mul'cond's 'rn, 'rm, 'rs"); int rd = rn; // Remap the rn field to the Rd register. int32_t alu_out = rm_val * rs_val; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); } } else { int rd = instr->RdValue(); int32_t acc_value = get_register(rd); if (instr->Bit(22) == 0) { // The MLA instruction description (A 4.1.28) refers to the order // of registers as "Rd, Rm, Rs, Rn". But confusingly it uses the // Rn field to encode the Rd register and the Rd field to encode // the Rn register. // Format(instr, "mla'cond's 'rn, 'rm, 'rs, 'rd"); int32_t mul_out = rm_val * rs_val; int32_t result = acc_value + mul_out; set_register(rn, result); } else { // Format(instr, "mls'cond's 'rn, 'rm, 'rs, 'rd"); int32_t mul_out = rm_val * rs_val; int32_t result = acc_value - mul_out; set_register(rn, result); } } } else { // The signed/long multiply instructions use the terms RdHi and RdLo // when referring to the target registers. They are mapped to the Rn // and Rd fields as follows: // RdLo == Rd // RdHi == Rn (This is confusingly stored in variable rd here // because the mul instruction from above uses the // Rn field to encode the Rd register. Good luck figuring // this out without reading the ARM instruction manual // at a very detailed level.) // Format(instr, "'um'al'cond's 'rd, 'rn, 'rs, 'rm"); int rd_hi = rn; // Remap the rn field to the RdHi register. int rd_lo = instr->RdValue(); int32_t hi_res = 0; int32_t lo_res = 0; if (instr->Bit(22) == 1) { int64_t left_op = static_cast(rm_val); int64_t right_op = static_cast(rs_val); uint64_t result = left_op * right_op; hi_res = static_cast(result >> 32); lo_res = static_cast(result & 0xFFFFFFFF); } else { // unsigned multiply uint64_t left_op = static_cast(rm_val); uint64_t right_op = static_cast(rs_val); uint64_t result = left_op * right_op; hi_res = static_cast(result >> 32); lo_res = static_cast(result & 0xFFFFFFFF); } set_register(rd_lo, lo_res); set_register(rd_hi, hi_res); if (instr->HasS()) { UNIMPLEMENTED(); } } } else { if (instr->Bits(24, 23) == 3) { if (instr->Bit(20) == 1) { // ldrex int rt = instr->RtValue(); int rn = instr->RnValue(); int32_t addr = get_register(rn); switch (instr->Bits(22, 21)) { case 0: { // Format(instr, "ldrex'cond 'rt, ['rn]"); int value = ReadExW(addr); set_register(rt, value); break; } case 1: { // Format(instr, "ldrexd'cond 'rt, ['rn]"); int* rn_data = ReadExDW(addr); set_dw_register(rt, rn_data); break; } case 2: { // Format(instr, "ldrexb'cond 'rt, ['rn]"); uint8_t value = ReadExBU(addr); set_register(rt, value); break; } case 3: { // Format(instr, "ldrexh'cond 'rt, ['rn]"); uint16_t value = ReadExHU(addr); set_register(rt, value); break; } default: UNREACHABLE(); break; } } else { // The instruction is documented as strex rd, rt, [rn], but the // "rt" register is using the rm bits. int rd = instr->RdValue(); int rt = instr->RmValue(); int rn = instr->RnValue(); DCHECK_NE(rd, rn); DCHECK_NE(rd, rt); int32_t addr = get_register(rn); switch (instr->Bits(22, 21)) { case 0: { // Format(instr, "strex'cond 'rd, 'rm, ['rn]"); int value = get_register(rt); int status = WriteExW(addr, value); set_register(rd, status); break; } case 1: { // Format(instr, "strexd'cond 'rd, 'rm, ['rn]"); DCHECK_EQ(rt % 2, 0); int32_t value1 = get_register(rt); int32_t value2 = get_register(rt + 1); int status = WriteExDW(addr, value1, value2); set_register(rd, status); break; } case 2: { // Format(instr, "strexb'cond 'rd, 'rm, ['rn]"); uint8_t value = get_register(rt); int status = WriteExB(addr, value); set_register(rd, status); break; } case 3: { // Format(instr, "strexh'cond 'rd, 'rm, ['rn]"); uint16_t value = get_register(rt); int status = WriteExH(addr, value); set_register(rd, status); break; } default: UNREACHABLE(); break; } } } else { UNIMPLEMENTED(); // Not used by V8. } } } else { // extra load/store instructions int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); int32_t addr = 0; if (instr->Bit(22) == 0) { int rm = instr->RmValue(); int32_t rm_val = get_register(rm); switch (instr->PUField()) { case da_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], -'rm"); DCHECK(!instr->HasW()); addr = rn_val; rn_val -= rm_val; set_register(rn, rn_val); break; } case ia_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], +'rm"); DCHECK(!instr->HasW()); addr = rn_val; rn_val += rm_val; set_register(rn, rn_val); break; } case db_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, -'rm]'w"); rn_val -= rm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } case ib_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, +'rm]'w"); rn_val += rm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } default: { // The PU field is a 2-bit field. UNREACHABLE(); break; } } } else { int32_t imm_val = (instr->ImmedHValue() << 4) | instr->ImmedLValue(); switch (instr->PUField()) { case da_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], #-'off8"); DCHECK(!instr->HasW()); addr = rn_val; rn_val -= imm_val; set_register(rn, rn_val); break; } case ia_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], #+'off8"); DCHECK(!instr->HasW()); addr = rn_val; rn_val += imm_val; set_register(rn, rn_val); break; } case db_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, #-'off8]'w"); rn_val -= imm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } case ib_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, #+'off8]'w"); rn_val += imm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } default: { // The PU field is a 2-bit field. UNREACHABLE(); break; } } } if (((instr->Bits(7, 4) & 0xD) == 0xD) && (instr->Bit(20) == 0)) { DCHECK_EQ(rd % 2, 0); if (instr->HasH()) { // The strd instruction. int32_t value1 = get_register(rd); int32_t value2 = get_register(rd+1); WriteDW(addr, value1, value2); } else { // The ldrd instruction. int* rn_data = ReadDW(addr); set_dw_register(rd, rn_data); } } else if (instr->HasH()) { if (instr->HasSign()) { if (instr->HasL()) { int16_t val = ReadH(addr); set_register(rd, val); } else { int16_t val = get_register(rd); WriteH(addr, val); } } else { if (instr->HasL()) { uint16_t val = ReadHU(addr); set_register(rd, val); } else { uint16_t val = get_register(rd); WriteH(addr, val); } } } else { // signed byte loads DCHECK(instr->HasSign()); DCHECK(instr->HasL()); int8_t val = ReadB(addr); set_register(rd, val); } return; } } else if ((type == 0) && instr->IsMiscType0()) { if ((instr->Bits(27, 23) == 2) && (instr->Bits(21, 20) == 2) && (instr->Bits(15, 4) == 0xF00)) { // MSR int rm = instr->RmValue(); DCHECK_NE(pc, rm); // UNPREDICTABLE SRegisterFieldMask sreg_and_mask = instr->BitField(22, 22) | instr->BitField(19, 16); SetSpecialRegister(sreg_and_mask, get_register(rm)); } else if ((instr->Bits(27, 23) == 2) && (instr->Bits(21, 20) == 0) && (instr->Bits(11, 0) == 0)) { // MRS int rd = instr->RdValue(); DCHECK_NE(pc, rd); // UNPREDICTABLE SRegister sreg = static_cast(instr->BitField(22, 22)); set_register(rd, GetFromSpecialRegister(sreg)); } else if (instr->Bits(22, 21) == 1) { int rm = instr->RmValue(); switch (instr->BitField(7, 4)) { case BX: set_pc(get_register(rm)); break; case BLX: { uint32_t old_pc = get_pc(); set_pc(get_register(rm)); set_register(lr, old_pc + kInstrSize); break; } case BKPT: { ArmDebugger dbg(this); PrintF("Simulator hit BKPT.\n"); dbg.Debug(); break; } default: UNIMPLEMENTED(); } } else if (instr->Bits(22, 21) == 3) { int rm = instr->RmValue(); int rd = instr->RdValue(); switch (instr->BitField(7, 4)) { case CLZ: { uint32_t bits = get_register(rm); int leading_zeros = 0; if (bits == 0) { leading_zeros = 32; } else { while ((bits & 0x80000000u) == 0) { bits <<= 1; leading_zeros++; } } set_register(rd, leading_zeros); break; } default: UNIMPLEMENTED(); } } else { PrintF("%08x\n", instr->InstructionBits()); UNIMPLEMENTED(); } } else if ((type == 1) && instr->IsNopLikeType1()) { if (instr->BitField(7, 0) == 0) { // NOP. } else if (instr->BitField(7, 0) == 20) { // CSDB. } else { PrintF("%08x\n", instr->InstructionBits()); UNIMPLEMENTED(); } } else { int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); int32_t shifter_operand = 0; bool shifter_carry_out = 0; if (type == 0) { shifter_operand = GetShiftRm(instr, &shifter_carry_out); } else { DCHECK_EQ(instr->TypeValue(), 1); shifter_operand = GetImm(instr, &shifter_carry_out); } int32_t alu_out; switch (instr->OpcodeField()) { case AND: { // Format(instr, "and'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "and'cond's 'rd, 'rn, 'imm"); alu_out = rn_val & shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case EOR: { // Format(instr, "eor'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "eor'cond's 'rd, 'rn, 'imm"); alu_out = rn_val ^ shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case SUB: { // Format(instr, "sub'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "sub'cond's 'rd, 'rn, 'imm"); alu_out = rn_val - shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(!BorrowFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); } break; } case RSB: { // Format(instr, "rsb'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "rsb'cond's 'rd, 'rn, 'imm"); alu_out = shifter_operand - rn_val; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(!BorrowFrom(shifter_operand, rn_val)); SetVFlag(OverflowFrom(alu_out, shifter_operand, rn_val, false)); } break; } case ADD: { // Format(instr, "add'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "add'cond's 'rd, 'rn, 'imm"); alu_out = rn_val + shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(CarryFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true)); } break; } case ADC: { // Format(instr, "adc'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "adc'cond's 'rd, 'rn, 'imm"); alu_out = rn_val + shifter_operand + GetCarry(); set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(CarryFrom(rn_val, shifter_operand, GetCarry())); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true)); } break; } case SBC: { // Format(instr, "sbc'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "sbc'cond's 'rd, 'rn, 'imm"); alu_out = (rn_val - shifter_operand) - (GetCarry() ? 0 : 1); set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(!BorrowFrom(rn_val, shifter_operand, GetCarry())); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); } break; } case RSC: { Format(instr, "rsc'cond's 'rd, 'rn, 'shift_rm"); Format(instr, "rsc'cond's 'rd, 'rn, 'imm"); break; } case TST: { if (instr->HasS()) { // Format(instr, "tst'cond 'rn, 'shift_rm"); // Format(instr, "tst'cond 'rn, 'imm"); alu_out = rn_val & shifter_operand; SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } else { // Format(instr, "movw'cond 'rd, 'imm"). alu_out = instr->ImmedMovwMovtValue(); set_register(rd, alu_out); } break; } case TEQ: { if (instr->HasS()) { // Format(instr, "teq'cond 'rn, 'shift_rm"); // Format(instr, "teq'cond 'rn, 'imm"); alu_out = rn_val ^ shifter_operand; SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } else { // Other instructions matching this pattern are handled in the // miscellaneous instructions part above. UNREACHABLE(); } break; } case CMP: { if (instr->HasS()) { // Format(instr, "cmp'cond 'rn, 'shift_rm"); // Format(instr, "cmp'cond 'rn, 'imm"); alu_out = rn_val - shifter_operand; SetNZFlags(alu_out); SetCFlag(!BorrowFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); } else { // Format(instr, "movt'cond 'rd, 'imm"). alu_out = (get_register(rd) & 0xFFFF) | (instr->ImmedMovwMovtValue() << 16); set_register(rd, alu_out); } break; } case CMN: { if (instr->HasS()) { // Format(instr, "cmn'cond 'rn, 'shift_rm"); // Format(instr, "cmn'cond 'rn, 'imm"); alu_out = rn_val + shifter_operand; SetNZFlags(alu_out); SetCFlag(CarryFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true)); } else { // Other instructions matching this pattern are handled in the // miscellaneous instructions part above. UNREACHABLE(); } break; } case ORR: { // Format(instr, "orr'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "orr'cond's 'rd, 'rn, 'imm"); alu_out = rn_val | shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case MOV: { // Format(instr, "mov'cond's 'rd, 'shift_rm"); // Format(instr, "mov'cond's 'rd, 'imm"); alu_out = shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case BIC: { // Format(instr, "bic'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "bic'cond's 'rd, 'rn, 'imm"); alu_out = rn_val & ~shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case MVN: { // Format(instr, "mvn'cond's 'rd, 'shift_rm"); // Format(instr, "mvn'cond's 'rd, 'imm"); alu_out = ~shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } default: { UNREACHABLE(); break; } } } } void Simulator::DecodeType2(Instruction* instr) { int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); int32_t im_val = instr->Offset12Value(); int32_t addr = 0; switch (instr->PUField()) { case da_x: { // Format(instr, "'memop'cond'b 'rd, ['rn], #-'off12"); DCHECK(!instr->HasW()); addr = rn_val; rn_val -= im_val; set_register(rn, rn_val); break; } case ia_x: { // Format(instr, "'memop'cond'b 'rd, ['rn], #+'off12"); DCHECK(!instr->HasW()); addr = rn_val; rn_val += im_val; set_register(rn, rn_val); break; } case db_x: { // Format(instr, "'memop'cond'b 'rd, ['rn, #-'off12]'w"); rn_val -= im_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } case ib_x: { // Format(instr, "'memop'cond'b 'rd, ['rn, #+'off12]'w"); rn_val += im_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } default: { UNREACHABLE(); break; } } if (instr->HasB()) { if (instr->HasL()) { byte val = ReadBU(addr); set_register(rd, val); } else { byte val = get_register(rd); WriteB(addr, val); } } else { if (instr->HasL()) { set_register(rd, ReadW(addr)); } else { WriteW(addr, get_register(rd)); } } } void Simulator::DecodeType3(Instruction* instr) { int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); bool shifter_carry_out = 0; int32_t shifter_operand = GetShiftRm(instr, &shifter_carry_out); int32_t addr = 0; switch (instr->PUField()) { case da_x: { DCHECK(!instr->HasW()); Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm"); UNIMPLEMENTED(); break; } case ia_x: { if (instr->Bit(4) == 0) { // Memop. } else { if (instr->Bit(5) == 0) { switch (instr->Bits(22, 21)) { case 0: if (instr->Bit(20) == 0) { if (instr->Bit(6) == 0) { // Pkhbt. uint32_t rn_val = get_register(rn); uint32_t rm_val = get_register(instr->RmValue()); int32_t shift = instr->Bits(11, 7); rm_val <<= shift; set_register(rd, (rn_val & 0xFFFF) | (rm_val & 0xFFFF0000U)); } else { // Pkhtb. uint32_t rn_val = get_register(rn); int32_t rm_val = get_register(instr->RmValue()); int32_t shift = instr->Bits(11, 7); if (shift == 0) { shift = 32; } rm_val >>= shift; set_register(rd, (rn_val & 0xFFFF0000U) | (rm_val & 0xFFFF)); } } else { UNIMPLEMENTED(); } break; case 1: UNIMPLEMENTED(); break; case 2: UNIMPLEMENTED(); break; case 3: { // Usat. int32_t sat_pos = instr->Bits(20, 16); int32_t sat_val = (1 << sat_pos) - 1; int32_t shift = instr->Bits(11, 7); int32_t shift_type = instr->Bit(6); int32_t rm_val = get_register(instr->RmValue()); if (shift_type == 0) { // LSL rm_val <<= shift; } else { // ASR rm_val >>= shift; } // If saturation occurs, the Q flag should be set in the CPSR. // There is no Q flag yet, and no instruction (MRS) to read the // CPSR directly. if (rm_val > sat_val) { rm_val = sat_val; } else if (rm_val < 0) { rm_val = 0; } set_register(rd, rm_val); break; } } } else { switch (instr->Bits(22, 21)) { case 0: UNIMPLEMENTED(); break; case 1: if (instr->Bits(9, 6) == 1) { if (instr->Bit(20) == 0) { if (instr->Bits(19, 16) == 0xF) { // Sxtb. int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, static_cast(rm_val)); } else { // Sxtab. int32_t rn_val = get_register(rn); int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + static_cast(rm_val)); } } else { if (instr->Bits(19, 16) == 0xF) { // Sxth. int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, static_cast(rm_val)); } else { // Sxtah. int32_t rn_val = get_register(rn); int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + static_cast(rm_val)); } } } else if (instr->Bits(27, 16) == 0x6BF && instr->Bits(11, 4) == 0xF3) { // Rev. uint32_t rm_val = get_register(instr->RmValue()); set_register(rd, ByteReverse(rm_val)); } else { UNREACHABLE(); } break; case 2: if ((instr->Bit(20) == 0) && (instr->Bits(9, 6) == 1)) { if (instr->Bits(19, 16) == 0xF) { // Uxtb16. uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, (rm_val & 0xFF) | (rm_val & 0xFF0000)); } else { UNIMPLEMENTED(); } } else { UNIMPLEMENTED(); } break; case 3: if ((instr->Bits(9, 6) == 1)) { if (instr->Bit(20) == 0) { if (instr->Bits(19, 16) == 0xF) { // Uxtb. uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, (rm_val & 0xFF)); } else { // Uxtab. uint32_t rn_val = get_register(rn); uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + (rm_val & 0xFF)); } } else { if (instr->Bits(19, 16) == 0xF) { // Uxth. uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, (rm_val & 0xFFFF)); } else { // Uxtah. uint32_t rn_val = get_register(rn); uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + (rm_val & 0xFFFF)); } } } else { // PU == 0b01, BW == 0b11, Bits(9, 6) != 0b0001 if ((instr->Bits(20, 16) == 0x1F) && (instr->Bits(11, 4) == 0xF3)) { // Rbit. uint32_t rm_val = get_register(instr->RmValue()); set_register(rd, base::bits::ReverseBits(rm_val)); } else { UNIMPLEMENTED(); } } break; } } return; } break; } case db_x: { if (instr->Bits(22, 20) == 0x5) { if (instr->Bits(7, 4) == 0x1) { int rm = instr->RmValue(); int32_t rm_val = get_register(rm); int rs = instr->RsValue(); int32_t rs_val = get_register(rs); if (instr->Bits(15, 12) == 0xF) { // SMMUL (in V8 notation matching ARM ISA format) // Format(instr, "smmul'cond 'rn, 'rm, 'rs"); rn_val = base::bits::SignedMulHigh32(rm_val, rs_val); } else { // SMMLA (in V8 notation matching ARM ISA format) // Format(instr, "smmla'cond 'rn, 'rm, 'rs, 'rd"); int rd = instr->RdValue(); int32_t rd_val = get_register(rd); rn_val = base::bits::SignedMulHighAndAdd32(rm_val, rs_val, rd_val); } set_register(rn, rn_val); return; } } if (instr->Bits(5, 4) == 0x1) { if ((instr->Bit(22) == 0x0) && (instr->Bit(20) == 0x1)) { // (s/u)div (in V8 notation matching ARM ISA format) rn = rm/rs // Format(instr, "'(s/u)div'cond'b 'rn, 'rm, 'rs); int rm = instr->RmValue(); int32_t rm_val = get_register(rm); int rs = instr->RsValue(); int32_t rs_val = get_register(rs); int32_t ret_val = 0; // udiv if (instr->Bit(21) == 0x1) { ret_val = bit_cast(base::bits::UnsignedDiv32( bit_cast(rm_val), bit_cast(rs_val))); } else { ret_val = base::bits::SignedDiv32(rm_val, rs_val); } set_register(rn, ret_val); return; } } // Format(instr, "'memop'cond'b 'rd, ['rn, -'shift_rm]'w"); addr = rn_val - shifter_operand; if (instr->HasW()) { set_register(rn, addr); } break; } case ib_x: { if (instr->HasW() && (instr->Bits(6, 4) == 0x5)) { uint32_t widthminus1 = static_cast(instr->Bits(20, 16)); uint32_t lsbit = static_cast(instr->Bits(11, 7)); uint32_t msbit = widthminus1 + lsbit; if (msbit <= 31) { if (instr->Bit(22)) { // ubfx - unsigned bitfield extract. uint32_t rm_val = static_cast(get_register(instr->RmValue())); uint32_t extr_val = rm_val << (31 - msbit); extr_val = extr_val >> (31 - widthminus1); set_register(instr->RdValue(), extr_val); } else { // sbfx - signed bitfield extract. int32_t rm_val = get_register(instr->RmValue()); int32_t extr_val = rm_val << (31 - msbit); extr_val = extr_val >> (31 - widthminus1); set_register(instr->RdValue(), extr_val); } } else { UNREACHABLE(); } return; } else if (!instr->HasW() && (instr->Bits(6, 4) == 0x1)) { uint32_t lsbit = static_cast(instr->Bits(11, 7)); uint32_t msbit = static_cast(instr->Bits(20, 16)); if (msbit >= lsbit) { // bfc or bfi - bitfield clear/insert. uint32_t rd_val = static_cast(get_register(instr->RdValue())); uint32_t bitcount = msbit - lsbit + 1; uint32_t mask = 0xFFFFFFFFu >> (32 - bitcount); rd_val &= ~(mask << lsbit); if (instr->RmValue() != 15) { // bfi - bitfield insert. uint32_t rm_val = static_cast(get_register(instr->RmValue())); rm_val &= mask; rd_val |= rm_val << lsbit; } set_register(instr->RdValue(), rd_val); } else { UNREACHABLE(); } return; } else { // Format(instr, "'memop'cond'b 'rd, ['rn, +'shift_rm]'w"); addr = rn_val + shifter_operand; if (instr->HasW()) { set_register(rn, addr); } } break; } default: { UNREACHABLE(); break; } } if (instr->HasB()) { if (instr->HasL()) { uint8_t byte = ReadB(addr); set_register(rd, byte); } else { uint8_t byte = get_register(rd); WriteB(addr, byte); } } else { if (instr->HasL()) { set_register(rd, ReadW(addr)); } else { WriteW(addr, get_register(rd)); } } } void Simulator::DecodeType4(Instruction* instr) { DCHECK_EQ(instr->Bit(22), 0); // only allowed to be set in privileged mode if (instr->HasL()) { // Format(instr, "ldm'cond'pu 'rn'w, 'rlist"); HandleRList(instr, true); } else { // Format(instr, "stm'cond'pu 'rn'w, 'rlist"); HandleRList(instr, false); } } void Simulator::DecodeType5(Instruction* instr) { // Format(instr, "b'l'cond 'target"); int off = (instr->SImmed24Value() << 2); intptr_t pc_address = get_pc(); if (instr->HasLink()) { set_register(lr, pc_address + kInstrSize); } int pc_reg = get_register(pc); set_pc(pc_reg + off); } void Simulator::DecodeType6(Instruction* instr) { DecodeType6CoprocessorIns(instr); } void Simulator::DecodeType7(Instruction* instr) { if (instr->Bit(24) == 1) { SoftwareInterrupt(instr); } else { switch (instr->CoprocessorValue()) { case 10: // Fall through. case 11: DecodeTypeVFP(instr); break; case 15: DecodeTypeCP15(instr); break; default: UNIMPLEMENTED(); } } } // void Simulator::DecodeTypeVFP(Instruction* instr) // The Following ARMv7 VFPv instructions are currently supported. // vmov :Sn = Rt // vmov :Rt = Sn // vcvt: Dd = Sm // vcvt: Sd = Dm // vcvt.f64.s32 Dd, Dd, # // Dd = vabs(Dm) // Sd = vabs(Sm) // Dd = vneg(Dm) // Sd = vneg(Sm) // Dd = vadd(Dn, Dm) // Sd = vadd(Sn, Sm) // Dd = vsub(Dn, Dm) // Sd = vsub(Sn, Sm) // Dd = vmul(Dn, Dm) // Sd = vmul(Sn, Sm) // Dd = vdiv(Dn, Dm) // Sd = vdiv(Sn, Sm) // vcmp(Dd, Dm) // vcmp(Sd, Sm) // Dd = vsqrt(Dm) // Sd = vsqrt(Sm) // vmrs // vdup.size Qd, Rt. void Simulator::DecodeTypeVFP(Instruction* instr) { DCHECK((instr->TypeValue() == 7) && (instr->Bit(24) == 0x0) ); DCHECK_EQ(instr->Bits(11, 9), 0x5); // Obtain single precision register codes. int m = instr->VFPMRegValue(kSinglePrecision); int d = instr->VFPDRegValue(kSinglePrecision); int n = instr->VFPNRegValue(kSinglePrecision); // Obtain double precision register codes. int vm = instr->VFPMRegValue(kDoublePrecision); int vd = instr->VFPDRegValue(kDoublePrecision); int vn = instr->VFPNRegValue(kDoublePrecision); if (instr->Bit(4) == 0) { if (instr->Opc1Value() == 0x7) { // Other data processing instructions if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x1)) { // vmov register to register. if (instr->SzValue() == 0x1) { uint32_t data[2]; get_d_register(vm, data); set_d_register(vd, data); } else { set_s_register(d, get_s_register(m)); } } else if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x3)) { // vabs if (instr->SzValue() == 0x1) { Float64 dm = get_double_from_d_register(vm); constexpr uint64_t kSignBit64 = uint64_t{1} << 63; Float64 dd = Float64::FromBits(dm.get_bits() & ~kSignBit64); dd = canonicalizeNaN(dd); set_d_register_from_double(vd, dd); } else { Float32 sm = get_float_from_s_register(m); constexpr uint32_t kSignBit32 = uint32_t{1} << 31; Float32 sd = Float32::FromBits(sm.get_bits() & ~kSignBit32); sd = canonicalizeNaN(sd); set_s_register_from_float(d, sd); } } else if ((instr->Opc2Value() == 0x1) && (instr->Opc3Value() == 0x1)) { // vneg if (instr->SzValue() == 0x1) { Float64 dm = get_double_from_d_register(vm); constexpr uint64_t kSignBit64 = uint64_t{1} << 63; Float64 dd = Float64::FromBits(dm.get_bits() ^ kSignBit64); dd = canonicalizeNaN(dd); set_d_register_from_double(vd, dd); } else { Float32 sm = get_float_from_s_register(m); constexpr uint32_t kSignBit32 = uint32_t{1} << 31; Float32 sd = Float32::FromBits(sm.get_bits() ^ kSignBit32); sd = canonicalizeNaN(sd); set_s_register_from_float(d, sd); } } else if ((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3)) { DecodeVCVTBetweenDoubleAndSingle(instr); } else if ((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) { DecodeVCVTBetweenFloatingPointAndInteger(instr); } else if ((instr->Opc2Value() == 0xA) && (instr->Opc3Value() == 0x3) && (instr->Bit(8) == 1)) { // vcvt.f64.s32 Dd, Dd, # int fraction_bits = 32 - ((instr->Bits(3, 0) << 1) | instr->Bit(5)); int fixed_value = get_sinteger_from_s_register(vd * 2); double divide = 1 << fraction_bits; set_d_register_from_double(vd, fixed_value / divide); } else if (((instr->Opc2Value() >> 1) == 0x6) && (instr->Opc3Value() & 0x1)) { DecodeVCVTBetweenFloatingPointAndInteger(instr); } else if (((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) && (instr->Opc3Value() & 0x1)) { DecodeVCMP(instr); } else if (((instr->Opc2Value() == 0x1)) && (instr->Opc3Value() == 0x3)) { // vsqrt lazily_initialize_fast_sqrt(isolate_); if (instr->SzValue() == 0x1) { double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = fast_sqrt(dm_value, isolate_); dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = fast_sqrt(sm_value, isolate_); sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else if (instr->Opc3Value() == 0x0) { // vmov immediate. if (instr->SzValue() == 0x1) { set_d_register_from_double(vd, instr->DoubleImmedVmov()); } else { // Cast double to float. float value = instr->DoubleImmedVmov().get_scalar(); set_s_register_from_float(d, value); } } else if (((instr->Opc2Value() == 0x6)) && (instr->Opc3Value() == 0x3)) { // vrintz - truncate if (instr->SzValue() == 0x1) { double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = trunc(dm_value); dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = truncf(sm_value); sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else { UNREACHABLE(); // Not used by V8. } } else if (instr->Opc1Value() == 0x3) { if (instr->Opc3Value() & 0x1) { // vsub if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value - dm_value; dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value - sm_value; sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else { // vadd if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value + dm_value; dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value + sm_value; sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } } else if ((instr->Opc1Value() == 0x2) && !(instr->Opc3Value() & 0x1)) { // vmul if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value * dm_value; dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value * sm_value; sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else if ((instr->Opc1Value() == 0x0)) { // vmla, vmls const bool is_vmls = (instr->Opc3Value() & 0x1); if (instr->SzValue() == 0x1) { const double dd_val = get_double_from_d_register(vd).get_scalar(); const double dn_val = get_double_from_d_register(vn).get_scalar(); const double dm_val = get_double_from_d_register(vm).get_scalar(); // Note: we do the mul and add/sub in separate steps to avoid getting a // result with too high precision. const double res = dn_val * dm_val; set_d_register_from_double(vd, res); if (is_vmls) { set_d_register_from_double(vd, canonicalizeNaN(dd_val - res)); } else { set_d_register_from_double(vd, canonicalizeNaN(dd_val + res)); } } else { const float sd_val = get_float_from_s_register(d).get_scalar(); const float sn_val = get_float_from_s_register(n).get_scalar(); const float sm_val = get_float_from_s_register(m).get_scalar(); // Note: we do the mul and add/sub in separate steps to avoid getting a // result with too high precision. const float res = sn_val * sm_val; set_s_register_from_float(d, res); if (is_vmls) { set_s_register_from_float(d, canonicalizeNaN(sd_val - res)); } else { set_s_register_from_float(d, canonicalizeNaN(sd_val + res)); } } } else if ((instr->Opc1Value() == 0x4) && !(instr->Opc3Value() & 0x1)) { // vdiv if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value / dm_value; div_zero_vfp_flag_ = (dm_value == 0); dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value / sm_value; div_zero_vfp_flag_ = (sm_value == 0); sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else { UNIMPLEMENTED(); // Not used by V8. } } else { if ((instr->VCValue() == 0x0) && (instr->VAValue() == 0x0)) { DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(instr); } else if ((instr->VLValue() == 0x0) && (instr->VCValue() == 0x1)) { if (instr->Bit(23) == 0) { // vmov (ARM core register to scalar) int vd = instr->VFPNRegValue(kDoublePrecision); int rt = instr->RtValue(); int opc1_opc2 = (instr->Bits(22, 21) << 2) | instr->Bits(6, 5); if ((opc1_opc2 & 0xB) == 0) { // NeonS32/NeonU32 uint32_t data[2]; get_d_register(vd, data); data[instr->Bit(21)] = get_register(rt); set_d_register(vd, data); } else { uint64_t data; get_d_register(vd, &data); uint64_t rt_value = get_register(rt); if ((opc1_opc2 & 0x8) != 0) { // NeonS8 / NeonU8 int i = opc1_opc2 & 0x7; int shift = i * kBitsPerByte; const uint64_t mask = 0xFF; data &= ~(mask << shift); data |= (rt_value & mask) << shift; set_d_register(vd, &data); } else if ((opc1_opc2 & 0x1) != 0) { // NeonS16 / NeonU16 int i = (opc1_opc2 >> 1) & 0x3; int shift = i * kBitsPerByte * kShortSize; const uint64_t mask = 0xFFFF; data &= ~(mask << shift); data |= (rt_value & mask) << shift; set_d_register(vd, &data); } else { UNREACHABLE(); // Not used by V8. } } } else { // vdup.size Qd, Rt. NeonSize size = Neon32; if (instr->Bit(5) != 0) size = Neon16; else if (instr->Bit(22) != 0) size = Neon8; int vd = instr->VFPNRegValue(kSimd128Precision); int rt = instr->RtValue(); uint32_t rt_value = get_register(rt); uint32_t q_data[4]; switch (size) { case Neon8: { rt_value &= 0xFF; uint8_t* dst = reinterpret_cast(q_data); for (int i = 0; i < 16; i++) { dst[i] = rt_value; } break; } case Neon16: { // Perform pairwise op. rt_value &= 0xFFFFu; uint32_t rt_rt = (rt_value << 16) | (rt_value & 0xFFFFu); for (int i = 0; i < 4; i++) { q_data[i] = rt_rt; } break; } case Neon32: { for (int i = 0; i < 4; i++) { q_data[i] = rt_value; } break; } default: UNREACHABLE(); break; } set_neon_register(vd, q_data); } } else if ((instr->VLValue() == 0x1) && (instr->VCValue() == 0x1)) { // vmov (scalar to ARM core register) int vn = instr->VFPNRegValue(kDoublePrecision); int rt = instr->RtValue(); int opc1_opc2 = (instr->Bits(22, 21) << 2) | instr->Bits(6, 5); uint64_t data; get_d_register(vn, &data); if ((opc1_opc2 & 0xB) == 0) { // NeonS32 / NeonU32 int32_t int_data[2]; memcpy(int_data, &data, sizeof(int_data)); set_register(rt, int_data[instr->Bit(21)]); } else { uint64_t data; get_d_register(vn, &data); bool u = instr->Bit(23) != 0; if ((opc1_opc2 & 0x8) != 0) { // NeonS8 / NeonU8 int i = opc1_opc2 & 0x7; int shift = i * kBitsPerByte; uint32_t scalar = (data >> shift) & 0xFFu; if (!u && (scalar & 0x80) != 0) scalar |= 0xFFFFFF00; set_register(rt, scalar); } else if ((opc1_opc2 & 0x1) != 0) { // NeonS16 / NeonU16 int i = (opc1_opc2 >> 1) & 0x3; int shift = i * kBitsPerByte * kShortSize; uint32_t scalar = (data >> shift) & 0xFFFFu; if (!u && (scalar & 0x8000) != 0) scalar |= 0xFFFF0000; set_register(rt, scalar); } else { UNREACHABLE(); // Not used by V8. } } } else if ((instr->VLValue() == 0x1) && (instr->VCValue() == 0x0) && (instr->VAValue() == 0x7) && (instr->Bits(19, 16) == 0x1)) { // vmrs uint32_t rt = instr->RtValue(); if (rt == 0xF) { Copy_FPSCR_to_APSR(); } else { // Emulate FPSCR from the Simulator flags. uint32_t fpscr = (n_flag_FPSCR_ << 31) | (z_flag_FPSCR_ << 30) | (c_flag_FPSCR_ << 29) | (v_flag_FPSCR_ << 28) | (FPSCR_default_NaN_mode_ << 25) | (inexact_vfp_flag_ << 4) | (underflow_vfp_flag_ << 3) | (overflow_vfp_flag_ << 2) | (div_zero_vfp_flag_ << 1) | (inv_op_vfp_flag_ << 0) | (FPSCR_rounding_mode_); set_register(rt, fpscr); } } else if ((instr->VLValue() == 0x0) && (instr->VCValue() == 0x0) && (instr->VAValue() == 0x7) && (instr->Bits(19, 16) == 0x1)) { // vmsr uint32_t rt = instr->RtValue(); if (rt == pc) { UNREACHABLE(); } else { uint32_t rt_value = get_register(rt); n_flag_FPSCR_ = (rt_value >> 31) & 1; z_flag_FPSCR_ = (rt_value >> 30) & 1; c_flag_FPSCR_ = (rt_value >> 29) & 1; v_flag_FPSCR_ = (rt_value >> 28) & 1; FPSCR_default_NaN_mode_ = (rt_value >> 25) & 1; inexact_vfp_flag_ = (rt_value >> 4) & 1; underflow_vfp_flag_ = (rt_value >> 3) & 1; overflow_vfp_flag_ = (rt_value >> 2) & 1; div_zero_vfp_flag_ = (rt_value >> 1) & 1; inv_op_vfp_flag_ = (rt_value >> 0) & 1; FPSCR_rounding_mode_ = static_cast((rt_value) & kVFPRoundingModeMask); } } else { UNIMPLEMENTED(); // Not used by V8. } } } void Simulator::DecodeTypeCP15(Instruction* instr) { DCHECK((instr->TypeValue() == 7) && (instr->Bit(24) == 0x0)); DCHECK_EQ(instr->CoprocessorValue(), 15); if (instr->Bit(4) == 1) { // mcr int crn = instr->Bits(19, 16); int crm = instr->Bits(3, 0); int opc1 = instr->Bits(23, 21); int opc2 = instr->Bits(7, 5); if ((opc1 == 0) && (crn == 7)) { // ARMv6 memory barrier operations. // Details available in ARM DDI 0406C.b, B3-1750. if (((crm == 10) && (opc2 == 5)) || // CP15DMB ((crm == 10) && (opc2 == 4)) || // CP15DSB ((crm == 5) && (opc2 == 4))) { // CP15ISB // These are ignored by the simulator for now. } else { UNIMPLEMENTED(); } } } else { UNIMPLEMENTED(); } } void Simulator::DecodeVMOVBetweenCoreAndSinglePrecisionRegisters( Instruction* instr) { DCHECK((instr->Bit(4) == 1) && (instr->VCValue() == 0x0) && (instr->VAValue() == 0x0)); int t = instr->RtValue(); int n = instr->VFPNRegValue(kSinglePrecision); bool to_arm_register = (instr->VLValue() == 0x1); if (to_arm_register) { int32_t int_value = get_sinteger_from_s_register(n); set_register(t, int_value); } else { int32_t rs_val = get_register(t); set_s_register_from_sinteger(n, rs_val); } } void Simulator::DecodeVCMP(Instruction* instr) { DCHECK((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7)); DCHECK(((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) && (instr->Opc3Value() & 0x1)); // Comparison. VFPRegPrecision precision = kSinglePrecision; if (instr->SzValue() == 0x1) { precision = kDoublePrecision; } int d = instr->VFPDRegValue(precision); int m = 0; if (instr->Opc2Value() == 0x4) { m = instr->VFPMRegValue(precision); } if (precision == kDoublePrecision) { double dd_value = get_double_from_d_register(d).get_scalar(); double dm_value = 0.0; if (instr->Opc2Value() == 0x4) { dm_value = get_double_from_d_register(m).get_scalar(); } // Raise exceptions for quiet NaNs if necessary. if (instr->Bit(7) == 1) { if (std::isnan(dd_value)) { inv_op_vfp_flag_ = true; } } Compute_FPSCR_Flags(dd_value, dm_value); } else { float sd_value = get_float_from_s_register(d).get_scalar(); float sm_value = 0.0; if (instr->Opc2Value() == 0x4) { sm_value = get_float_from_s_register(m).get_scalar(); } // Raise exceptions for quiet NaNs if necessary. if (instr->Bit(7) == 1) { if (std::isnan(sd_value)) { inv_op_vfp_flag_ = true; } } Compute_FPSCR_Flags(sd_value, sm_value); } } void Simulator::DecodeVCVTBetweenDoubleAndSingle(Instruction* instr) { DCHECK((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7)); DCHECK((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3)); VFPRegPrecision dst_precision = kDoublePrecision; VFPRegPrecision src_precision = kSinglePrecision; if (instr->SzValue() == 1) { dst_precision = kSinglePrecision; src_precision = kDoublePrecision; } int dst = instr->VFPDRegValue(dst_precision); int src = instr->VFPMRegValue(src_precision); if (dst_precision == kSinglePrecision) { double val = get_double_from_d_register(src).get_scalar(); set_s_register_from_float(dst, static_cast(val)); } else { float val = get_float_from_s_register(src).get_scalar(); set_d_register_from_double(dst, static_cast(val)); } } bool get_inv_op_vfp_flag(VFPRoundingMode mode, double val, bool unsigned_) { DCHECK((mode == RN) || (mode == RM) || (mode == RZ)); double max_uint = static_cast(0xFFFFFFFFu); double max_int = static_cast(kMaxInt); double min_int = static_cast(kMinInt); // Check for NaN. if (val != val) { return true; } // Check for overflow. This code works because 32bit integers can be // exactly represented by ieee-754 64bit floating-point values. switch (mode) { case RN: return unsigned_ ? (val >= (max_uint + 0.5)) || (val < -0.5) : (val >= (max_int + 0.5)) || (val < (min_int - 0.5)); case RM: return unsigned_ ? (val >= (max_uint + 1.0)) || (val < 0) : (val >= (max_int + 1.0)) || (val < min_int); case RZ: return unsigned_ ? (val >= (max_uint + 1.0)) || (val <= -1) : (val >= (max_int + 1.0)) || (val <= (min_int - 1.0)); default: UNREACHABLE(); } } // We call this function only if we had a vfp invalid exception. // It returns the correct saturated value. int VFPConversionSaturate(double val, bool unsigned_res) { if (val != val) { return 0; } else { if (unsigned_res) { return (val < 0) ? 0 : 0xFFFFFFFFu; } else { return (val < 0) ? kMinInt : kMaxInt; } } } int32_t Simulator::ConvertDoubleToInt(double val, bool unsigned_integer, VFPRoundingMode mode) { // TODO(jkummerow): These casts are undefined behavior if the integral // part of {val} does not fit into the destination type. int32_t result = unsigned_integer ? static_cast(val) : static_cast(val); inv_op_vfp_flag_ = get_inv_op_vfp_flag(mode, val, unsigned_integer); double abs_diff = unsigned_integer ? std::fabs(val - static_cast(result)) : std::fabs(val - result); inexact_vfp_flag_ = (abs_diff != 0); if (inv_op_vfp_flag_) { result = VFPConversionSaturate(val, unsigned_integer); } else { switch (mode) { case RN: { int val_sign = (val > 0) ? 1 : -1; if (abs_diff > 0.5) { result += val_sign; } else if (abs_diff == 0.5) { // Round to even if exactly halfway. result = ((result % 2) == 0) ? result : result + val_sign; } break; } case RM: result = result > val ? result - 1 : result; break; case RZ: // Nothing to do. break; default: UNREACHABLE(); } } return result; } void Simulator::DecodeVCVTBetweenFloatingPointAndInteger(Instruction* instr) { DCHECK((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7) && (instr->Bits(27, 23) == 0x1D)); DCHECK(((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) || (((instr->Opc2Value() >> 1) == 0x6) && (instr->Opc3Value() & 0x1))); // Conversion between floating-point and integer. bool to_integer = (instr->Bit(18) == 1); VFPRegPrecision src_precision = (instr->SzValue() == 1) ? kDoublePrecision : kSinglePrecision; if (to_integer) { // We are playing with code close to the C++ standard's limits below, // hence the very simple code and heavy checks. // // Note: // C++ defines default type casting from floating point to integer as // (close to) rounding toward zero ("fractional part discarded"). int dst = instr->VFPDRegValue(kSinglePrecision); int src = instr->VFPMRegValue(src_precision); // Bit 7 in vcvt instructions indicates if we should use the FPSCR rounding // mode or the default Round to Zero mode. VFPRoundingMode mode = (instr->Bit(7) != 1) ? FPSCR_rounding_mode_ : RZ; DCHECK((mode == RM) || (mode == RZ) || (mode == RN)); bool unsigned_integer = (instr->Bit(16) == 0); bool double_precision = (src_precision == kDoublePrecision); double val = double_precision ? get_double_from_d_register(src).get_scalar() : get_float_from_s_register(src).get_scalar(); int32_t temp = ConvertDoubleToInt(val, unsigned_integer, mode); // Update the destination register. set_s_register_from_sinteger(dst, temp); } else { bool unsigned_integer = (instr->Bit(7) == 0); int dst = instr->VFPDRegValue(src_precision); int src = instr->VFPMRegValue(kSinglePrecision); int val = get_sinteger_from_s_register(src); if (src_precision == kDoublePrecision) { if (unsigned_integer) { set_d_register_from_double( dst, static_cast(static_cast(val))); } else { set_d_register_from_double(dst, static_cast(val)); } } else { if (unsigned_integer) { set_s_register_from_float( dst, static_cast(static_cast(val))); } else { set_s_register_from_float(dst, static_cast(val)); } } } } // void Simulator::DecodeType6CoprocessorIns(Instruction* instr) // Decode Type 6 coprocessor instructions. // Dm = vmov(Rt, Rt2) // = vmov(Dm) // Ddst = MEM(Rbase + 4*offset). // MEM(Rbase + 4*offset) = Dsrc. void Simulator::DecodeType6CoprocessorIns(Instruction* instr) { DCHECK_EQ(instr->TypeValue(), 6); if (instr->CoprocessorValue() == 0xA) { switch (instr->OpcodeValue()) { case 0x8: case 0xA: case 0xC: case 0xE: { // Load and store single precision float to memory. int rn = instr->RnValue(); int vd = instr->VFPDRegValue(kSinglePrecision); int offset = instr->Immed8Value(); if (!instr->HasU()) { offset = -offset; } int32_t address = get_register(rn) + 4 * offset; // Load and store address for singles must be at least four-byte // aligned. DCHECK_EQ(address % 4, 0); if (instr->HasL()) { // Load single from memory: vldr. set_s_register_from_sinteger(vd, ReadW(address)); } else { // Store single to memory: vstr. WriteW(address, get_sinteger_from_s_register(vd)); } break; } case 0x4: case 0x5: case 0x6: case 0x7: case 0x9: case 0xB: // Load/store multiple single from memory: vldm/vstm. HandleVList(instr); break; default: UNIMPLEMENTED(); // Not used by V8. } } else if (instr->CoprocessorValue() == 0xB) { switch (instr->OpcodeValue()) { case 0x2: // Load and store double to two GP registers if (instr->Bits(7, 6) != 0 || instr->Bit(4) != 1) { UNIMPLEMENTED(); // Not used by V8. } else { int rt = instr->RtValue(); int rn = instr->RnValue(); int vm = instr->VFPMRegValue(kDoublePrecision); if (instr->HasL()) { uint32_t data[2]; get_d_register(vm, data); set_register(rt, data[0]); set_register(rn, data[1]); } else { int32_t data[] = { get_register(rt), get_register(rn) }; set_d_register(vm, reinterpret_cast(data)); } } break; case 0x8: case 0xA: case 0xC: case 0xE: { // Load and store double to memory. int rn = instr->RnValue(); int vd = instr->VFPDRegValue(kDoublePrecision); int offset = instr->Immed8Value(); if (!instr->HasU()) { offset = -offset; } int32_t address = get_register(rn) + 4 * offset; // Load and store address for doubles must be at least four-byte // aligned. DCHECK_EQ(address % 4, 0); if (instr->HasL()) { // Load double from memory: vldr. int32_t data[] = {ReadW(address), ReadW(address + 4)}; set_d_register(vd, reinterpret_cast(data)); } else { // Store double to memory: vstr. uint32_t data[2]; get_d_register(vd, data); WriteW(address, data[0]); WriteW(address + 4, data[1]); } break; } case 0x4: case 0x5: case 0x6: case 0x7: case 0x9: case 0xB: // Load/store multiple double from memory: vldm/vstm. HandleVList(instr); break; default: UNIMPLEMENTED(); // Not used by V8. } } else { UNIMPLEMENTED(); // Not used by V8. } } // Templated operations for NEON instructions. template U Widen(T value) { static_assert(sizeof(int64_t) > sizeof(T), "T must be int32_t or smaller"); static_assert(sizeof(U) > sizeof(T), "T must smaller than U"); return static_cast(value); } template U Narrow(T value) { static_assert(sizeof(int8_t) < sizeof(T), "T must be int16_t or larger"); static_assert(sizeof(U) < sizeof(T), "T must larger than U"); static_assert(std::is_unsigned() == std::is_unsigned(), "Signed-ness of T and U must match"); // Make sure value can be expressed in the smaller type; otherwise, the // casted result is implementation defined. DCHECK_LE(std::numeric_limits::min(), value); DCHECK_GE(std::numeric_limits::max(), value); return static_cast(value); } template T Clamp(int64_t value) { static_assert(sizeof(int64_t) > sizeof(T), "T must be int32_t or smaller"); int64_t min = static_cast(std::numeric_limits::min()); int64_t max = static_cast(std::numeric_limits::max()); int64_t clamped = std::max(min, std::min(max, value)); return static_cast(clamped); } template void Widen(Simulator* simulator, int Vd, int Vm) { static const int kLanes = 8 / sizeof(T); T src[kLanes]; U dst[kLanes]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kLanes; i++) { dst[i] = Widen(src[i]); } simulator->set_neon_register(Vd, dst); } template void Abs(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); T src[kElems]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kElems; i++) { src[i] = std::abs(src[i]); } simulator->set_neon_register(Vd, src); } template void Neg(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); T src[kElems]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kElems; i++) { src[i] = -src[i]; } simulator->set_neon_register(Vd, src); } template void SaturatingNarrow(Simulator* simulator, int Vd, int Vm) { static const int kLanes = 16 / sizeof(T); T src[kLanes]; U dst[kLanes]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kLanes; i++) { dst[i] = Narrow(Clamp(src[i])); } simulator->set_neon_register(Vd, dst); } template void AddSaturate(Simulator* simulator, int Vd, int Vm, int Vn) { static const int kLanes = 16 / sizeof(T); T src1[kLanes], src2[kLanes]; simulator->get_neon_register(Vn, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kLanes; i++) { src1[i] = Clamp(Widen(src1[i]) + Widen(src2[i])); } simulator->set_neon_register(Vd, src1); } template void SubSaturate(Simulator* simulator, int Vd, int Vm, int Vn) { static const int kLanes = 16 / sizeof(T); T src1[kLanes], src2[kLanes]; simulator->get_neon_register(Vn, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kLanes; i++) { src1[i] = Clamp(Widen(src1[i]) - Widen(src2[i])); } simulator->set_neon_register(Vd, src1); } template void Zip(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); static const int kPairs = kElems / 2; T src1[kElems], src2[kElems], dst1[kElems], dst2[kElems]; simulator->get_neon_register(Vd, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kPairs; i++) { dst1[i * 2] = src1[i]; dst1[i * 2 + 1] = src2[i]; dst2[i * 2] = src1[i + kPairs]; dst2[i * 2 + 1] = src2[i + kPairs]; } simulator->set_neon_register(Vd, dst1); simulator->set_neon_register(Vm, dst2); } template void Unzip(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); static const int kPairs = kElems / 2; T src1[kElems], src2[kElems], dst1[kElems], dst2[kElems]; simulator->get_neon_register(Vd, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kPairs; i++) { dst1[i] = src1[i * 2]; dst1[i + kPairs] = src2[i * 2]; dst2[i] = src1[i * 2 + 1]; dst2[i + kPairs] = src2[i * 2 + 1]; } simulator->set_neon_register(Vd, dst1); simulator->set_neon_register(Vm, dst2); } template void Transpose(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); static const int kPairs = kElems / 2; T src1[kElems], src2[kElems]; simulator->get_neon_register(Vd, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kPairs; i++) { std::swap(src1[2 * i + 1], src2[2 * i]); } simulator->set_neon_register(Vd, src1); simulator->set_neon_register
\n"); PrintF(" or all stop(s).\n"); PrintF(" stop enable/disable all/ : enables / disables\n"); PrintF(" all or number stop(s)\n"); PrintF(" stop unstop\n"); PrintF(" ignore the stop instruction at the current location\n"); PrintF(" from now on\n"); } else { PrintF("Unknown command: %s\n", cmd); } } } // Add all the breakpoints back to stop execution and enter the debugger // shell when hit. RedoBreakpoints(); #undef COMMAND_SIZE #undef ARG_SIZE #undef STR #undef XSTR } bool Simulator::ICacheMatch(void* one, void* two) { DCHECK_EQ(reinterpret_cast(one) & CachePage::kPageMask, 0); DCHECK_EQ(reinterpret_cast(two) & CachePage::kPageMask, 0); return one == two; } static uint32_t ICacheHash(void* key) { return static_cast(reinterpret_cast(key)) >> 2; } static bool AllOnOnePage(uintptr_t start, int size) { intptr_t start_page = (start & ~CachePage::kPageMask); intptr_t end_page = ((start + size) & ~CachePage::kPageMask); return start_page == end_page; } void Simulator::set_last_debugger_input(char* input) { DeleteArray(last_debugger_input_); last_debugger_input_ = input; } void Simulator::SetRedirectInstruction(Instruction* instruction) { instruction->SetInstructionBits(al | (0xF * B24) | kCallRtRedirected); } void Simulator::FlushICache(base::CustomMatcherHashMap* i_cache, void* start_addr, size_t size) { intptr_t start = reinterpret_cast(start_addr); int intra_line = (start & CachePage::kLineMask); start -= intra_line; size += intra_line; size = ((size - 1) | CachePage::kLineMask) + 1; int offset = (start & CachePage::kPageMask); while (!AllOnOnePage(start, size - 1)) { int bytes_to_flush = CachePage::kPageSize - offset; FlushOnePage(i_cache, start, bytes_to_flush); start += bytes_to_flush; size -= bytes_to_flush; DCHECK_EQ(0, start & CachePage::kPageMask); offset = 0; } if (size != 0) { FlushOnePage(i_cache, start, size); } } CachePage* Simulator::GetCachePage(base::CustomMatcherHashMap* i_cache, void* page) { base::HashMap::Entry* entry = i_cache->LookupOrInsert(page, ICacheHash(page)); if (entry->value == nullptr) { CachePage* new_page = new CachePage(); entry->value = new_page; } return reinterpret_cast(entry->value); } // Flush from start up to and not including start + size. void Simulator::FlushOnePage(base::CustomMatcherHashMap* i_cache, intptr_t start, int size) { DCHECK_LE(size, CachePage::kPageSize); DCHECK(AllOnOnePage(start, size - 1)); DCHECK_EQ(start & CachePage::kLineMask, 0); DCHECK_EQ(size & CachePage::kLineMask, 0); void* page = reinterpret_cast(start & (~CachePage::kPageMask)); int offset = (start & CachePage::kPageMask); CachePage* cache_page = GetCachePage(i_cache, page); char* valid_bytemap = cache_page->ValidityByte(offset); memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift); } void Simulator::CheckICache(base::CustomMatcherHashMap* i_cache, Instruction* instr) { intptr_t address = reinterpret_cast(instr); void* page = reinterpret_cast(address & (~CachePage::kPageMask)); void* line = reinterpret_cast(address & (~CachePage::kLineMask)); int offset = (address & CachePage::kPageMask); CachePage* cache_page = GetCachePage(i_cache, page); char* cache_valid_byte = cache_page->ValidityByte(offset); bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID); char* cached_line = cache_page->CachedData(offset & ~CachePage::kLineMask); if (cache_hit) { // Check that the data in memory matches the contents of the I-cache. CHECK_EQ(0, memcmp(reinterpret_cast(instr), cache_page->CachedData(offset), kInstrSize)); } else { // Cache miss. Load memory into the cache. memcpy(cached_line, line, CachePage::kLineLength); *cache_valid_byte = CachePage::LINE_VALID; } } Simulator::Simulator(Isolate* isolate) : isolate_(isolate) { // Set up simulator support first. Some of this information is needed to // setup the architecture state. size_t stack_size = 1 * 1024*1024; // allocate 1MB for stack stack_ = reinterpret_cast(malloc(stack_size)); pc_modified_ = false; icount_ = 0; break_pc_ = nullptr; break_instr_ = 0; // Set up architecture state. // All registers are initialized to zero to start with. for (int i = 0; i < num_registers; i++) { registers_[i] = 0; } n_flag_ = false; z_flag_ = false; c_flag_ = false; v_flag_ = false; // Initializing VFP registers. // All registers are initialized to zero to start with // even though s_registers_ & d_registers_ share the same // physical registers in the target. for (int i = 0; i < num_d_registers * 2; i++) { vfp_registers_[i] = 0; } n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = false; v_flag_FPSCR_ = false; FPSCR_rounding_mode_ = RN; FPSCR_default_NaN_mode_ = false; inv_op_vfp_flag_ = false; div_zero_vfp_flag_ = false; overflow_vfp_flag_ = false; underflow_vfp_flag_ = false; inexact_vfp_flag_ = false; // 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 // some buffer below. registers_[sp] = reinterpret_cast(stack_) + stack_size - 64; // The lr and pc are initialized to a known bad value that will cause an // access violation if the simulator ever tries to execute it. registers_[pc] = bad_lr; registers_[lr] = bad_lr; last_debugger_input_ = nullptr; } Simulator::~Simulator() { global_monitor_.Pointer()->RemoveProcessor(&global_monitor_processor_); free(stack_); } // Get the active Simulator for the current thread. Simulator* Simulator::current(Isolate* isolate) { v8::internal::Isolate::PerIsolateThreadData* isolate_data = isolate->FindOrAllocatePerThreadDataForThisThread(); DCHECK_NOT_NULL(isolate_data); Simulator* sim = isolate_data->simulator(); if (sim == nullptr) { // TODO(146): delete the simulator object when a thread/isolate goes away. sim = new Simulator(isolate); isolate_data->set_simulator(sim); } return sim; } // Sets the register in the architecture state. It will also deal with updating // Simulator internal state for special registers such as PC. void Simulator::set_register(int reg, int32_t value) { DCHECK((reg >= 0) && (reg < num_registers)); if (reg == pc) { pc_modified_ = true; } registers_[reg] = value; } // Get the register from the architecture state. This function does handle // the special case of accessing the PC register. int32_t Simulator::get_register(int reg) const { DCHECK((reg >= 0) && (reg < num_registers)); // Stupid code added to avoid bug in GCC. // See: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949 if (reg >= num_registers) return 0; // End stupid code. return registers_[reg] + ((reg == pc) ? Instruction::kPcLoadDelta : 0); } double Simulator::get_double_from_register_pair(int reg) { DCHECK((reg >= 0) && (reg < num_registers) && ((reg % 2) == 0)); double dm_val = 0.0; // Read the bits from the unsigned integer register_[] array // into the double precision floating point value and return it. char buffer[2 * sizeof(vfp_registers_[0])]; memcpy(buffer, ®isters_[reg], 2 * sizeof(registers_[0])); memcpy(&dm_val, buffer, 2 * sizeof(registers_[0])); return(dm_val); } void Simulator::set_register_pair_from_double(int reg, double* value) { DCHECK((reg >= 0) && (reg < num_registers) && ((reg % 2) == 0)); memcpy(registers_ + reg, value, sizeof(*value)); } void Simulator::set_dw_register(int dreg, const int* dbl) { DCHECK((dreg >= 0) && (dreg < num_d_registers)); registers_[dreg] = dbl[0]; registers_[dreg + 1] = dbl[1]; } void Simulator::get_d_register(int dreg, uint64_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value)); } void Simulator::set_d_register(int dreg, const uint64_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value)); } void Simulator::get_d_register(int dreg, uint32_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value) * 2); } void Simulator::set_d_register(int dreg, const uint32_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value) * 2); } template void Simulator::get_neon_register(int reg, T (&value)[SIZE / sizeof(T)]) { DCHECK(SIZE == kSimd128Size || SIZE == kDoubleSize); DCHECK_LE(0, reg); DCHECK_GT(SIZE == kSimd128Size ? num_q_registers : num_d_registers, reg); memcpy(value, vfp_registers_ + reg * (SIZE / 4), SIZE); } template void Simulator::set_neon_register(int reg, const T (&value)[SIZE / sizeof(T)]) { DCHECK(SIZE == kSimd128Size || SIZE == kDoubleSize); DCHECK_LE(0, reg); DCHECK_GT(SIZE == kSimd128Size ? num_q_registers : num_d_registers, reg); memcpy(vfp_registers_ + reg * (SIZE / 4), value, SIZE); } // Raw access to the PC register. void Simulator::set_pc(int32_t value) { pc_modified_ = true; registers_[pc] = value; } bool Simulator::has_bad_pc() const { return ((registers_[pc] == bad_lr) || (registers_[pc] == end_sim_pc)); } // Raw access to the PC register without the special adjustment when reading. int32_t Simulator::get_pc() const { return registers_[pc]; } // Getting from and setting into VFP registers. void Simulator::set_s_register(int sreg, unsigned int value) { DCHECK((sreg >= 0) && (sreg < num_s_registers)); vfp_registers_[sreg] = value; } unsigned int Simulator::get_s_register(int sreg) const { DCHECK((sreg >= 0) && (sreg < num_s_registers)); return vfp_registers_[sreg]; } template void Simulator::SetVFPRegister(int reg_index, const InputType& value) { unsigned bytes = register_size * sizeof(vfp_registers_[0]); DCHECK_EQ(sizeof(InputType), bytes); DCHECK_GE(reg_index, 0); if (register_size == 1) DCHECK(reg_index < num_s_registers); if (register_size == 2) DCHECK(reg_index < DwVfpRegister::NumRegisters()); memcpy(&vfp_registers_[reg_index * register_size], &value, bytes); } template ReturnType Simulator::GetFromVFPRegister(int reg_index) { unsigned bytes = register_size * sizeof(vfp_registers_[0]); DCHECK_EQ(sizeof(ReturnType), bytes); DCHECK_GE(reg_index, 0); if (register_size == 1) DCHECK(reg_index < num_s_registers); if (register_size == 2) DCHECK(reg_index < DwVfpRegister::NumRegisters()); ReturnType value; memcpy(&value, &vfp_registers_[register_size * reg_index], bytes); return value; } void Simulator::SetSpecialRegister(SRegisterFieldMask reg_and_mask, uint32_t value) { // Only CPSR_f is implemented. Of that, only N, Z, C and V are implemented. if ((reg_and_mask == CPSR_f) && ((value & ~kSpecialCondition) == 0)) { n_flag_ = ((value & (1 << 31)) != 0); z_flag_ = ((value & (1 << 30)) != 0); c_flag_ = ((value & (1 << 29)) != 0); v_flag_ = ((value & (1 << 28)) != 0); } else { UNIMPLEMENTED(); } } uint32_t Simulator::GetFromSpecialRegister(SRegister reg) { uint32_t result = 0; // Only CPSR_f is implemented. if (reg == CPSR) { if (n_flag_) result |= (1 << 31); if (z_flag_) result |= (1 << 30); if (c_flag_) result |= (1 << 29); if (v_flag_) result |= (1 << 28); } else { UNIMPLEMENTED(); } return result; } // Runtime FP routines take: // - two double arguments // - one double argument and zero or one integer arguments. // All are consructed here from r0-r3 or d0, d1 and r0. void Simulator::GetFpArgs(double* x, double* y, int32_t* z) { if (use_eabi_hardfloat()) { *x = get_double_from_d_register(0).get_scalar(); *y = get_double_from_d_register(1).get_scalar(); *z = get_register(0); } else { // Registers 0 and 1 -> x. *x = get_double_from_register_pair(0); // Register 2 and 3 -> y. *y = get_double_from_register_pair(2); // Register 2 -> z *z = get_register(2); } } // The return value is either in r0/r1 or d0. void Simulator::SetFpResult(const double& result) { if (use_eabi_hardfloat()) { char buffer[2 * sizeof(vfp_registers_[0])]; memcpy(buffer, &result, sizeof(buffer)); // Copy result to d0. memcpy(vfp_registers_, buffer, sizeof(buffer)); } else { char buffer[2 * sizeof(registers_[0])]; memcpy(buffer, &result, sizeof(buffer)); // Copy result to r0 and r1. memcpy(registers_, buffer, sizeof(buffer)); } } void Simulator::TrashCallerSaveRegisters() { // We don't trash the registers with the return value. registers_[2] = 0x50BAD4U; registers_[3] = 0x50BAD4U; registers_[12] = 0x50BAD4U; } int Simulator::ReadW(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); intptr_t* ptr = reinterpret_cast(addr); return *ptr; } int Simulator::ReadExW(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::Word); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); intptr_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::WriteW(int32_t addr, int value) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); intptr_t* ptr = reinterpret_cast(addr); *ptr = value; } int Simulator::WriteExW(int32_t addr, int value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::Word) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { intptr_t* ptr = reinterpret_cast(addr); *ptr = value; return 0; } else { return 1; } } uint16_t Simulator::ReadHU(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); uint16_t* ptr = reinterpret_cast(addr); return *ptr; } int16_t Simulator::ReadH(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); int16_t* ptr = reinterpret_cast(addr); return *ptr; } uint16_t Simulator::ReadExHU(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::HalfWord); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); uint16_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::WriteH(int32_t addr, uint16_t value) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); uint16_t* ptr = reinterpret_cast(addr); *ptr = value; } void Simulator::WriteH(int32_t addr, int16_t value) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); int16_t* ptr = reinterpret_cast(addr); *ptr = value; } int Simulator::WriteExH(int32_t addr, uint16_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::HalfWord) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { uint16_t* ptr = reinterpret_cast(addr); *ptr = value; return 0; } else { return 1; } } uint8_t Simulator::ReadBU(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); uint8_t* ptr = reinterpret_cast(addr); return *ptr; } int8_t Simulator::ReadB(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); int8_t* ptr = reinterpret_cast(addr); return *ptr; } uint8_t Simulator::ReadExBU(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::Byte); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); uint8_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::WriteB(int32_t addr, uint8_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); uint8_t* ptr = reinterpret_cast(addr); *ptr = value; } void Simulator::WriteB(int32_t addr, int8_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); int8_t* ptr = reinterpret_cast(addr); *ptr = value; } int Simulator::WriteExB(int32_t addr, uint8_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::Byte) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { uint8_t* ptr = reinterpret_cast(addr); *ptr = value; return 0; } else { return 1; } } int32_t* Simulator::ReadDW(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); int32_t* ptr = reinterpret_cast(addr); return ptr; } int32_t* Simulator::ReadExDW(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::DoubleWord); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); int32_t* ptr = reinterpret_cast(addr); return ptr; } void Simulator::WriteDW(int32_t addr, int32_t value1, int32_t value2) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); int32_t* ptr = reinterpret_cast(addr); *ptr++ = value1; *ptr = value2; } int Simulator::WriteExDW(int32_t addr, int32_t value1, int32_t value2) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::DoubleWord) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { intptr_t* ptr = reinterpret_cast(addr); *ptr++ = value1; *ptr = value2; return 0; } else { return 1; } } // Returns the limit of the stack area to enable checking for stack overflows. uintptr_t Simulator::StackLimit(uintptr_t c_limit) const { // The simulator uses a separate JS stack. If we have exhausted the C stack, // we also drop down the JS limit to reflect the exhaustion on the JS stack. if (GetCurrentStackPosition() < c_limit) { return reinterpret_cast(get_sp()); } // Otherwise the limit is the JS stack. Leave a safety margin of 1024 bytes // to prevent overrunning the stack when pushing values. return reinterpret_cast(stack_) + 1024; } // Unsupported instructions use Format to print an error and stop execution. void Simulator::Format(Instruction* instr, const char* format) { PrintF("Simulator found unsupported instruction:\n 0x%08" V8PRIxPTR ": %s\n", reinterpret_cast(instr), format); UNIMPLEMENTED(); } // Checks if the current instruction should be executed based on its // condition bits. bool Simulator::ConditionallyExecute(Instruction* instr) { switch (instr->ConditionField()) { case eq: return z_flag_; case ne: return !z_flag_; case cs: return c_flag_; case cc: return !c_flag_; case mi: return n_flag_; case pl: return !n_flag_; case vs: return v_flag_; case vc: return !v_flag_; case hi: return c_flag_ && !z_flag_; case ls: return !c_flag_ || z_flag_; case ge: return n_flag_ == v_flag_; case lt: return n_flag_ != v_flag_; case gt: return !z_flag_ && (n_flag_ == v_flag_); case le: return z_flag_ || (n_flag_ != v_flag_); case al: return true; default: UNREACHABLE(); } return false; } // Calculate and set the Negative and Zero flags. void Simulator::SetNZFlags(int32_t val) { n_flag_ = (val < 0); z_flag_ = (val == 0); } // Set the Carry flag. void Simulator::SetCFlag(bool val) { c_flag_ = val; } // Set the oVerflow flag. void Simulator::SetVFlag(bool val) { v_flag_ = val; } // Calculate C flag value for additions. bool Simulator::CarryFrom(int32_t left, int32_t right, int32_t carry) { uint32_t uleft = static_cast(left); uint32_t uright = static_cast(right); uint32_t urest = 0xFFFFFFFFU - uleft; return (uright > urest) || (carry && (((uright + 1) > urest) || (uright > (urest - 1)))); } // Calculate C flag value for subtractions. bool Simulator::BorrowFrom(int32_t left, int32_t right, int32_t carry) { uint32_t uleft = static_cast(left); uint32_t uright = static_cast(right); return (uright > uleft) || (!carry && (((uright + 1) > uleft) || (uright > (uleft - 1)))); } // Calculate V flag value for additions and subtractions. bool Simulator::OverflowFrom(int32_t alu_out, int32_t left, int32_t right, bool addition) { bool overflow; if (addition) { // operands have the same sign overflow = ((left >= 0 && right >= 0) || (left < 0 && right < 0)) // and operands and result have different sign && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); } else { // operands have different signs overflow = ((left < 0 && right >= 0) || (left >= 0 && right < 0)) // and first operand and result have different signs && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); } return overflow; } // Support for VFP comparisons. void Simulator::Compute_FPSCR_Flags(float val1, float val2) { if (std::isnan(val1) || std::isnan(val2)) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = true; // All non-NaN cases. } else if (val1 == val2) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = true; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } else if (val1 < val2) { n_flag_FPSCR_ = true; z_flag_FPSCR_ = false; c_flag_FPSCR_ = false; v_flag_FPSCR_ = false; } else { // Case when (val1 > val2). n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } } void Simulator::Compute_FPSCR_Flags(double val1, double val2) { if (std::isnan(val1) || std::isnan(val2)) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = true; // All non-NaN cases. } else if (val1 == val2) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = true; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } else if (val1 < val2) { n_flag_FPSCR_ = true; z_flag_FPSCR_ = false; c_flag_FPSCR_ = false; v_flag_FPSCR_ = false; } else { // Case when (val1 > val2). n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } } void Simulator::Copy_FPSCR_to_APSR() { n_flag_ = n_flag_FPSCR_; z_flag_ = z_flag_FPSCR_; c_flag_ = c_flag_FPSCR_; v_flag_ = v_flag_FPSCR_; } // Addressing Mode 1 - Data-processing operands: // Get the value based on the shifter_operand with register. int32_t Simulator::GetShiftRm(Instruction* instr, bool* carry_out) { ShiftOp shift = instr->ShiftField(); int shift_amount = instr->ShiftAmountValue(); int32_t result = get_register(instr->RmValue()); if (instr->Bit(4) == 0) { // by immediate if ((shift == ROR) && (shift_amount == 0)) { UNIMPLEMENTED(); return result; } else if (((shift == LSR) || (shift == ASR)) && (shift_amount == 0)) { shift_amount = 32; } switch (shift) { case ASR: { if (shift_amount == 0) { if (result < 0) { result = 0xFFFFFFFF; *carry_out = true; } else { result = 0; *carry_out = false; } } else { result >>= (shift_amount - 1); *carry_out = (result & 1) == 1; result >>= 1; } break; } case LSL: { if (shift_amount == 0) { *carry_out = c_flag_; } else { result <<= (shift_amount - 1); *carry_out = (result < 0); result <<= 1; } break; } case LSR: { if (shift_amount == 0) { result = 0; *carry_out = c_flag_; } else { uint32_t uresult = static_cast(result); uresult >>= (shift_amount - 1); *carry_out = (uresult & 1) == 1; uresult >>= 1; result = static_cast(uresult); } break; } case ROR: { if (shift_amount == 0) { *carry_out = c_flag_; } else { uint32_t left = static_cast(result) >> shift_amount; uint32_t right = static_cast(result) << (32 - shift_amount); result = right | left; *carry_out = (static_cast(result) >> 31) != 0; } break; } default: { UNREACHABLE(); break; } } } else { // by register int rs = instr->RsValue(); shift_amount = get_register(rs) & 0xFF; switch (shift) { case ASR: { if (shift_amount == 0) { *carry_out = c_flag_; } else if (shift_amount < 32) { result >>= (shift_amount - 1); *carry_out = (result & 1) == 1; result >>= 1; } else { DCHECK_GE(shift_amount, 32); if (result < 0) { *carry_out = true; result = 0xFFFFFFFF; } else { *carry_out = false; result = 0; } } break; } case LSL: { if (shift_amount == 0) { *carry_out = c_flag_; } else if (shift_amount < 32) { result <<= (shift_amount - 1); *carry_out = (result < 0); result <<= 1; } else if (shift_amount == 32) { *carry_out = (result & 1) == 1; result = 0; } else { DCHECK_GT(shift_amount, 32); *carry_out = false; result = 0; } break; } case LSR: { if (shift_amount == 0) { *carry_out = c_flag_; } else if (shift_amount < 32) { uint32_t uresult = static_cast(result); uresult >>= (shift_amount - 1); *carry_out = (uresult & 1) == 1; uresult >>= 1; result = static_cast(uresult); } else if (shift_amount == 32) { *carry_out = (result < 0); result = 0; } else { *carry_out = false; result = 0; } break; } case ROR: { if (shift_amount == 0) { *carry_out = c_flag_; } else { uint32_t left = static_cast(result) >> shift_amount; uint32_t right = static_cast(result) << (32 - shift_amount); result = right | left; *carry_out = (static_cast(result) >> 31) != 0; } break; } default: { UNREACHABLE(); break; } } } return result; } // Addressing Mode 1 - Data-processing operands: // Get the value based on the shifter_operand with immediate. int32_t Simulator::GetImm(Instruction* instr, bool* carry_out) { int rotate = instr->RotateValue() * 2; int immed8 = instr->Immed8Value(); int imm = base::bits::RotateRight32(immed8, rotate); *carry_out = (rotate == 0) ? c_flag_ : (imm < 0); return imm; } static int count_bits(int bit_vector) { int count = 0; while (bit_vector != 0) { if ((bit_vector & 1) != 0) { count++; } bit_vector >>= 1; } return count; } int32_t Simulator::ProcessPU(Instruction* instr, int num_regs, int reg_size, intptr_t* start_address, intptr_t* end_address) { int rn = instr->RnValue(); int32_t rn_val = get_register(rn); switch (instr->PUField()) { case da_x: { UNIMPLEMENTED(); break; } case ia_x: { *start_address = rn_val; *end_address = rn_val + (num_regs * reg_size) - reg_size; rn_val = rn_val + (num_regs * reg_size); break; } case db_x: { *start_address = rn_val - (num_regs * reg_size); *end_address = rn_val - reg_size; rn_val = *start_address; break; } case ib_x: { *start_address = rn_val + reg_size; *end_address = rn_val + (num_regs * reg_size); rn_val = *end_address; break; } default: { UNREACHABLE(); break; } } return rn_val; } // Addressing Mode 4 - Load and Store Multiple void Simulator::HandleRList(Instruction* instr, bool load) { int rlist = instr->RlistValue(); int num_regs = count_bits(rlist); intptr_t start_address = 0; intptr_t end_address = 0; int32_t rn_val = ProcessPU(instr, num_regs, kPointerSize, &start_address, &end_address); intptr_t* address = reinterpret_cast(start_address); // Catch null pointers a little earlier. DCHECK(start_address > 8191 || start_address < 0); int reg = 0; while (rlist != 0) { if ((rlist & 1) != 0) { if (load) { set_register(reg, *address); } else { *address = get_register(reg); } address += 1; } reg++; rlist >>= 1; } DCHECK(end_address == ((intptr_t)address) - 4); if (instr->HasW()) { set_register(instr->RnValue(), rn_val); } } // Addressing Mode 6 - Load and Store Multiple Coprocessor registers. void Simulator::HandleVList(Instruction* instr) { VFPRegPrecision precision = (instr->SzValue() == 0) ? kSinglePrecision : kDoublePrecision; int operand_size = (precision == kSinglePrecision) ? 4 : 8; bool load = (instr->VLValue() == 0x1); int vd; int num_regs; vd = instr->VFPDRegValue(precision); if (precision == kSinglePrecision) { num_regs = instr->Immed8Value(); } else { num_regs = instr->Immed8Value() / 2; } intptr_t start_address = 0; intptr_t end_address = 0; int32_t rn_val = ProcessPU(instr, num_regs, operand_size, &start_address, &end_address); intptr_t* address = reinterpret_cast(start_address); for (int reg = vd; reg < vd + num_regs; reg++) { if (precision == kSinglePrecision) { if (load) { set_s_register_from_sinteger(reg, ReadW(reinterpret_cast(address))); } else { WriteW(reinterpret_cast(address), get_sinteger_from_s_register(reg)); } address += 1; } else { if (load) { int32_t data[] = {ReadW(reinterpret_cast(address)), ReadW(reinterpret_cast(address + 1))}; set_d_register(reg, reinterpret_cast(data)); } else { uint32_t data[2]; get_d_register(reg, data); WriteW(reinterpret_cast(address), data[0]); WriteW(reinterpret_cast(address + 1), data[1]); } address += 2; } } DCHECK(reinterpret_cast(address) - operand_size == end_address); if (instr->HasW()) { set_register(instr->RnValue(), rn_val); } } // Calls into the V8 runtime are based on this very simple interface. // 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 // 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, int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7, int32_t arg8); // These prototypes handle the four types of FP calls. typedef int64_t (*SimulatorRuntimeCompareCall)(double darg0, double darg1); typedef double (*SimulatorRuntimeFPFPCall)(double darg0, double darg1); typedef double (*SimulatorRuntimeFPCall)(double darg0); typedef double (*SimulatorRuntimeFPIntCall)(double darg0, int32_t arg0); // This signature supports direct call in to API function native callback // (refer to InvocationCallback in v8.h). typedef void (*SimulatorRuntimeDirectApiCall)(int32_t arg0); typedef void (*SimulatorRuntimeProfilingApiCall)(int32_t arg0, void* arg1); // This signature supports direct call to accessor getter callback. typedef void (*SimulatorRuntimeDirectGetterCall)(int32_t arg0, int32_t arg1); typedef void (*SimulatorRuntimeProfilingGetterCall)( int32_t arg0, int32_t arg1, void* arg2); // Software interrupt instructions are used by the simulator to call into the // C-based V8 runtime. void Simulator::SoftwareInterrupt(Instruction* instr) { int svc = instr->SvcValue(); switch (svc) { case kCallRtRedirected: { // Check if stack is aligned. Error if not aligned is reported below to // include information on the function called. bool stack_aligned = (get_register(sp) & (::v8::internal::FLAG_sim_stack_alignment - 1)) == 0; Redirection* redirection = Redirection::FromInstruction(instr); int32_t arg0 = get_register(r0); int32_t arg1 = get_register(r1); int32_t arg2 = get_register(r2); int32_t arg3 = get_register(r3); int32_t* stack_pointer = reinterpret_cast(get_register(sp)); int32_t arg4 = stack_pointer[0]; int32_t arg5 = stack_pointer[1]; int32_t arg6 = stack_pointer[2]; int32_t arg7 = stack_pointer[3]; int32_t arg8 = stack_pointer[4]; STATIC_ASSERT(kMaxCParameters == 9); bool fp_call = (redirection->type() == ExternalReference::BUILTIN_FP_FP_CALL) || (redirection->type() == ExternalReference::BUILTIN_COMPARE_CALL) || (redirection->type() == ExternalReference::BUILTIN_FP_CALL) || (redirection->type() == ExternalReference::BUILTIN_FP_INT_CALL); // 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_lr = get_register(lr); intptr_t external = reinterpret_cast(redirection->external_function()); if (fp_call) { double dval0, dval1; // one or two double parameters int32_t ival; // zero or one integer parameters int64_t iresult = 0; // integer return value double dresult = 0; // double return value GetFpArgs(&dval0, &dval1, &ival); if (::v8::internal::FLAG_trace_sim || !stack_aligned) { SimulatorRuntimeCall generic_target = reinterpret_cast(external); switch (redirection->type()) { case ExternalReference::BUILTIN_FP_FP_CALL: case ExternalReference::BUILTIN_COMPARE_CALL: PrintF("Call to host function at %p with args %f, %f", reinterpret_cast(FUNCTION_ADDR(generic_target)), dval0, dval1); break; case ExternalReference::BUILTIN_FP_CALL: PrintF("Call to host function at %p with arg %f", reinterpret_cast(FUNCTION_ADDR(generic_target)), dval0); break; case ExternalReference::BUILTIN_FP_INT_CALL: PrintF("Call to host function at %p with args %f, %d", reinterpret_cast(FUNCTION_ADDR(generic_target)), dval0, ival); break; default: UNREACHABLE(); break; } if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); switch (redirection->type()) { case ExternalReference::BUILTIN_COMPARE_CALL: { SimulatorRuntimeCompareCall target = reinterpret_cast(external); iresult = target(dval0, dval1); set_register(r0, static_cast(iresult)); set_register(r1, static_cast(iresult >> 32)); break; } case ExternalReference::BUILTIN_FP_FP_CALL: { SimulatorRuntimeFPFPCall target = reinterpret_cast(external); dresult = target(dval0, dval1); SetFpResult(dresult); break; } case ExternalReference::BUILTIN_FP_CALL: { SimulatorRuntimeFPCall target = reinterpret_cast(external); dresult = target(dval0); SetFpResult(dresult); break; } case ExternalReference::BUILTIN_FP_INT_CALL: { SimulatorRuntimeFPIntCall target = reinterpret_cast(external); dresult = target(dval0, ival); SetFpResult(dresult); break; } default: UNREACHABLE(); break; } if (::v8::internal::FLAG_trace_sim || !stack_aligned) { switch (redirection->type()) { case ExternalReference::BUILTIN_COMPARE_CALL: PrintF("Returned %08x\n", static_cast(iresult)); break; case ExternalReference::BUILTIN_FP_FP_CALL: case ExternalReference::BUILTIN_FP_CALL: case ExternalReference::BUILTIN_FP_INT_CALL: PrintF("Returned %f\n", dresult); break; default: UNREACHABLE(); break; } } } else if (redirection->type() == ExternalReference::DIRECT_API_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x", reinterpret_cast(external), arg0); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeDirectApiCall target = reinterpret_cast(external); target(arg0); } else if ( redirection->type() == ExternalReference::PROFILING_API_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x %08x", reinterpret_cast(external), arg0, arg1); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeProfilingApiCall target = reinterpret_cast(external); target(arg0, Redirection::ReverseRedirection(arg1)); } else if ( redirection->type() == ExternalReference::DIRECT_GETTER_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x %08x", reinterpret_cast(external), arg0, arg1); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeDirectGetterCall target = reinterpret_cast(external); target(arg0, arg1); } else if ( redirection->type() == ExternalReference::PROFILING_GETTER_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x %08x %08x", reinterpret_cast(external), arg0, arg1, arg2); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeProfilingGetterCall target = reinterpret_cast( external); target(arg0, arg1, Redirection::ReverseRedirection(arg2)); } else { // builtin call. DCHECK(redirection->type() == ExternalReference::BUILTIN_CALL || redirection->type() == ExternalReference::BUILTIN_CALL_PAIR); SimulatorRuntimeCall target = reinterpret_cast(external); if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF( "Call to host function at %p " "args %08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x", reinterpret_cast(FUNCTION_ADDR(target)), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); int32_t lo_res = static_cast(result); int32_t hi_res = static_cast(result >> 32); if (::v8::internal::FLAG_trace_sim) { PrintF("Returned %08x\n", lo_res); } set_register(r0, lo_res); set_register(r1, hi_res); } set_register(lr, saved_lr); set_pc(get_register(lr)); break; } case kBreakpoint: { ArmDebugger dbg(this); dbg.Debug(); break; } // stop uses all codes greater than 1 << 23. default: { if (svc >= (1 << 23)) { uint32_t code = svc & kStopCodeMask; if (isWatchedStop(code)) { IncreaseStopCounter(code); } // Stop if it is enabled, otherwise go on jumping over the stop // and the message address. if (isEnabledStop(code)) { ArmDebugger dbg(this); dbg.Stop(instr); } } else { // This is not a valid svc code. UNREACHABLE(); break; } } } } float Simulator::canonicalizeNaN(float value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr uint32_t kDefaultNaN = 0x7FC00000u; if (FPSCR_default_NaN_mode_ && std::isnan(value)) { value = bit_cast(kDefaultNaN); } return value; } Float32 Simulator::canonicalizeNaN(Float32 value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr Float32 kDefaultNaN = Float32::FromBits(0x7FC00000u); return FPSCR_default_NaN_mode_ && value.is_nan() ? kDefaultNaN : value; } double Simulator::canonicalizeNaN(double value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr uint64_t kDefaultNaN = uint64_t{0x7FF8000000000000}; if (FPSCR_default_NaN_mode_ && std::isnan(value)) { value = bit_cast(kDefaultNaN); } return value; } Float64 Simulator::canonicalizeNaN(Float64 value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr Float64 kDefaultNaN = Float64::FromBits(uint64_t{0x7FF8000000000000}); return FPSCR_default_NaN_mode_ && value.is_nan() ? kDefaultNaN : value; } // Stop helper functions. bool Simulator::isStopInstruction(Instruction* instr) { return (instr->Bits(27, 24) == 0xF) && (instr->SvcValue() >= kStopCode); } bool Simulator::isWatchedStop(uint32_t code) { DCHECK_LE(code, kMaxStopCode); return code < kNumOfWatchedStops; } bool Simulator::isEnabledStop(uint32_t code) { DCHECK_LE(code, kMaxStopCode); // Unwatched stops are always enabled. return !isWatchedStop(code) || !(watched_stops_[code].count & kStopDisabledBit); } void Simulator::EnableStop(uint32_t code) { DCHECK(isWatchedStop(code)); if (!isEnabledStop(code)) { watched_stops_[code].count &= ~kStopDisabledBit; } } void Simulator::DisableStop(uint32_t code) { DCHECK(isWatchedStop(code)); if (isEnabledStop(code)) { watched_stops_[code].count |= kStopDisabledBit; } } void Simulator::IncreaseStopCounter(uint32_t code) { DCHECK_LE(code, kMaxStopCode); DCHECK(isWatchedStop(code)); if ((watched_stops_[code].count & ~(1 << 31)) == 0x7FFFFFFF) { PrintF("Stop counter for code %i has overflowed.\n" "Enabling this code and reseting the counter to 0.\n", code); watched_stops_[code].count = 0; EnableStop(code); } else { watched_stops_[code].count++; } } // Print a stop status. void Simulator::PrintStopInfo(uint32_t code) { DCHECK_LE(code, kMaxStopCode); if (!isWatchedStop(code)) { PrintF("Stop not watched."); } else { const char* state = isEnabledStop(code) ? "Enabled" : "Disabled"; int32_t count = watched_stops_[code].count & ~kStopDisabledBit; // Don't print the state of unused breakpoints. if (count != 0) { if (watched_stops_[code].desc) { PrintF("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n", code, code, state, count, watched_stops_[code].desc); } else { PrintF("stop %i - 0x%x: \t%s, \tcounter = %i\n", code, code, state, count); } } } } // Handle execution based on instruction types. // Instruction types 0 and 1 are both rolled into one function because they // only differ in the handling of the shifter_operand. void Simulator::DecodeType01(Instruction* instr) { int type = instr->TypeValue(); if ((type == 0) && instr->IsSpecialType0()) { // multiply instruction or extra loads and stores if (instr->Bits(7, 4) == 9) { if (instr->Bit(24) == 0) { // Raw field decoding here. Multiply instructions have their Rd in // funny places. int rn = instr->RnValue(); int rm = instr->RmValue(); int rs = instr->RsValue(); int32_t rs_val = get_register(rs); int32_t rm_val = get_register(rm); if (instr->Bit(23) == 0) { if (instr->Bit(21) == 0) { // The MUL instruction description (A 4.1.33) refers to Rd as being // the destination for the operation, but it confusingly uses the // Rn field to encode it. // Format(instr, "mul'cond's 'rn, 'rm, 'rs"); int rd = rn; // Remap the rn field to the Rd register. int32_t alu_out = rm_val * rs_val; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); } } else { int rd = instr->RdValue(); int32_t acc_value = get_register(rd); if (instr->Bit(22) == 0) { // The MLA instruction description (A 4.1.28) refers to the order // of registers as "Rd, Rm, Rs, Rn". But confusingly it uses the // Rn field to encode the Rd register and the Rd field to encode // the Rn register. // Format(instr, "mla'cond's 'rn, 'rm, 'rs, 'rd"); int32_t mul_out = rm_val * rs_val; int32_t result = acc_value + mul_out; set_register(rn, result); } else { // Format(instr, "mls'cond's 'rn, 'rm, 'rs, 'rd"); int32_t mul_out = rm_val * rs_val; int32_t result = acc_value - mul_out; set_register(rn, result); } } } else { // The signed/long multiply instructions use the terms RdHi and RdLo // when referring to the target registers. They are mapped to the Rn // and Rd fields as follows: // RdLo == Rd // RdHi == Rn (This is confusingly stored in variable rd here // because the mul instruction from above uses the // Rn field to encode the Rd register. Good luck figuring // this out without reading the ARM instruction manual // at a very detailed level.) // Format(instr, "'um'al'cond's 'rd, 'rn, 'rs, 'rm"); int rd_hi = rn; // Remap the rn field to the RdHi register. int rd_lo = instr->RdValue(); int32_t hi_res = 0; int32_t lo_res = 0; if (instr->Bit(22) == 1) { int64_t left_op = static_cast(rm_val); int64_t right_op = static_cast(rs_val); uint64_t result = left_op * right_op; hi_res = static_cast(result >> 32); lo_res = static_cast(result & 0xFFFFFFFF); } else { // unsigned multiply uint64_t left_op = static_cast(rm_val); uint64_t right_op = static_cast(rs_val); uint64_t result = left_op * right_op; hi_res = static_cast(result >> 32); lo_res = static_cast(result & 0xFFFFFFFF); } set_register(rd_lo, lo_res); set_register(rd_hi, hi_res); if (instr->HasS()) { UNIMPLEMENTED(); } } } else { if (instr->Bits(24, 23) == 3) { if (instr->Bit(20) == 1) { // ldrex int rt = instr->RtValue(); int rn = instr->RnValue(); int32_t addr = get_register(rn); switch (instr->Bits(22, 21)) { case 0: { // Format(instr, "ldrex'cond 'rt, ['rn]"); int value = ReadExW(addr); set_register(rt, value); break; } case 1: { // Format(instr, "ldrexd'cond 'rt, ['rn]"); int* rn_data = ReadExDW(addr); set_dw_register(rt, rn_data); break; } case 2: { // Format(instr, "ldrexb'cond 'rt, ['rn]"); uint8_t value = ReadExBU(addr); set_register(rt, value); break; } case 3: { // Format(instr, "ldrexh'cond 'rt, ['rn]"); uint16_t value = ReadExHU(addr); set_register(rt, value); break; } default: UNREACHABLE(); break; } } else { // The instruction is documented as strex rd, rt, [rn], but the // "rt" register is using the rm bits. int rd = instr->RdValue(); int rt = instr->RmValue(); int rn = instr->RnValue(); DCHECK_NE(rd, rn); DCHECK_NE(rd, rt); int32_t addr = get_register(rn); switch (instr->Bits(22, 21)) { case 0: { // Format(instr, "strex'cond 'rd, 'rm, ['rn]"); int value = get_register(rt); int status = WriteExW(addr, value); set_register(rd, status); break; } case 1: { // Format(instr, "strexd'cond 'rd, 'rm, ['rn]"); DCHECK_EQ(rt % 2, 0); int32_t value1 = get_register(rt); int32_t value2 = get_register(rt + 1); int status = WriteExDW(addr, value1, value2); set_register(rd, status); break; } case 2: { // Format(instr, "strexb'cond 'rd, 'rm, ['rn]"); uint8_t value = get_register(rt); int status = WriteExB(addr, value); set_register(rd, status); break; } case 3: { // Format(instr, "strexh'cond 'rd, 'rm, ['rn]"); uint16_t value = get_register(rt); int status = WriteExH(addr, value); set_register(rd, status); break; } default: UNREACHABLE(); break; } } } else { UNIMPLEMENTED(); // Not used by V8. } } } else { // extra load/store instructions int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); int32_t addr = 0; if (instr->Bit(22) == 0) { int rm = instr->RmValue(); int32_t rm_val = get_register(rm); switch (instr->PUField()) { case da_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], -'rm"); DCHECK(!instr->HasW()); addr = rn_val; rn_val -= rm_val; set_register(rn, rn_val); break; } case ia_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], +'rm"); DCHECK(!instr->HasW()); addr = rn_val; rn_val += rm_val; set_register(rn, rn_val); break; } case db_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, -'rm]'w"); rn_val -= rm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } case ib_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, +'rm]'w"); rn_val += rm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } default: { // The PU field is a 2-bit field. UNREACHABLE(); break; } } } else { int32_t imm_val = (instr->ImmedHValue() << 4) | instr->ImmedLValue(); switch (instr->PUField()) { case da_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], #-'off8"); DCHECK(!instr->HasW()); addr = rn_val; rn_val -= imm_val; set_register(rn, rn_val); break; } case ia_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], #+'off8"); DCHECK(!instr->HasW()); addr = rn_val; rn_val += imm_val; set_register(rn, rn_val); break; } case db_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, #-'off8]'w"); rn_val -= imm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } case ib_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, #+'off8]'w"); rn_val += imm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } default: { // The PU field is a 2-bit field. UNREACHABLE(); break; } } } if (((instr->Bits(7, 4) & 0xD) == 0xD) && (instr->Bit(20) == 0)) { DCHECK_EQ(rd % 2, 0); if (instr->HasH()) { // The strd instruction. int32_t value1 = get_register(rd); int32_t value2 = get_register(rd+1); WriteDW(addr, value1, value2); } else { // The ldrd instruction. int* rn_data = ReadDW(addr); set_dw_register(rd, rn_data); } } else if (instr->HasH()) { if (instr->HasSign()) { if (instr->HasL()) { int16_t val = ReadH(addr); set_register(rd, val); } else { int16_t val = get_register(rd); WriteH(addr, val); } } else { if (instr->HasL()) { uint16_t val = ReadHU(addr); set_register(rd, val); } else { uint16_t val = get_register(rd); WriteH(addr, val); } } } else { // signed byte loads DCHECK(instr->HasSign()); DCHECK(instr->HasL()); int8_t val = ReadB(addr); set_register(rd, val); } return; } } else if ((type == 0) && instr->IsMiscType0()) { if ((instr->Bits(27, 23) == 2) && (instr->Bits(21, 20) == 2) && (instr->Bits(15, 4) == 0xF00)) { // MSR int rm = instr->RmValue(); DCHECK_NE(pc, rm); // UNPREDICTABLE SRegisterFieldMask sreg_and_mask = instr->BitField(22, 22) | instr->BitField(19, 16); SetSpecialRegister(sreg_and_mask, get_register(rm)); } else if ((instr->Bits(27, 23) == 2) && (instr->Bits(21, 20) == 0) && (instr->Bits(11, 0) == 0)) { // MRS int rd = instr->RdValue(); DCHECK_NE(pc, rd); // UNPREDICTABLE SRegister sreg = static_cast(instr->BitField(22, 22)); set_register(rd, GetFromSpecialRegister(sreg)); } else if (instr->Bits(22, 21) == 1) { int rm = instr->RmValue(); switch (instr->BitField(7, 4)) { case BX: set_pc(get_register(rm)); break; case BLX: { uint32_t old_pc = get_pc(); set_pc(get_register(rm)); set_register(lr, old_pc + kInstrSize); break; } case BKPT: { ArmDebugger dbg(this); PrintF("Simulator hit BKPT.\n"); dbg.Debug(); break; } default: UNIMPLEMENTED(); } } else if (instr->Bits(22, 21) == 3) { int rm = instr->RmValue(); int rd = instr->RdValue(); switch (instr->BitField(7, 4)) { case CLZ: { uint32_t bits = get_register(rm); int leading_zeros = 0; if (bits == 0) { leading_zeros = 32; } else { while ((bits & 0x80000000u) == 0) { bits <<= 1; leading_zeros++; } } set_register(rd, leading_zeros); break; } default: UNIMPLEMENTED(); } } else { PrintF("%08x\n", instr->InstructionBits()); UNIMPLEMENTED(); } } else if ((type == 1) && instr->IsNopLikeType1()) { if (instr->BitField(7, 0) == 0) { // NOP. } else if (instr->BitField(7, 0) == 20) { // CSDB. } else { PrintF("%08x\n", instr->InstructionBits()); UNIMPLEMENTED(); } } else { int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); int32_t shifter_operand = 0; bool shifter_carry_out = 0; if (type == 0) { shifter_operand = GetShiftRm(instr, &shifter_carry_out); } else { DCHECK_EQ(instr->TypeValue(), 1); shifter_operand = GetImm(instr, &shifter_carry_out); } int32_t alu_out; switch (instr->OpcodeField()) { case AND: { // Format(instr, "and'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "and'cond's 'rd, 'rn, 'imm"); alu_out = rn_val & shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case EOR: { // Format(instr, "eor'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "eor'cond's 'rd, 'rn, 'imm"); alu_out = rn_val ^ shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case SUB: { // Format(instr, "sub'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "sub'cond's 'rd, 'rn, 'imm"); alu_out = rn_val - shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(!BorrowFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); } break; } case RSB: { // Format(instr, "rsb'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "rsb'cond's 'rd, 'rn, 'imm"); alu_out = shifter_operand - rn_val; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(!BorrowFrom(shifter_operand, rn_val)); SetVFlag(OverflowFrom(alu_out, shifter_operand, rn_val, false)); } break; } case ADD: { // Format(instr, "add'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "add'cond's 'rd, 'rn, 'imm"); alu_out = rn_val + shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(CarryFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true)); } break; } case ADC: { // Format(instr, "adc'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "adc'cond's 'rd, 'rn, 'imm"); alu_out = rn_val + shifter_operand + GetCarry(); set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(CarryFrom(rn_val, shifter_operand, GetCarry())); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true)); } break; } case SBC: { // Format(instr, "sbc'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "sbc'cond's 'rd, 'rn, 'imm"); alu_out = (rn_val - shifter_operand) - (GetCarry() ? 0 : 1); set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(!BorrowFrom(rn_val, shifter_operand, GetCarry())); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); } break; } case RSC: { Format(instr, "rsc'cond's 'rd, 'rn, 'shift_rm"); Format(instr, "rsc'cond's 'rd, 'rn, 'imm"); break; } case TST: { if (instr->HasS()) { // Format(instr, "tst'cond 'rn, 'shift_rm"); // Format(instr, "tst'cond 'rn, 'imm"); alu_out = rn_val & shifter_operand; SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } else { // Format(instr, "movw'cond 'rd, 'imm"). alu_out = instr->ImmedMovwMovtValue(); set_register(rd, alu_out); } break; } case TEQ: { if (instr->HasS()) { // Format(instr, "teq'cond 'rn, 'shift_rm"); // Format(instr, "teq'cond 'rn, 'imm"); alu_out = rn_val ^ shifter_operand; SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } else { // Other instructions matching this pattern are handled in the // miscellaneous instructions part above. UNREACHABLE(); } break; } case CMP: { if (instr->HasS()) { // Format(instr, "cmp'cond 'rn, 'shift_rm"); // Format(instr, "cmp'cond 'rn, 'imm"); alu_out = rn_val - shifter_operand; SetNZFlags(alu_out); SetCFlag(!BorrowFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); } else { // Format(instr, "movt'cond 'rd, 'imm"). alu_out = (get_register(rd) & 0xFFFF) | (instr->ImmedMovwMovtValue() << 16); set_register(rd, alu_out); } break; } case CMN: { if (instr->HasS()) { // Format(instr, "cmn'cond 'rn, 'shift_rm"); // Format(instr, "cmn'cond 'rn, 'imm"); alu_out = rn_val + shifter_operand; SetNZFlags(alu_out); SetCFlag(CarryFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true)); } else { // Other instructions matching this pattern are handled in the // miscellaneous instructions part above. UNREACHABLE(); } break; } case ORR: { // Format(instr, "orr'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "orr'cond's 'rd, 'rn, 'imm"); alu_out = rn_val | shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case MOV: { // Format(instr, "mov'cond's 'rd, 'shift_rm"); // Format(instr, "mov'cond's 'rd, 'imm"); alu_out = shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case BIC: { // Format(instr, "bic'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "bic'cond's 'rd, 'rn, 'imm"); alu_out = rn_val & ~shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case MVN: { // Format(instr, "mvn'cond's 'rd, 'shift_rm"); // Format(instr, "mvn'cond's 'rd, 'imm"); alu_out = ~shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } default: { UNREACHABLE(); break; } } } } void Simulator::DecodeType2(Instruction* instr) { int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); int32_t im_val = instr->Offset12Value(); int32_t addr = 0; switch (instr->PUField()) { case da_x: { // Format(instr, "'memop'cond'b 'rd, ['rn], #-'off12"); DCHECK(!instr->HasW()); addr = rn_val; rn_val -= im_val; set_register(rn, rn_val); break; } case ia_x: { // Format(instr, "'memop'cond'b 'rd, ['rn], #+'off12"); DCHECK(!instr->HasW()); addr = rn_val; rn_val += im_val; set_register(rn, rn_val); break; } case db_x: { // Format(instr, "'memop'cond'b 'rd, ['rn, #-'off12]'w"); rn_val -= im_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } case ib_x: { // Format(instr, "'memop'cond'b 'rd, ['rn, #+'off12]'w"); rn_val += im_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } default: { UNREACHABLE(); break; } } if (instr->HasB()) { if (instr->HasL()) { byte val = ReadBU(addr); set_register(rd, val); } else { byte val = get_register(rd); WriteB(addr, val); } } else { if (instr->HasL()) { set_register(rd, ReadW(addr)); } else { WriteW(addr, get_register(rd)); } } } void Simulator::DecodeType3(Instruction* instr) { int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); bool shifter_carry_out = 0; int32_t shifter_operand = GetShiftRm(instr, &shifter_carry_out); int32_t addr = 0; switch (instr->PUField()) { case da_x: { DCHECK(!instr->HasW()); Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm"); UNIMPLEMENTED(); break; } case ia_x: { if (instr->Bit(4) == 0) { // Memop. } else { if (instr->Bit(5) == 0) { switch (instr->Bits(22, 21)) { case 0: if (instr->Bit(20) == 0) { if (instr->Bit(6) == 0) { // Pkhbt. uint32_t rn_val = get_register(rn); uint32_t rm_val = get_register(instr->RmValue()); int32_t shift = instr->Bits(11, 7); rm_val <<= shift; set_register(rd, (rn_val & 0xFFFF) | (rm_val & 0xFFFF0000U)); } else { // Pkhtb. uint32_t rn_val = get_register(rn); int32_t rm_val = get_register(instr->RmValue()); int32_t shift = instr->Bits(11, 7); if (shift == 0) { shift = 32; } rm_val >>= shift; set_register(rd, (rn_val & 0xFFFF0000U) | (rm_val & 0xFFFF)); } } else { UNIMPLEMENTED(); } break; case 1: UNIMPLEMENTED(); break; case 2: UNIMPLEMENTED(); break; case 3: { // Usat. int32_t sat_pos = instr->Bits(20, 16); int32_t sat_val = (1 << sat_pos) - 1; int32_t shift = instr->Bits(11, 7); int32_t shift_type = instr->Bit(6); int32_t rm_val = get_register(instr->RmValue()); if (shift_type == 0) { // LSL rm_val <<= shift; } else { // ASR rm_val >>= shift; } // If saturation occurs, the Q flag should be set in the CPSR. // There is no Q flag yet, and no instruction (MRS) to read the // CPSR directly. if (rm_val > sat_val) { rm_val = sat_val; } else if (rm_val < 0) { rm_val = 0; } set_register(rd, rm_val); break; } } } else { switch (instr->Bits(22, 21)) { case 0: UNIMPLEMENTED(); break; case 1: if (instr->Bits(9, 6) == 1) { if (instr->Bit(20) == 0) { if (instr->Bits(19, 16) == 0xF) { // Sxtb. int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, static_cast(rm_val)); } else { // Sxtab. int32_t rn_val = get_register(rn); int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + static_cast(rm_val)); } } else { if (instr->Bits(19, 16) == 0xF) { // Sxth. int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, static_cast(rm_val)); } else { // Sxtah. int32_t rn_val = get_register(rn); int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + static_cast(rm_val)); } } } else if (instr->Bits(27, 16) == 0x6BF && instr->Bits(11, 4) == 0xF3) { // Rev. uint32_t rm_val = get_register(instr->RmValue()); set_register(rd, ByteReverse(rm_val)); } else { UNREACHABLE(); } break; case 2: if ((instr->Bit(20) == 0) && (instr->Bits(9, 6) == 1)) { if (instr->Bits(19, 16) == 0xF) { // Uxtb16. uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, (rm_val & 0xFF) | (rm_val & 0xFF0000)); } else { UNIMPLEMENTED(); } } else { UNIMPLEMENTED(); } break; case 3: if ((instr->Bits(9, 6) == 1)) { if (instr->Bit(20) == 0) { if (instr->Bits(19, 16) == 0xF) { // Uxtb. uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, (rm_val & 0xFF)); } else { // Uxtab. uint32_t rn_val = get_register(rn); uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + (rm_val & 0xFF)); } } else { if (instr->Bits(19, 16) == 0xF) { // Uxth. uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, (rm_val & 0xFFFF)); } else { // Uxtah. uint32_t rn_val = get_register(rn); uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + (rm_val & 0xFFFF)); } } } else { // PU == 0b01, BW == 0b11, Bits(9, 6) != 0b0001 if ((instr->Bits(20, 16) == 0x1F) && (instr->Bits(11, 4) == 0xF3)) { // Rbit. uint32_t rm_val = get_register(instr->RmValue()); set_register(rd, base::bits::ReverseBits(rm_val)); } else { UNIMPLEMENTED(); } } break; } } return; } break; } case db_x: { if (instr->Bits(22, 20) == 0x5) { if (instr->Bits(7, 4) == 0x1) { int rm = instr->RmValue(); int32_t rm_val = get_register(rm); int rs = instr->RsValue(); int32_t rs_val = get_register(rs); if (instr->Bits(15, 12) == 0xF) { // SMMUL (in V8 notation matching ARM ISA format) // Format(instr, "smmul'cond 'rn, 'rm, 'rs"); rn_val = base::bits::SignedMulHigh32(rm_val, rs_val); } else { // SMMLA (in V8 notation matching ARM ISA format) // Format(instr, "smmla'cond 'rn, 'rm, 'rs, 'rd"); int rd = instr->RdValue(); int32_t rd_val = get_register(rd); rn_val = base::bits::SignedMulHighAndAdd32(rm_val, rs_val, rd_val); } set_register(rn, rn_val); return; } } if (instr->Bits(5, 4) == 0x1) { if ((instr->Bit(22) == 0x0) && (instr->Bit(20) == 0x1)) { // (s/u)div (in V8 notation matching ARM ISA format) rn = rm/rs // Format(instr, "'(s/u)div'cond'b 'rn, 'rm, 'rs); int rm = instr->RmValue(); int32_t rm_val = get_register(rm); int rs = instr->RsValue(); int32_t rs_val = get_register(rs); int32_t ret_val = 0; // udiv if (instr->Bit(21) == 0x1) { ret_val = bit_cast(base::bits::UnsignedDiv32( bit_cast(rm_val), bit_cast(rs_val))); } else { ret_val = base::bits::SignedDiv32(rm_val, rs_val); } set_register(rn, ret_val); return; } } // Format(instr, "'memop'cond'b 'rd, ['rn, -'shift_rm]'w"); addr = rn_val - shifter_operand; if (instr->HasW()) { set_register(rn, addr); } break; } case ib_x: { if (instr->HasW() && (instr->Bits(6, 4) == 0x5)) { uint32_t widthminus1 = static_cast(instr->Bits(20, 16)); uint32_t lsbit = static_cast(instr->Bits(11, 7)); uint32_t msbit = widthminus1 + lsbit; if (msbit <= 31) { if (instr->Bit(22)) { // ubfx - unsigned bitfield extract. uint32_t rm_val = static_cast(get_register(instr->RmValue())); uint32_t extr_val = rm_val << (31 - msbit); extr_val = extr_val >> (31 - widthminus1); set_register(instr->RdValue(), extr_val); } else { // sbfx - signed bitfield extract. int32_t rm_val = get_register(instr->RmValue()); int32_t extr_val = rm_val << (31 - msbit); extr_val = extr_val >> (31 - widthminus1); set_register(instr->RdValue(), extr_val); } } else { UNREACHABLE(); } return; } else if (!instr->HasW() && (instr->Bits(6, 4) == 0x1)) { uint32_t lsbit = static_cast(instr->Bits(11, 7)); uint32_t msbit = static_cast(instr->Bits(20, 16)); if (msbit >= lsbit) { // bfc or bfi - bitfield clear/insert. uint32_t rd_val = static_cast(get_register(instr->RdValue())); uint32_t bitcount = msbit - lsbit + 1; uint32_t mask = 0xFFFFFFFFu >> (32 - bitcount); rd_val &= ~(mask << lsbit); if (instr->RmValue() != 15) { // bfi - bitfield insert. uint32_t rm_val = static_cast(get_register(instr->RmValue())); rm_val &= mask; rd_val |= rm_val << lsbit; } set_register(instr->RdValue(), rd_val); } else { UNREACHABLE(); } return; } else { // Format(instr, "'memop'cond'b 'rd, ['rn, +'shift_rm]'w"); addr = rn_val + shifter_operand; if (instr->HasW()) { set_register(rn, addr); } } break; } default: { UNREACHABLE(); break; } } if (instr->HasB()) { if (instr->HasL()) { uint8_t byte = ReadB(addr); set_register(rd, byte); } else { uint8_t byte = get_register(rd); WriteB(addr, byte); } } else { if (instr->HasL()) { set_register(rd, ReadW(addr)); } else { WriteW(addr, get_register(rd)); } } } void Simulator::DecodeType4(Instruction* instr) { DCHECK_EQ(instr->Bit(22), 0); // only allowed to be set in privileged mode if (instr->HasL()) { // Format(instr, "ldm'cond'pu 'rn'w, 'rlist"); HandleRList(instr, true); } else { // Format(instr, "stm'cond'pu 'rn'w, 'rlist"); HandleRList(instr, false); } } void Simulator::DecodeType5(Instruction* instr) { // Format(instr, "b'l'cond 'target"); int off = (instr->SImmed24Value() << 2); intptr_t pc_address = get_pc(); if (instr->HasLink()) { set_register(lr, pc_address + kInstrSize); } int pc_reg = get_register(pc); set_pc(pc_reg + off); } void Simulator::DecodeType6(Instruction* instr) { DecodeType6CoprocessorIns(instr); } void Simulator::DecodeType7(Instruction* instr) { if (instr->Bit(24) == 1) { SoftwareInterrupt(instr); } else { switch (instr->CoprocessorValue()) { case 10: // Fall through. case 11: DecodeTypeVFP(instr); break; case 15: DecodeTypeCP15(instr); break; default: UNIMPLEMENTED(); } } } // void Simulator::DecodeTypeVFP(Instruction* instr) // The Following ARMv7 VFPv instructions are currently supported. // vmov :Sn = Rt // vmov :Rt = Sn // vcvt: Dd = Sm // vcvt: Sd = Dm // vcvt.f64.s32 Dd, Dd, # // Dd = vabs(Dm) // Sd = vabs(Sm) // Dd = vneg(Dm) // Sd = vneg(Sm) // Dd = vadd(Dn, Dm) // Sd = vadd(Sn, Sm) // Dd = vsub(Dn, Dm) // Sd = vsub(Sn, Sm) // Dd = vmul(Dn, Dm) // Sd = vmul(Sn, Sm) // Dd = vdiv(Dn, Dm) // Sd = vdiv(Sn, Sm) // vcmp(Dd, Dm) // vcmp(Sd, Sm) // Dd = vsqrt(Dm) // Sd = vsqrt(Sm) // vmrs // vdup.size Qd, Rt. void Simulator::DecodeTypeVFP(Instruction* instr) { DCHECK((instr->TypeValue() == 7) && (instr->Bit(24) == 0x0) ); DCHECK_EQ(instr->Bits(11, 9), 0x5); // Obtain single precision register codes. int m = instr->VFPMRegValue(kSinglePrecision); int d = instr->VFPDRegValue(kSinglePrecision); int n = instr->VFPNRegValue(kSinglePrecision); // Obtain double precision register codes. int vm = instr->VFPMRegValue(kDoublePrecision); int vd = instr->VFPDRegValue(kDoublePrecision); int vn = instr->VFPNRegValue(kDoublePrecision); if (instr->Bit(4) == 0) { if (instr->Opc1Value() == 0x7) { // Other data processing instructions if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x1)) { // vmov register to register. if (instr->SzValue() == 0x1) { uint32_t data[2]; get_d_register(vm, data); set_d_register(vd, data); } else { set_s_register(d, get_s_register(m)); } } else if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x3)) { // vabs if (instr->SzValue() == 0x1) { Float64 dm = get_double_from_d_register(vm); constexpr uint64_t kSignBit64 = uint64_t{1} << 63; Float64 dd = Float64::FromBits(dm.get_bits() & ~kSignBit64); dd = canonicalizeNaN(dd); set_d_register_from_double(vd, dd); } else { Float32 sm = get_float_from_s_register(m); constexpr uint32_t kSignBit32 = uint32_t{1} << 31; Float32 sd = Float32::FromBits(sm.get_bits() & ~kSignBit32); sd = canonicalizeNaN(sd); set_s_register_from_float(d, sd); } } else if ((instr->Opc2Value() == 0x1) && (instr->Opc3Value() == 0x1)) { // vneg if (instr->SzValue() == 0x1) { Float64 dm = get_double_from_d_register(vm); constexpr uint64_t kSignBit64 = uint64_t{1} << 63; Float64 dd = Float64::FromBits(dm.get_bits() ^ kSignBit64); dd = canonicalizeNaN(dd); set_d_register_from_double(vd, dd); } else { Float32 sm = get_float_from_s_register(m); constexpr uint32_t kSignBit32 = uint32_t{1} << 31; Float32 sd = Float32::FromBits(sm.get_bits() ^ kSignBit32); sd = canonicalizeNaN(sd); set_s_register_from_float(d, sd); } } else if ((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3)) { DecodeVCVTBetweenDoubleAndSingle(instr); } else if ((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) { DecodeVCVTBetweenFloatingPointAndInteger(instr); } else if ((instr->Opc2Value() == 0xA) && (instr->Opc3Value() == 0x3) && (instr->Bit(8) == 1)) { // vcvt.f64.s32 Dd, Dd, # int fraction_bits = 32 - ((instr->Bits(3, 0) << 1) | instr->Bit(5)); int fixed_value = get_sinteger_from_s_register(vd * 2); double divide = 1 << fraction_bits; set_d_register_from_double(vd, fixed_value / divide); } else if (((instr->Opc2Value() >> 1) == 0x6) && (instr->Opc3Value() & 0x1)) { DecodeVCVTBetweenFloatingPointAndInteger(instr); } else if (((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) && (instr->Opc3Value() & 0x1)) { DecodeVCMP(instr); } else if (((instr->Opc2Value() == 0x1)) && (instr->Opc3Value() == 0x3)) { // vsqrt lazily_initialize_fast_sqrt(isolate_); if (instr->SzValue() == 0x1) { double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = fast_sqrt(dm_value, isolate_); dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = fast_sqrt(sm_value, isolate_); sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else if (instr->Opc3Value() == 0x0) { // vmov immediate. if (instr->SzValue() == 0x1) { set_d_register_from_double(vd, instr->DoubleImmedVmov()); } else { // Cast double to float. float value = instr->DoubleImmedVmov().get_scalar(); set_s_register_from_float(d, value); } } else if (((instr->Opc2Value() == 0x6)) && (instr->Opc3Value() == 0x3)) { // vrintz - truncate if (instr->SzValue() == 0x1) { double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = trunc(dm_value); dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = truncf(sm_value); sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else { UNREACHABLE(); // Not used by V8. } } else if (instr->Opc1Value() == 0x3) { if (instr->Opc3Value() & 0x1) { // vsub if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value - dm_value; dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value - sm_value; sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else { // vadd if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value + dm_value; dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value + sm_value; sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } } else if ((instr->Opc1Value() == 0x2) && !(instr->Opc3Value() & 0x1)) { // vmul if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value * dm_value; dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value * sm_value; sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else if ((instr->Opc1Value() == 0x0)) { // vmla, vmls const bool is_vmls = (instr->Opc3Value() & 0x1); if (instr->SzValue() == 0x1) { const double dd_val = get_double_from_d_register(vd).get_scalar(); const double dn_val = get_double_from_d_register(vn).get_scalar(); const double dm_val = get_double_from_d_register(vm).get_scalar(); // Note: we do the mul and add/sub in separate steps to avoid getting a // result with too high precision. const double res = dn_val * dm_val; set_d_register_from_double(vd, res); if (is_vmls) { set_d_register_from_double(vd, canonicalizeNaN(dd_val - res)); } else { set_d_register_from_double(vd, canonicalizeNaN(dd_val + res)); } } else { const float sd_val = get_float_from_s_register(d).get_scalar(); const float sn_val = get_float_from_s_register(n).get_scalar(); const float sm_val = get_float_from_s_register(m).get_scalar(); // Note: we do the mul and add/sub in separate steps to avoid getting a // result with too high precision. const float res = sn_val * sm_val; set_s_register_from_float(d, res); if (is_vmls) { set_s_register_from_float(d, canonicalizeNaN(sd_val - res)); } else { set_s_register_from_float(d, canonicalizeNaN(sd_val + res)); } } } else if ((instr->Opc1Value() == 0x4) && !(instr->Opc3Value() & 0x1)) { // vdiv if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value / dm_value; div_zero_vfp_flag_ = (dm_value == 0); dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value / sm_value; div_zero_vfp_flag_ = (sm_value == 0); sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else { UNIMPLEMENTED(); // Not used by V8. } } else { if ((instr->VCValue() == 0x0) && (instr->VAValue() == 0x0)) { DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(instr); } else if ((instr->VLValue() == 0x0) && (instr->VCValue() == 0x1)) { if (instr->Bit(23) == 0) { // vmov (ARM core register to scalar) int vd = instr->VFPNRegValue(kDoublePrecision); int rt = instr->RtValue(); int opc1_opc2 = (instr->Bits(22, 21) << 2) | instr->Bits(6, 5); if ((opc1_opc2 & 0xB) == 0) { // NeonS32/NeonU32 uint32_t data[2]; get_d_register(vd, data); data[instr->Bit(21)] = get_register(rt); set_d_register(vd, data); } else { uint64_t data; get_d_register(vd, &data); uint64_t rt_value = get_register(rt); if ((opc1_opc2 & 0x8) != 0) { // NeonS8 / NeonU8 int i = opc1_opc2 & 0x7; int shift = i * kBitsPerByte; const uint64_t mask = 0xFF; data &= ~(mask << shift); data |= (rt_value & mask) << shift; set_d_register(vd, &data); } else if ((opc1_opc2 & 0x1) != 0) { // NeonS16 / NeonU16 int i = (opc1_opc2 >> 1) & 0x3; int shift = i * kBitsPerByte * kShortSize; const uint64_t mask = 0xFFFF; data &= ~(mask << shift); data |= (rt_value & mask) << shift; set_d_register(vd, &data); } else { UNREACHABLE(); // Not used by V8. } } } else { // vdup.size Qd, Rt. NeonSize size = Neon32; if (instr->Bit(5) != 0) size = Neon16; else if (instr->Bit(22) != 0) size = Neon8; int vd = instr->VFPNRegValue(kSimd128Precision); int rt = instr->RtValue(); uint32_t rt_value = get_register(rt); uint32_t q_data[4]; switch (size) { case Neon8: { rt_value &= 0xFF; uint8_t* dst = reinterpret_cast(q_data); for (int i = 0; i < 16; i++) { dst[i] = rt_value; } break; } case Neon16: { // Perform pairwise op. rt_value &= 0xFFFFu; uint32_t rt_rt = (rt_value << 16) | (rt_value & 0xFFFFu); for (int i = 0; i < 4; i++) { q_data[i] = rt_rt; } break; } case Neon32: { for (int i = 0; i < 4; i++) { q_data[i] = rt_value; } break; } default: UNREACHABLE(); break; } set_neon_register(vd, q_data); } } else if ((instr->VLValue() == 0x1) && (instr->VCValue() == 0x1)) { // vmov (scalar to ARM core register) int vn = instr->VFPNRegValue(kDoublePrecision); int rt = instr->RtValue(); int opc1_opc2 = (instr->Bits(22, 21) << 2) | instr->Bits(6, 5); uint64_t data; get_d_register(vn, &data); if ((opc1_opc2 & 0xB) == 0) { // NeonS32 / NeonU32 int32_t int_data[2]; memcpy(int_data, &data, sizeof(int_data)); set_register(rt, int_data[instr->Bit(21)]); } else { uint64_t data; get_d_register(vn, &data); bool u = instr->Bit(23) != 0; if ((opc1_opc2 & 0x8) != 0) { // NeonS8 / NeonU8 int i = opc1_opc2 & 0x7; int shift = i * kBitsPerByte; uint32_t scalar = (data >> shift) & 0xFFu; if (!u && (scalar & 0x80) != 0) scalar |= 0xFFFFFF00; set_register(rt, scalar); } else if ((opc1_opc2 & 0x1) != 0) { // NeonS16 / NeonU16 int i = (opc1_opc2 >> 1) & 0x3; int shift = i * kBitsPerByte * kShortSize; uint32_t scalar = (data >> shift) & 0xFFFFu; if (!u && (scalar & 0x8000) != 0) scalar |= 0xFFFF0000; set_register(rt, scalar); } else { UNREACHABLE(); // Not used by V8. } } } else if ((instr->VLValue() == 0x1) && (instr->VCValue() == 0x0) && (instr->VAValue() == 0x7) && (instr->Bits(19, 16) == 0x1)) { // vmrs uint32_t rt = instr->RtValue(); if (rt == 0xF) { Copy_FPSCR_to_APSR(); } else { // Emulate FPSCR from the Simulator flags. uint32_t fpscr = (n_flag_FPSCR_ << 31) | (z_flag_FPSCR_ << 30) | (c_flag_FPSCR_ << 29) | (v_flag_FPSCR_ << 28) | (FPSCR_default_NaN_mode_ << 25) | (inexact_vfp_flag_ << 4) | (underflow_vfp_flag_ << 3) | (overflow_vfp_flag_ << 2) | (div_zero_vfp_flag_ << 1) | (inv_op_vfp_flag_ << 0) | (FPSCR_rounding_mode_); set_register(rt, fpscr); } } else if ((instr->VLValue() == 0x0) && (instr->VCValue() == 0x0) && (instr->VAValue() == 0x7) && (instr->Bits(19, 16) == 0x1)) { // vmsr uint32_t rt = instr->RtValue(); if (rt == pc) { UNREACHABLE(); } else { uint32_t rt_value = get_register(rt); n_flag_FPSCR_ = (rt_value >> 31) & 1; z_flag_FPSCR_ = (rt_value >> 30) & 1; c_flag_FPSCR_ = (rt_value >> 29) & 1; v_flag_FPSCR_ = (rt_value >> 28) & 1; FPSCR_default_NaN_mode_ = (rt_value >> 25) & 1; inexact_vfp_flag_ = (rt_value >> 4) & 1; underflow_vfp_flag_ = (rt_value >> 3) & 1; overflow_vfp_flag_ = (rt_value >> 2) & 1; div_zero_vfp_flag_ = (rt_value >> 1) & 1; inv_op_vfp_flag_ = (rt_value >> 0) & 1; FPSCR_rounding_mode_ = static_cast((rt_value) & kVFPRoundingModeMask); } } else { UNIMPLEMENTED(); // Not used by V8. } } } void Simulator::DecodeTypeCP15(Instruction* instr) { DCHECK((instr->TypeValue() == 7) && (instr->Bit(24) == 0x0)); DCHECK_EQ(instr->CoprocessorValue(), 15); if (instr->Bit(4) == 1) { // mcr int crn = instr->Bits(19, 16); int crm = instr->Bits(3, 0); int opc1 = instr->Bits(23, 21); int opc2 = instr->Bits(7, 5); if ((opc1 == 0) && (crn == 7)) { // ARMv6 memory barrier operations. // Details available in ARM DDI 0406C.b, B3-1750. if (((crm == 10) && (opc2 == 5)) || // CP15DMB ((crm == 10) && (opc2 == 4)) || // CP15DSB ((crm == 5) && (opc2 == 4))) { // CP15ISB // These are ignored by the simulator for now. } else { UNIMPLEMENTED(); } } } else { UNIMPLEMENTED(); } } void Simulator::DecodeVMOVBetweenCoreAndSinglePrecisionRegisters( Instruction* instr) { DCHECK((instr->Bit(4) == 1) && (instr->VCValue() == 0x0) && (instr->VAValue() == 0x0)); int t = instr->RtValue(); int n = instr->VFPNRegValue(kSinglePrecision); bool to_arm_register = (instr->VLValue() == 0x1); if (to_arm_register) { int32_t int_value = get_sinteger_from_s_register(n); set_register(t, int_value); } else { int32_t rs_val = get_register(t); set_s_register_from_sinteger(n, rs_val); } } void Simulator::DecodeVCMP(Instruction* instr) { DCHECK((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7)); DCHECK(((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) && (instr->Opc3Value() & 0x1)); // Comparison. VFPRegPrecision precision = kSinglePrecision; if (instr->SzValue() == 0x1) { precision = kDoublePrecision; } int d = instr->VFPDRegValue(precision); int m = 0; if (instr->Opc2Value() == 0x4) { m = instr->VFPMRegValue(precision); } if (precision == kDoublePrecision) { double dd_value = get_double_from_d_register(d).get_scalar(); double dm_value = 0.0; if (instr->Opc2Value() == 0x4) { dm_value = get_double_from_d_register(m).get_scalar(); } // Raise exceptions for quiet NaNs if necessary. if (instr->Bit(7) == 1) { if (std::isnan(dd_value)) { inv_op_vfp_flag_ = true; } } Compute_FPSCR_Flags(dd_value, dm_value); } else { float sd_value = get_float_from_s_register(d).get_scalar(); float sm_value = 0.0; if (instr->Opc2Value() == 0x4) { sm_value = get_float_from_s_register(m).get_scalar(); } // Raise exceptions for quiet NaNs if necessary. if (instr->Bit(7) == 1) { if (std::isnan(sd_value)) { inv_op_vfp_flag_ = true; } } Compute_FPSCR_Flags(sd_value, sm_value); } } void Simulator::DecodeVCVTBetweenDoubleAndSingle(Instruction* instr) { DCHECK((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7)); DCHECK((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3)); VFPRegPrecision dst_precision = kDoublePrecision; VFPRegPrecision src_precision = kSinglePrecision; if (instr->SzValue() == 1) { dst_precision = kSinglePrecision; src_precision = kDoublePrecision; } int dst = instr->VFPDRegValue(dst_precision); int src = instr->VFPMRegValue(src_precision); if (dst_precision == kSinglePrecision) { double val = get_double_from_d_register(src).get_scalar(); set_s_register_from_float(dst, static_cast(val)); } else { float val = get_float_from_s_register(src).get_scalar(); set_d_register_from_double(dst, static_cast(val)); } } bool get_inv_op_vfp_flag(VFPRoundingMode mode, double val, bool unsigned_) { DCHECK((mode == RN) || (mode == RM) || (mode == RZ)); double max_uint = static_cast(0xFFFFFFFFu); double max_int = static_cast(kMaxInt); double min_int = static_cast(kMinInt); // Check for NaN. if (val != val) { return true; } // Check for overflow. This code works because 32bit integers can be // exactly represented by ieee-754 64bit floating-point values. switch (mode) { case RN: return unsigned_ ? (val >= (max_uint + 0.5)) || (val < -0.5) : (val >= (max_int + 0.5)) || (val < (min_int - 0.5)); case RM: return unsigned_ ? (val >= (max_uint + 1.0)) || (val < 0) : (val >= (max_int + 1.0)) || (val < min_int); case RZ: return unsigned_ ? (val >= (max_uint + 1.0)) || (val <= -1) : (val >= (max_int + 1.0)) || (val <= (min_int - 1.0)); default: UNREACHABLE(); } } // We call this function only if we had a vfp invalid exception. // It returns the correct saturated value. int VFPConversionSaturate(double val, bool unsigned_res) { if (val != val) { return 0; } else { if (unsigned_res) { return (val < 0) ? 0 : 0xFFFFFFFFu; } else { return (val < 0) ? kMinInt : kMaxInt; } } } int32_t Simulator::ConvertDoubleToInt(double val, bool unsigned_integer, VFPRoundingMode mode) { // TODO(jkummerow): These casts are undefined behavior if the integral // part of {val} does not fit into the destination type. int32_t result = unsigned_integer ? static_cast(val) : static_cast(val); inv_op_vfp_flag_ = get_inv_op_vfp_flag(mode, val, unsigned_integer); double abs_diff = unsigned_integer ? std::fabs(val - static_cast(result)) : std::fabs(val - result); inexact_vfp_flag_ = (abs_diff != 0); if (inv_op_vfp_flag_) { result = VFPConversionSaturate(val, unsigned_integer); } else { switch (mode) { case RN: { int val_sign = (val > 0) ? 1 : -1; if (abs_diff > 0.5) { result += val_sign; } else if (abs_diff == 0.5) { // Round to even if exactly halfway. result = ((result % 2) == 0) ? result : result + val_sign; } break; } case RM: result = result > val ? result - 1 : result; break; case RZ: // Nothing to do. break; default: UNREACHABLE(); } } return result; } void Simulator::DecodeVCVTBetweenFloatingPointAndInteger(Instruction* instr) { DCHECK((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7) && (instr->Bits(27, 23) == 0x1D)); DCHECK(((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) || (((instr->Opc2Value() >> 1) == 0x6) && (instr->Opc3Value() & 0x1))); // Conversion between floating-point and integer. bool to_integer = (instr->Bit(18) == 1); VFPRegPrecision src_precision = (instr->SzValue() == 1) ? kDoublePrecision : kSinglePrecision; if (to_integer) { // We are playing with code close to the C++ standard's limits below, // hence the very simple code and heavy checks. // // Note: // C++ defines default type casting from floating point to integer as // (close to) rounding toward zero ("fractional part discarded"). int dst = instr->VFPDRegValue(kSinglePrecision); int src = instr->VFPMRegValue(src_precision); // Bit 7 in vcvt instructions indicates if we should use the FPSCR rounding // mode or the default Round to Zero mode. VFPRoundingMode mode = (instr->Bit(7) != 1) ? FPSCR_rounding_mode_ : RZ; DCHECK((mode == RM) || (mode == RZ) || (mode == RN)); bool unsigned_integer = (instr->Bit(16) == 0); bool double_precision = (src_precision == kDoublePrecision); double val = double_precision ? get_double_from_d_register(src).get_scalar() : get_float_from_s_register(src).get_scalar(); int32_t temp = ConvertDoubleToInt(val, unsigned_integer, mode); // Update the destination register. set_s_register_from_sinteger(dst, temp); } else { bool unsigned_integer = (instr->Bit(7) == 0); int dst = instr->VFPDRegValue(src_precision); int src = instr->VFPMRegValue(kSinglePrecision); int val = get_sinteger_from_s_register(src); if (src_precision == kDoublePrecision) { if (unsigned_integer) { set_d_register_from_double( dst, static_cast(static_cast(val))); } else { set_d_register_from_double(dst, static_cast(val)); } } else { if (unsigned_integer) { set_s_register_from_float( dst, static_cast(static_cast(val))); } else { set_s_register_from_float(dst, static_cast(val)); } } } } // void Simulator::DecodeType6CoprocessorIns(Instruction* instr) // Decode Type 6 coprocessor instructions. // Dm = vmov(Rt, Rt2) // = vmov(Dm) // Ddst = MEM(Rbase + 4*offset). // MEM(Rbase + 4*offset) = Dsrc. void Simulator::DecodeType6CoprocessorIns(Instruction* instr) { DCHECK_EQ(instr->TypeValue(), 6); if (instr->CoprocessorValue() == 0xA) { switch (instr->OpcodeValue()) { case 0x8: case 0xA: case 0xC: case 0xE: { // Load and store single precision float to memory. int rn = instr->RnValue(); int vd = instr->VFPDRegValue(kSinglePrecision); int offset = instr->Immed8Value(); if (!instr->HasU()) { offset = -offset; } int32_t address = get_register(rn) + 4 * offset; // Load and store address for singles must be at least four-byte // aligned. DCHECK_EQ(address % 4, 0); if (instr->HasL()) { // Load single from memory: vldr. set_s_register_from_sinteger(vd, ReadW(address)); } else { // Store single to memory: vstr. WriteW(address, get_sinteger_from_s_register(vd)); } break; } case 0x4: case 0x5: case 0x6: case 0x7: case 0x9: case 0xB: // Load/store multiple single from memory: vldm/vstm. HandleVList(instr); break; default: UNIMPLEMENTED(); // Not used by V8. } } else if (instr->CoprocessorValue() == 0xB) { switch (instr->OpcodeValue()) { case 0x2: // Load and store double to two GP registers if (instr->Bits(7, 6) != 0 || instr->Bit(4) != 1) { UNIMPLEMENTED(); // Not used by V8. } else { int rt = instr->RtValue(); int rn = instr->RnValue(); int vm = instr->VFPMRegValue(kDoublePrecision); if (instr->HasL()) { uint32_t data[2]; get_d_register(vm, data); set_register(rt, data[0]); set_register(rn, data[1]); } else { int32_t data[] = { get_register(rt), get_register(rn) }; set_d_register(vm, reinterpret_cast(data)); } } break; case 0x8: case 0xA: case 0xC: case 0xE: { // Load and store double to memory. int rn = instr->RnValue(); int vd = instr->VFPDRegValue(kDoublePrecision); int offset = instr->Immed8Value(); if (!instr->HasU()) { offset = -offset; } int32_t address = get_register(rn) + 4 * offset; // Load and store address for doubles must be at least four-byte // aligned. DCHECK_EQ(address % 4, 0); if (instr->HasL()) { // Load double from memory: vldr. int32_t data[] = {ReadW(address), ReadW(address + 4)}; set_d_register(vd, reinterpret_cast(data)); } else { // Store double to memory: vstr. uint32_t data[2]; get_d_register(vd, data); WriteW(address, data[0]); WriteW(address + 4, data[1]); } break; } case 0x4: case 0x5: case 0x6: case 0x7: case 0x9: case 0xB: // Load/store multiple double from memory: vldm/vstm. HandleVList(instr); break; default: UNIMPLEMENTED(); // Not used by V8. } } else { UNIMPLEMENTED(); // Not used by V8. } } // Templated operations for NEON instructions. template U Widen(T value) { static_assert(sizeof(int64_t) > sizeof(T), "T must be int32_t or smaller"); static_assert(sizeof(U) > sizeof(T), "T must smaller than U"); return static_cast(value); } template U Narrow(T value) { static_assert(sizeof(int8_t) < sizeof(T), "T must be int16_t or larger"); static_assert(sizeof(U) < sizeof(T), "T must larger than U"); static_assert(std::is_unsigned() == std::is_unsigned(), "Signed-ness of T and U must match"); // Make sure value can be expressed in the smaller type; otherwise, the // casted result is implementation defined. DCHECK_LE(std::numeric_limits::min(), value); DCHECK_GE(std::numeric_limits::max(), value); return static_cast(value); } template T Clamp(int64_t value) { static_assert(sizeof(int64_t) > sizeof(T), "T must be int32_t or smaller"); int64_t min = static_cast(std::numeric_limits::min()); int64_t max = static_cast(std::numeric_limits::max()); int64_t clamped = std::max(min, std::min(max, value)); return static_cast(clamped); } template void Widen(Simulator* simulator, int Vd, int Vm) { static const int kLanes = 8 / sizeof(T); T src[kLanes]; U dst[kLanes]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kLanes; i++) { dst[i] = Widen(src[i]); } simulator->set_neon_register(Vd, dst); } template void Abs(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); T src[kElems]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kElems; i++) { src[i] = std::abs(src[i]); } simulator->set_neon_register(Vd, src); } template void Neg(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); T src[kElems]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kElems; i++) { src[i] = -src[i]; } simulator->set_neon_register(Vd, src); } template void SaturatingNarrow(Simulator* simulator, int Vd, int Vm) { static const int kLanes = 16 / sizeof(T); T src[kLanes]; U dst[kLanes]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kLanes; i++) { dst[i] = Narrow(Clamp(src[i])); } simulator->set_neon_register(Vd, dst); } template void AddSaturate(Simulator* simulator, int Vd, int Vm, int Vn) { static const int kLanes = 16 / sizeof(T); T src1[kLanes], src2[kLanes]; simulator->get_neon_register(Vn, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kLanes; i++) { src1[i] = Clamp(Widen(src1[i]) + Widen(src2[i])); } simulator->set_neon_register(Vd, src1); } template void SubSaturate(Simulator* simulator, int Vd, int Vm, int Vn) { static const int kLanes = 16 / sizeof(T); T src1[kLanes], src2[kLanes]; simulator->get_neon_register(Vn, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kLanes; i++) { src1[i] = Clamp(Widen(src1[i]) - Widen(src2[i])); } simulator->set_neon_register(Vd, src1); } template void Zip(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); static const int kPairs = kElems / 2; T src1[kElems], src2[kElems], dst1[kElems], dst2[kElems]; simulator->get_neon_register(Vd, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kPairs; i++) { dst1[i * 2] = src1[i]; dst1[i * 2 + 1] = src2[i]; dst2[i * 2] = src1[i + kPairs]; dst2[i * 2 + 1] = src2[i + kPairs]; } simulator->set_neon_register(Vd, dst1); simulator->set_neon_register(Vm, dst2); } template void Unzip(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); static const int kPairs = kElems / 2; T src1[kElems], src2[kElems], dst1[kElems], dst2[kElems]; simulator->get_neon_register(Vd, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kPairs; i++) { dst1[i] = src1[i * 2]; dst1[i + kPairs] = src2[i * 2]; dst2[i] = src1[i * 2 + 1]; dst2[i + kPairs] = src2[i * 2 + 1]; } simulator->set_neon_register(Vd, dst1); simulator->set_neon_register(Vm, dst2); } template void Transpose(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); static const int kPairs = kElems / 2; T src1[kElems], src2[kElems]; simulator->get_neon_register(Vd, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kPairs; i++) { std::swap(src1[2 * i + 1], src2[2 * i]); } simulator->set_neon_register(Vd, src1); simulator->set_neon_register
: enables / disables\n"); PrintF(" all or number stop(s)\n"); PrintF(" stop unstop\n"); PrintF(" ignore the stop instruction at the current location\n"); PrintF(" from now on\n"); } else { PrintF("Unknown command: %s\n", cmd); } } } // Add all the breakpoints back to stop execution and enter the debugger // shell when hit. RedoBreakpoints(); #undef COMMAND_SIZE #undef ARG_SIZE #undef STR #undef XSTR } bool Simulator::ICacheMatch(void* one, void* two) { DCHECK_EQ(reinterpret_cast(one) & CachePage::kPageMask, 0); DCHECK_EQ(reinterpret_cast(two) & CachePage::kPageMask, 0); return one == two; } static uint32_t ICacheHash(void* key) { return static_cast(reinterpret_cast(key)) >> 2; } static bool AllOnOnePage(uintptr_t start, int size) { intptr_t start_page = (start & ~CachePage::kPageMask); intptr_t end_page = ((start + size) & ~CachePage::kPageMask); return start_page == end_page; } void Simulator::set_last_debugger_input(char* input) { DeleteArray(last_debugger_input_); last_debugger_input_ = input; } void Simulator::SetRedirectInstruction(Instruction* instruction) { instruction->SetInstructionBits(al | (0xF * B24) | kCallRtRedirected); } void Simulator::FlushICache(base::CustomMatcherHashMap* i_cache, void* start_addr, size_t size) { intptr_t start = reinterpret_cast(start_addr); int intra_line = (start & CachePage::kLineMask); start -= intra_line; size += intra_line; size = ((size - 1) | CachePage::kLineMask) + 1; int offset = (start & CachePage::kPageMask); while (!AllOnOnePage(start, size - 1)) { int bytes_to_flush = CachePage::kPageSize - offset; FlushOnePage(i_cache, start, bytes_to_flush); start += bytes_to_flush; size -= bytes_to_flush; DCHECK_EQ(0, start & CachePage::kPageMask); offset = 0; } if (size != 0) { FlushOnePage(i_cache, start, size); } } CachePage* Simulator::GetCachePage(base::CustomMatcherHashMap* i_cache, void* page) { base::HashMap::Entry* entry = i_cache->LookupOrInsert(page, ICacheHash(page)); if (entry->value == nullptr) { CachePage* new_page = new CachePage(); entry->value = new_page; } return reinterpret_cast(entry->value); } // Flush from start up to and not including start + size. void Simulator::FlushOnePage(base::CustomMatcherHashMap* i_cache, intptr_t start, int size) { DCHECK_LE(size, CachePage::kPageSize); DCHECK(AllOnOnePage(start, size - 1)); DCHECK_EQ(start & CachePage::kLineMask, 0); DCHECK_EQ(size & CachePage::kLineMask, 0); void* page = reinterpret_cast(start & (~CachePage::kPageMask)); int offset = (start & CachePage::kPageMask); CachePage* cache_page = GetCachePage(i_cache, page); char* valid_bytemap = cache_page->ValidityByte(offset); memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift); } void Simulator::CheckICache(base::CustomMatcherHashMap* i_cache, Instruction* instr) { intptr_t address = reinterpret_cast(instr); void* page = reinterpret_cast(address & (~CachePage::kPageMask)); void* line = reinterpret_cast(address & (~CachePage::kLineMask)); int offset = (address & CachePage::kPageMask); CachePage* cache_page = GetCachePage(i_cache, page); char* cache_valid_byte = cache_page->ValidityByte(offset); bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID); char* cached_line = cache_page->CachedData(offset & ~CachePage::kLineMask); if (cache_hit) { // Check that the data in memory matches the contents of the I-cache. CHECK_EQ(0, memcmp(reinterpret_cast(instr), cache_page->CachedData(offset), kInstrSize)); } else { // Cache miss. Load memory into the cache. memcpy(cached_line, line, CachePage::kLineLength); *cache_valid_byte = CachePage::LINE_VALID; } } Simulator::Simulator(Isolate* isolate) : isolate_(isolate) { // Set up simulator support first. Some of this information is needed to // setup the architecture state. size_t stack_size = 1 * 1024*1024; // allocate 1MB for stack stack_ = reinterpret_cast(malloc(stack_size)); pc_modified_ = false; icount_ = 0; break_pc_ = nullptr; break_instr_ = 0; // Set up architecture state. // All registers are initialized to zero to start with. for (int i = 0; i < num_registers; i++) { registers_[i] = 0; } n_flag_ = false; z_flag_ = false; c_flag_ = false; v_flag_ = false; // Initializing VFP registers. // All registers are initialized to zero to start with // even though s_registers_ & d_registers_ share the same // physical registers in the target. for (int i = 0; i < num_d_registers * 2; i++) { vfp_registers_[i] = 0; } n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = false; v_flag_FPSCR_ = false; FPSCR_rounding_mode_ = RN; FPSCR_default_NaN_mode_ = false; inv_op_vfp_flag_ = false; div_zero_vfp_flag_ = false; overflow_vfp_flag_ = false; underflow_vfp_flag_ = false; inexact_vfp_flag_ = false; // 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 // some buffer below. registers_[sp] = reinterpret_cast(stack_) + stack_size - 64; // The lr and pc are initialized to a known bad value that will cause an // access violation if the simulator ever tries to execute it. registers_[pc] = bad_lr; registers_[lr] = bad_lr; last_debugger_input_ = nullptr; } Simulator::~Simulator() { global_monitor_.Pointer()->RemoveProcessor(&global_monitor_processor_); free(stack_); } // Get the active Simulator for the current thread. Simulator* Simulator::current(Isolate* isolate) { v8::internal::Isolate::PerIsolateThreadData* isolate_data = isolate->FindOrAllocatePerThreadDataForThisThread(); DCHECK_NOT_NULL(isolate_data); Simulator* sim = isolate_data->simulator(); if (sim == nullptr) { // TODO(146): delete the simulator object when a thread/isolate goes away. sim = new Simulator(isolate); isolate_data->set_simulator(sim); } return sim; } // Sets the register in the architecture state. It will also deal with updating // Simulator internal state for special registers such as PC. void Simulator::set_register(int reg, int32_t value) { DCHECK((reg >= 0) && (reg < num_registers)); if (reg == pc) { pc_modified_ = true; } registers_[reg] = value; } // Get the register from the architecture state. This function does handle // the special case of accessing the PC register. int32_t Simulator::get_register(int reg) const { DCHECK((reg >= 0) && (reg < num_registers)); // Stupid code added to avoid bug in GCC. // See: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949 if (reg >= num_registers) return 0; // End stupid code. return registers_[reg] + ((reg == pc) ? Instruction::kPcLoadDelta : 0); } double Simulator::get_double_from_register_pair(int reg) { DCHECK((reg >= 0) && (reg < num_registers) && ((reg % 2) == 0)); double dm_val = 0.0; // Read the bits from the unsigned integer register_[] array // into the double precision floating point value and return it. char buffer[2 * sizeof(vfp_registers_[0])]; memcpy(buffer, ®isters_[reg], 2 * sizeof(registers_[0])); memcpy(&dm_val, buffer, 2 * sizeof(registers_[0])); return(dm_val); } void Simulator::set_register_pair_from_double(int reg, double* value) { DCHECK((reg >= 0) && (reg < num_registers) && ((reg % 2) == 0)); memcpy(registers_ + reg, value, sizeof(*value)); } void Simulator::set_dw_register(int dreg, const int* dbl) { DCHECK((dreg >= 0) && (dreg < num_d_registers)); registers_[dreg] = dbl[0]; registers_[dreg + 1] = dbl[1]; } void Simulator::get_d_register(int dreg, uint64_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value)); } void Simulator::set_d_register(int dreg, const uint64_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value)); } void Simulator::get_d_register(int dreg, uint32_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value) * 2); } void Simulator::set_d_register(int dreg, const uint32_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value) * 2); } template void Simulator::get_neon_register(int reg, T (&value)[SIZE / sizeof(T)]) { DCHECK(SIZE == kSimd128Size || SIZE == kDoubleSize); DCHECK_LE(0, reg); DCHECK_GT(SIZE == kSimd128Size ? num_q_registers : num_d_registers, reg); memcpy(value, vfp_registers_ + reg * (SIZE / 4), SIZE); } template void Simulator::set_neon_register(int reg, const T (&value)[SIZE / sizeof(T)]) { DCHECK(SIZE == kSimd128Size || SIZE == kDoubleSize); DCHECK_LE(0, reg); DCHECK_GT(SIZE == kSimd128Size ? num_q_registers : num_d_registers, reg); memcpy(vfp_registers_ + reg * (SIZE / 4), value, SIZE); } // Raw access to the PC register. void Simulator::set_pc(int32_t value) { pc_modified_ = true; registers_[pc] = value; } bool Simulator::has_bad_pc() const { return ((registers_[pc] == bad_lr) || (registers_[pc] == end_sim_pc)); } // Raw access to the PC register without the special adjustment when reading. int32_t Simulator::get_pc() const { return registers_[pc]; } // Getting from and setting into VFP registers. void Simulator::set_s_register(int sreg, unsigned int value) { DCHECK((sreg >= 0) && (sreg < num_s_registers)); vfp_registers_[sreg] = value; } unsigned int Simulator::get_s_register(int sreg) const { DCHECK((sreg >= 0) && (sreg < num_s_registers)); return vfp_registers_[sreg]; } template void Simulator::SetVFPRegister(int reg_index, const InputType& value) { unsigned bytes = register_size * sizeof(vfp_registers_[0]); DCHECK_EQ(sizeof(InputType), bytes); DCHECK_GE(reg_index, 0); if (register_size == 1) DCHECK(reg_index < num_s_registers); if (register_size == 2) DCHECK(reg_index < DwVfpRegister::NumRegisters()); memcpy(&vfp_registers_[reg_index * register_size], &value, bytes); } template ReturnType Simulator::GetFromVFPRegister(int reg_index) { unsigned bytes = register_size * sizeof(vfp_registers_[0]); DCHECK_EQ(sizeof(ReturnType), bytes); DCHECK_GE(reg_index, 0); if (register_size == 1) DCHECK(reg_index < num_s_registers); if (register_size == 2) DCHECK(reg_index < DwVfpRegister::NumRegisters()); ReturnType value; memcpy(&value, &vfp_registers_[register_size * reg_index], bytes); return value; } void Simulator::SetSpecialRegister(SRegisterFieldMask reg_and_mask, uint32_t value) { // Only CPSR_f is implemented. Of that, only N, Z, C and V are implemented. if ((reg_and_mask == CPSR_f) && ((value & ~kSpecialCondition) == 0)) { n_flag_ = ((value & (1 << 31)) != 0); z_flag_ = ((value & (1 << 30)) != 0); c_flag_ = ((value & (1 << 29)) != 0); v_flag_ = ((value & (1 << 28)) != 0); } else { UNIMPLEMENTED(); } } uint32_t Simulator::GetFromSpecialRegister(SRegister reg) { uint32_t result = 0; // Only CPSR_f is implemented. if (reg == CPSR) { if (n_flag_) result |= (1 << 31); if (z_flag_) result |= (1 << 30); if (c_flag_) result |= (1 << 29); if (v_flag_) result |= (1 << 28); } else { UNIMPLEMENTED(); } return result; } // Runtime FP routines take: // - two double arguments // - one double argument and zero or one integer arguments. // All are consructed here from r0-r3 or d0, d1 and r0. void Simulator::GetFpArgs(double* x, double* y, int32_t* z) { if (use_eabi_hardfloat()) { *x = get_double_from_d_register(0).get_scalar(); *y = get_double_from_d_register(1).get_scalar(); *z = get_register(0); } else { // Registers 0 and 1 -> x. *x = get_double_from_register_pair(0); // Register 2 and 3 -> y. *y = get_double_from_register_pair(2); // Register 2 -> z *z = get_register(2); } } // The return value is either in r0/r1 or d0. void Simulator::SetFpResult(const double& result) { if (use_eabi_hardfloat()) { char buffer[2 * sizeof(vfp_registers_[0])]; memcpy(buffer, &result, sizeof(buffer)); // Copy result to d0. memcpy(vfp_registers_, buffer, sizeof(buffer)); } else { char buffer[2 * sizeof(registers_[0])]; memcpy(buffer, &result, sizeof(buffer)); // Copy result to r0 and r1. memcpy(registers_, buffer, sizeof(buffer)); } } void Simulator::TrashCallerSaveRegisters() { // We don't trash the registers with the return value. registers_[2] = 0x50BAD4U; registers_[3] = 0x50BAD4U; registers_[12] = 0x50BAD4U; } int Simulator::ReadW(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); intptr_t* ptr = reinterpret_cast(addr); return *ptr; } int Simulator::ReadExW(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::Word); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); intptr_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::WriteW(int32_t addr, int value) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); intptr_t* ptr = reinterpret_cast(addr); *ptr = value; } int Simulator::WriteExW(int32_t addr, int value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::Word) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { intptr_t* ptr = reinterpret_cast(addr); *ptr = value; return 0; } else { return 1; } } uint16_t Simulator::ReadHU(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); uint16_t* ptr = reinterpret_cast(addr); return *ptr; } int16_t Simulator::ReadH(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); int16_t* ptr = reinterpret_cast(addr); return *ptr; } uint16_t Simulator::ReadExHU(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::HalfWord); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); uint16_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::WriteH(int32_t addr, uint16_t value) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); uint16_t* ptr = reinterpret_cast(addr); *ptr = value; } void Simulator::WriteH(int32_t addr, int16_t value) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); int16_t* ptr = reinterpret_cast(addr); *ptr = value; } int Simulator::WriteExH(int32_t addr, uint16_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::HalfWord) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { uint16_t* ptr = reinterpret_cast(addr); *ptr = value; return 0; } else { return 1; } } uint8_t Simulator::ReadBU(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); uint8_t* ptr = reinterpret_cast(addr); return *ptr; } int8_t Simulator::ReadB(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); int8_t* ptr = reinterpret_cast(addr); return *ptr; } uint8_t Simulator::ReadExBU(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::Byte); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); uint8_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::WriteB(int32_t addr, uint8_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); uint8_t* ptr = reinterpret_cast(addr); *ptr = value; } void Simulator::WriteB(int32_t addr, int8_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); int8_t* ptr = reinterpret_cast(addr); *ptr = value; } int Simulator::WriteExB(int32_t addr, uint8_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::Byte) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { uint8_t* ptr = reinterpret_cast(addr); *ptr = value; return 0; } else { return 1; } } int32_t* Simulator::ReadDW(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); int32_t* ptr = reinterpret_cast(addr); return ptr; } int32_t* Simulator::ReadExDW(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::DoubleWord); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); int32_t* ptr = reinterpret_cast(addr); return ptr; } void Simulator::WriteDW(int32_t addr, int32_t value1, int32_t value2) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); int32_t* ptr = reinterpret_cast(addr); *ptr++ = value1; *ptr = value2; } int Simulator::WriteExDW(int32_t addr, int32_t value1, int32_t value2) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::DoubleWord) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { intptr_t* ptr = reinterpret_cast(addr); *ptr++ = value1; *ptr = value2; return 0; } else { return 1; } } // Returns the limit of the stack area to enable checking for stack overflows. uintptr_t Simulator::StackLimit(uintptr_t c_limit) const { // The simulator uses a separate JS stack. If we have exhausted the C stack, // we also drop down the JS limit to reflect the exhaustion on the JS stack. if (GetCurrentStackPosition() < c_limit) { return reinterpret_cast(get_sp()); } // Otherwise the limit is the JS stack. Leave a safety margin of 1024 bytes // to prevent overrunning the stack when pushing values. return reinterpret_cast(stack_) + 1024; } // Unsupported instructions use Format to print an error and stop execution. void Simulator::Format(Instruction* instr, const char* format) { PrintF("Simulator found unsupported instruction:\n 0x%08" V8PRIxPTR ": %s\n", reinterpret_cast(instr), format); UNIMPLEMENTED(); } // Checks if the current instruction should be executed based on its // condition bits. bool Simulator::ConditionallyExecute(Instruction* instr) { switch (instr->ConditionField()) { case eq: return z_flag_; case ne: return !z_flag_; case cs: return c_flag_; case cc: return !c_flag_; case mi: return n_flag_; case pl: return !n_flag_; case vs: return v_flag_; case vc: return !v_flag_; case hi: return c_flag_ && !z_flag_; case ls: return !c_flag_ || z_flag_; case ge: return n_flag_ == v_flag_; case lt: return n_flag_ != v_flag_; case gt: return !z_flag_ && (n_flag_ == v_flag_); case le: return z_flag_ || (n_flag_ != v_flag_); case al: return true; default: UNREACHABLE(); } return false; } // Calculate and set the Negative and Zero flags. void Simulator::SetNZFlags(int32_t val) { n_flag_ = (val < 0); z_flag_ = (val == 0); } // Set the Carry flag. void Simulator::SetCFlag(bool val) { c_flag_ = val; } // Set the oVerflow flag. void Simulator::SetVFlag(bool val) { v_flag_ = val; } // Calculate C flag value for additions. bool Simulator::CarryFrom(int32_t left, int32_t right, int32_t carry) { uint32_t uleft = static_cast(left); uint32_t uright = static_cast(right); uint32_t urest = 0xFFFFFFFFU - uleft; return (uright > urest) || (carry && (((uright + 1) > urest) || (uright > (urest - 1)))); } // Calculate C flag value for subtractions. bool Simulator::BorrowFrom(int32_t left, int32_t right, int32_t carry) { uint32_t uleft = static_cast(left); uint32_t uright = static_cast(right); return (uright > uleft) || (!carry && (((uright + 1) > uleft) || (uright > (uleft - 1)))); } // Calculate V flag value for additions and subtractions. bool Simulator::OverflowFrom(int32_t alu_out, int32_t left, int32_t right, bool addition) { bool overflow; if (addition) { // operands have the same sign overflow = ((left >= 0 && right >= 0) || (left < 0 && right < 0)) // and operands and result have different sign && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); } else { // operands have different signs overflow = ((left < 0 && right >= 0) || (left >= 0 && right < 0)) // and first operand and result have different signs && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); } return overflow; } // Support for VFP comparisons. void Simulator::Compute_FPSCR_Flags(float val1, float val2) { if (std::isnan(val1) || std::isnan(val2)) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = true; // All non-NaN cases. } else if (val1 == val2) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = true; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } else if (val1 < val2) { n_flag_FPSCR_ = true; z_flag_FPSCR_ = false; c_flag_FPSCR_ = false; v_flag_FPSCR_ = false; } else { // Case when (val1 > val2). n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } } void Simulator::Compute_FPSCR_Flags(double val1, double val2) { if (std::isnan(val1) || std::isnan(val2)) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = true; // All non-NaN cases. } else if (val1 == val2) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = true; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } else if (val1 < val2) { n_flag_FPSCR_ = true; z_flag_FPSCR_ = false; c_flag_FPSCR_ = false; v_flag_FPSCR_ = false; } else { // Case when (val1 > val2). n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } } void Simulator::Copy_FPSCR_to_APSR() { n_flag_ = n_flag_FPSCR_; z_flag_ = z_flag_FPSCR_; c_flag_ = c_flag_FPSCR_; v_flag_ = v_flag_FPSCR_; } // Addressing Mode 1 - Data-processing operands: // Get the value based on the shifter_operand with register. int32_t Simulator::GetShiftRm(Instruction* instr, bool* carry_out) { ShiftOp shift = instr->ShiftField(); int shift_amount = instr->ShiftAmountValue(); int32_t result = get_register(instr->RmValue()); if (instr->Bit(4) == 0) { // by immediate if ((shift == ROR) && (shift_amount == 0)) { UNIMPLEMENTED(); return result; } else if (((shift == LSR) || (shift == ASR)) && (shift_amount == 0)) { shift_amount = 32; } switch (shift) { case ASR: { if (shift_amount == 0) { if (result < 0) { result = 0xFFFFFFFF; *carry_out = true; } else { result = 0; *carry_out = false; } } else { result >>= (shift_amount - 1); *carry_out = (result & 1) == 1; result >>= 1; } break; } case LSL: { if (shift_amount == 0) { *carry_out = c_flag_; } else { result <<= (shift_amount - 1); *carry_out = (result < 0); result <<= 1; } break; } case LSR: { if (shift_amount == 0) { result = 0; *carry_out = c_flag_; } else { uint32_t uresult = static_cast(result); uresult >>= (shift_amount - 1); *carry_out = (uresult & 1) == 1; uresult >>= 1; result = static_cast(uresult); } break; } case ROR: { if (shift_amount == 0) { *carry_out = c_flag_; } else { uint32_t left = static_cast(result) >> shift_amount; uint32_t right = static_cast(result) << (32 - shift_amount); result = right | left; *carry_out = (static_cast(result) >> 31) != 0; } break; } default: { UNREACHABLE(); break; } } } else { // by register int rs = instr->RsValue(); shift_amount = get_register(rs) & 0xFF; switch (shift) { case ASR: { if (shift_amount == 0) { *carry_out = c_flag_; } else if (shift_amount < 32) { result >>= (shift_amount - 1); *carry_out = (result & 1) == 1; result >>= 1; } else { DCHECK_GE(shift_amount, 32); if (result < 0) { *carry_out = true; result = 0xFFFFFFFF; } else { *carry_out = false; result = 0; } } break; } case LSL: { if (shift_amount == 0) { *carry_out = c_flag_; } else if (shift_amount < 32) { result <<= (shift_amount - 1); *carry_out = (result < 0); result <<= 1; } else if (shift_amount == 32) { *carry_out = (result & 1) == 1; result = 0; } else { DCHECK_GT(shift_amount, 32); *carry_out = false; result = 0; } break; } case LSR: { if (shift_amount == 0) { *carry_out = c_flag_; } else if (shift_amount < 32) { uint32_t uresult = static_cast(result); uresult >>= (shift_amount - 1); *carry_out = (uresult & 1) == 1; uresult >>= 1; result = static_cast(uresult); } else if (shift_amount == 32) { *carry_out = (result < 0); result = 0; } else { *carry_out = false; result = 0; } break; } case ROR: { if (shift_amount == 0) { *carry_out = c_flag_; } else { uint32_t left = static_cast(result) >> shift_amount; uint32_t right = static_cast(result) << (32 - shift_amount); result = right | left; *carry_out = (static_cast(result) >> 31) != 0; } break; } default: { UNREACHABLE(); break; } } } return result; } // Addressing Mode 1 - Data-processing operands: // Get the value based on the shifter_operand with immediate. int32_t Simulator::GetImm(Instruction* instr, bool* carry_out) { int rotate = instr->RotateValue() * 2; int immed8 = instr->Immed8Value(); int imm = base::bits::RotateRight32(immed8, rotate); *carry_out = (rotate == 0) ? c_flag_ : (imm < 0); return imm; } static int count_bits(int bit_vector) { int count = 0; while (bit_vector != 0) { if ((bit_vector & 1) != 0) { count++; } bit_vector >>= 1; } return count; } int32_t Simulator::ProcessPU(Instruction* instr, int num_regs, int reg_size, intptr_t* start_address, intptr_t* end_address) { int rn = instr->RnValue(); int32_t rn_val = get_register(rn); switch (instr->PUField()) { case da_x: { UNIMPLEMENTED(); break; } case ia_x: { *start_address = rn_val; *end_address = rn_val + (num_regs * reg_size) - reg_size; rn_val = rn_val + (num_regs * reg_size); break; } case db_x: { *start_address = rn_val - (num_regs * reg_size); *end_address = rn_val - reg_size; rn_val = *start_address; break; } case ib_x: { *start_address = rn_val + reg_size; *end_address = rn_val + (num_regs * reg_size); rn_val = *end_address; break; } default: { UNREACHABLE(); break; } } return rn_val; } // Addressing Mode 4 - Load and Store Multiple void Simulator::HandleRList(Instruction* instr, bool load) { int rlist = instr->RlistValue(); int num_regs = count_bits(rlist); intptr_t start_address = 0; intptr_t end_address = 0; int32_t rn_val = ProcessPU(instr, num_regs, kPointerSize, &start_address, &end_address); intptr_t* address = reinterpret_cast(start_address); // Catch null pointers a little earlier. DCHECK(start_address > 8191 || start_address < 0); int reg = 0; while (rlist != 0) { if ((rlist & 1) != 0) { if (load) { set_register(reg, *address); } else { *address = get_register(reg); } address += 1; } reg++; rlist >>= 1; } DCHECK(end_address == ((intptr_t)address) - 4); if (instr->HasW()) { set_register(instr->RnValue(), rn_val); } } // Addressing Mode 6 - Load and Store Multiple Coprocessor registers. void Simulator::HandleVList(Instruction* instr) { VFPRegPrecision precision = (instr->SzValue() == 0) ? kSinglePrecision : kDoublePrecision; int operand_size = (precision == kSinglePrecision) ? 4 : 8; bool load = (instr->VLValue() == 0x1); int vd; int num_regs; vd = instr->VFPDRegValue(precision); if (precision == kSinglePrecision) { num_regs = instr->Immed8Value(); } else { num_regs = instr->Immed8Value() / 2; } intptr_t start_address = 0; intptr_t end_address = 0; int32_t rn_val = ProcessPU(instr, num_regs, operand_size, &start_address, &end_address); intptr_t* address = reinterpret_cast(start_address); for (int reg = vd; reg < vd + num_regs; reg++) { if (precision == kSinglePrecision) { if (load) { set_s_register_from_sinteger(reg, ReadW(reinterpret_cast(address))); } else { WriteW(reinterpret_cast(address), get_sinteger_from_s_register(reg)); } address += 1; } else { if (load) { int32_t data[] = {ReadW(reinterpret_cast(address)), ReadW(reinterpret_cast(address + 1))}; set_d_register(reg, reinterpret_cast(data)); } else { uint32_t data[2]; get_d_register(reg, data); WriteW(reinterpret_cast(address), data[0]); WriteW(reinterpret_cast(address + 1), data[1]); } address += 2; } } DCHECK(reinterpret_cast(address) - operand_size == end_address); if (instr->HasW()) { set_register(instr->RnValue(), rn_val); } } // Calls into the V8 runtime are based on this very simple interface. // 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 // 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, int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7, int32_t arg8); // These prototypes handle the four types of FP calls. typedef int64_t (*SimulatorRuntimeCompareCall)(double darg0, double darg1); typedef double (*SimulatorRuntimeFPFPCall)(double darg0, double darg1); typedef double (*SimulatorRuntimeFPCall)(double darg0); typedef double (*SimulatorRuntimeFPIntCall)(double darg0, int32_t arg0); // This signature supports direct call in to API function native callback // (refer to InvocationCallback in v8.h). typedef void (*SimulatorRuntimeDirectApiCall)(int32_t arg0); typedef void (*SimulatorRuntimeProfilingApiCall)(int32_t arg0, void* arg1); // This signature supports direct call to accessor getter callback. typedef void (*SimulatorRuntimeDirectGetterCall)(int32_t arg0, int32_t arg1); typedef void (*SimulatorRuntimeProfilingGetterCall)( int32_t arg0, int32_t arg1, void* arg2); // Software interrupt instructions are used by the simulator to call into the // C-based V8 runtime. void Simulator::SoftwareInterrupt(Instruction* instr) { int svc = instr->SvcValue(); switch (svc) { case kCallRtRedirected: { // Check if stack is aligned. Error if not aligned is reported below to // include information on the function called. bool stack_aligned = (get_register(sp) & (::v8::internal::FLAG_sim_stack_alignment - 1)) == 0; Redirection* redirection = Redirection::FromInstruction(instr); int32_t arg0 = get_register(r0); int32_t arg1 = get_register(r1); int32_t arg2 = get_register(r2); int32_t arg3 = get_register(r3); int32_t* stack_pointer = reinterpret_cast(get_register(sp)); int32_t arg4 = stack_pointer[0]; int32_t arg5 = stack_pointer[1]; int32_t arg6 = stack_pointer[2]; int32_t arg7 = stack_pointer[3]; int32_t arg8 = stack_pointer[4]; STATIC_ASSERT(kMaxCParameters == 9); bool fp_call = (redirection->type() == ExternalReference::BUILTIN_FP_FP_CALL) || (redirection->type() == ExternalReference::BUILTIN_COMPARE_CALL) || (redirection->type() == ExternalReference::BUILTIN_FP_CALL) || (redirection->type() == ExternalReference::BUILTIN_FP_INT_CALL); // 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_lr = get_register(lr); intptr_t external = reinterpret_cast(redirection->external_function()); if (fp_call) { double dval0, dval1; // one or two double parameters int32_t ival; // zero or one integer parameters int64_t iresult = 0; // integer return value double dresult = 0; // double return value GetFpArgs(&dval0, &dval1, &ival); if (::v8::internal::FLAG_trace_sim || !stack_aligned) { SimulatorRuntimeCall generic_target = reinterpret_cast(external); switch (redirection->type()) { case ExternalReference::BUILTIN_FP_FP_CALL: case ExternalReference::BUILTIN_COMPARE_CALL: PrintF("Call to host function at %p with args %f, %f", reinterpret_cast(FUNCTION_ADDR(generic_target)), dval0, dval1); break; case ExternalReference::BUILTIN_FP_CALL: PrintF("Call to host function at %p with arg %f", reinterpret_cast(FUNCTION_ADDR(generic_target)), dval0); break; case ExternalReference::BUILTIN_FP_INT_CALL: PrintF("Call to host function at %p with args %f, %d", reinterpret_cast(FUNCTION_ADDR(generic_target)), dval0, ival); break; default: UNREACHABLE(); break; } if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); switch (redirection->type()) { case ExternalReference::BUILTIN_COMPARE_CALL: { SimulatorRuntimeCompareCall target = reinterpret_cast(external); iresult = target(dval0, dval1); set_register(r0, static_cast(iresult)); set_register(r1, static_cast(iresult >> 32)); break; } case ExternalReference::BUILTIN_FP_FP_CALL: { SimulatorRuntimeFPFPCall target = reinterpret_cast(external); dresult = target(dval0, dval1); SetFpResult(dresult); break; } case ExternalReference::BUILTIN_FP_CALL: { SimulatorRuntimeFPCall target = reinterpret_cast(external); dresult = target(dval0); SetFpResult(dresult); break; } case ExternalReference::BUILTIN_FP_INT_CALL: { SimulatorRuntimeFPIntCall target = reinterpret_cast(external); dresult = target(dval0, ival); SetFpResult(dresult); break; } default: UNREACHABLE(); break; } if (::v8::internal::FLAG_trace_sim || !stack_aligned) { switch (redirection->type()) { case ExternalReference::BUILTIN_COMPARE_CALL: PrintF("Returned %08x\n", static_cast(iresult)); break; case ExternalReference::BUILTIN_FP_FP_CALL: case ExternalReference::BUILTIN_FP_CALL: case ExternalReference::BUILTIN_FP_INT_CALL: PrintF("Returned %f\n", dresult); break; default: UNREACHABLE(); break; } } } else if (redirection->type() == ExternalReference::DIRECT_API_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x", reinterpret_cast(external), arg0); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeDirectApiCall target = reinterpret_cast(external); target(arg0); } else if ( redirection->type() == ExternalReference::PROFILING_API_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x %08x", reinterpret_cast(external), arg0, arg1); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeProfilingApiCall target = reinterpret_cast(external); target(arg0, Redirection::ReverseRedirection(arg1)); } else if ( redirection->type() == ExternalReference::DIRECT_GETTER_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x %08x", reinterpret_cast(external), arg0, arg1); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeDirectGetterCall target = reinterpret_cast(external); target(arg0, arg1); } else if ( redirection->type() == ExternalReference::PROFILING_GETTER_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x %08x %08x", reinterpret_cast(external), arg0, arg1, arg2); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeProfilingGetterCall target = reinterpret_cast( external); target(arg0, arg1, Redirection::ReverseRedirection(arg2)); } else { // builtin call. DCHECK(redirection->type() == ExternalReference::BUILTIN_CALL || redirection->type() == ExternalReference::BUILTIN_CALL_PAIR); SimulatorRuntimeCall target = reinterpret_cast(external); if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF( "Call to host function at %p " "args %08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x", reinterpret_cast(FUNCTION_ADDR(target)), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); int32_t lo_res = static_cast(result); int32_t hi_res = static_cast(result >> 32); if (::v8::internal::FLAG_trace_sim) { PrintF("Returned %08x\n", lo_res); } set_register(r0, lo_res); set_register(r1, hi_res); } set_register(lr, saved_lr); set_pc(get_register(lr)); break; } case kBreakpoint: { ArmDebugger dbg(this); dbg.Debug(); break; } // stop uses all codes greater than 1 << 23. default: { if (svc >= (1 << 23)) { uint32_t code = svc & kStopCodeMask; if (isWatchedStop(code)) { IncreaseStopCounter(code); } // Stop if it is enabled, otherwise go on jumping over the stop // and the message address. if (isEnabledStop(code)) { ArmDebugger dbg(this); dbg.Stop(instr); } } else { // This is not a valid svc code. UNREACHABLE(); break; } } } } float Simulator::canonicalizeNaN(float value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr uint32_t kDefaultNaN = 0x7FC00000u; if (FPSCR_default_NaN_mode_ && std::isnan(value)) { value = bit_cast(kDefaultNaN); } return value; } Float32 Simulator::canonicalizeNaN(Float32 value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr Float32 kDefaultNaN = Float32::FromBits(0x7FC00000u); return FPSCR_default_NaN_mode_ && value.is_nan() ? kDefaultNaN : value; } double Simulator::canonicalizeNaN(double value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr uint64_t kDefaultNaN = uint64_t{0x7FF8000000000000}; if (FPSCR_default_NaN_mode_ && std::isnan(value)) { value = bit_cast(kDefaultNaN); } return value; } Float64 Simulator::canonicalizeNaN(Float64 value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr Float64 kDefaultNaN = Float64::FromBits(uint64_t{0x7FF8000000000000}); return FPSCR_default_NaN_mode_ && value.is_nan() ? kDefaultNaN : value; } // Stop helper functions. bool Simulator::isStopInstruction(Instruction* instr) { return (instr->Bits(27, 24) == 0xF) && (instr->SvcValue() >= kStopCode); } bool Simulator::isWatchedStop(uint32_t code) { DCHECK_LE(code, kMaxStopCode); return code < kNumOfWatchedStops; } bool Simulator::isEnabledStop(uint32_t code) { DCHECK_LE(code, kMaxStopCode); // Unwatched stops are always enabled. return !isWatchedStop(code) || !(watched_stops_[code].count & kStopDisabledBit); } void Simulator::EnableStop(uint32_t code) { DCHECK(isWatchedStop(code)); if (!isEnabledStop(code)) { watched_stops_[code].count &= ~kStopDisabledBit; } } void Simulator::DisableStop(uint32_t code) { DCHECK(isWatchedStop(code)); if (isEnabledStop(code)) { watched_stops_[code].count |= kStopDisabledBit; } } void Simulator::IncreaseStopCounter(uint32_t code) { DCHECK_LE(code, kMaxStopCode); DCHECK(isWatchedStop(code)); if ((watched_stops_[code].count & ~(1 << 31)) == 0x7FFFFFFF) { PrintF("Stop counter for code %i has overflowed.\n" "Enabling this code and reseting the counter to 0.\n", code); watched_stops_[code].count = 0; EnableStop(code); } else { watched_stops_[code].count++; } } // Print a stop status. void Simulator::PrintStopInfo(uint32_t code) { DCHECK_LE(code, kMaxStopCode); if (!isWatchedStop(code)) { PrintF("Stop not watched."); } else { const char* state = isEnabledStop(code) ? "Enabled" : "Disabled"; int32_t count = watched_stops_[code].count & ~kStopDisabledBit; // Don't print the state of unused breakpoints. if (count != 0) { if (watched_stops_[code].desc) { PrintF("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n", code, code, state, count, watched_stops_[code].desc); } else { PrintF("stop %i - 0x%x: \t%s, \tcounter = %i\n", code, code, state, count); } } } } // Handle execution based on instruction types. // Instruction types 0 and 1 are both rolled into one function because they // only differ in the handling of the shifter_operand. void Simulator::DecodeType01(Instruction* instr) { int type = instr->TypeValue(); if ((type == 0) && instr->IsSpecialType0()) { // multiply instruction or extra loads and stores if (instr->Bits(7, 4) == 9) { if (instr->Bit(24) == 0) { // Raw field decoding here. Multiply instructions have their Rd in // funny places. int rn = instr->RnValue(); int rm = instr->RmValue(); int rs = instr->RsValue(); int32_t rs_val = get_register(rs); int32_t rm_val = get_register(rm); if (instr->Bit(23) == 0) { if (instr->Bit(21) == 0) { // The MUL instruction description (A 4.1.33) refers to Rd as being // the destination for the operation, but it confusingly uses the // Rn field to encode it. // Format(instr, "mul'cond's 'rn, 'rm, 'rs"); int rd = rn; // Remap the rn field to the Rd register. int32_t alu_out = rm_val * rs_val; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); } } else { int rd = instr->RdValue(); int32_t acc_value = get_register(rd); if (instr->Bit(22) == 0) { // The MLA instruction description (A 4.1.28) refers to the order // of registers as "Rd, Rm, Rs, Rn". But confusingly it uses the // Rn field to encode the Rd register and the Rd field to encode // the Rn register. // Format(instr, "mla'cond's 'rn, 'rm, 'rs, 'rd"); int32_t mul_out = rm_val * rs_val; int32_t result = acc_value + mul_out; set_register(rn, result); } else { // Format(instr, "mls'cond's 'rn, 'rm, 'rs, 'rd"); int32_t mul_out = rm_val * rs_val; int32_t result = acc_value - mul_out; set_register(rn, result); } } } else { // The signed/long multiply instructions use the terms RdHi and RdLo // when referring to the target registers. They are mapped to the Rn // and Rd fields as follows: // RdLo == Rd // RdHi == Rn (This is confusingly stored in variable rd here // because the mul instruction from above uses the // Rn field to encode the Rd register. Good luck figuring // this out without reading the ARM instruction manual // at a very detailed level.) // Format(instr, "'um'al'cond's 'rd, 'rn, 'rs, 'rm"); int rd_hi = rn; // Remap the rn field to the RdHi register. int rd_lo = instr->RdValue(); int32_t hi_res = 0; int32_t lo_res = 0; if (instr->Bit(22) == 1) { int64_t left_op = static_cast(rm_val); int64_t right_op = static_cast(rs_val); uint64_t result = left_op * right_op; hi_res = static_cast(result >> 32); lo_res = static_cast(result & 0xFFFFFFFF); } else { // unsigned multiply uint64_t left_op = static_cast(rm_val); uint64_t right_op = static_cast(rs_val); uint64_t result = left_op * right_op; hi_res = static_cast(result >> 32); lo_res = static_cast(result & 0xFFFFFFFF); } set_register(rd_lo, lo_res); set_register(rd_hi, hi_res); if (instr->HasS()) { UNIMPLEMENTED(); } } } else { if (instr->Bits(24, 23) == 3) { if (instr->Bit(20) == 1) { // ldrex int rt = instr->RtValue(); int rn = instr->RnValue(); int32_t addr = get_register(rn); switch (instr->Bits(22, 21)) { case 0: { // Format(instr, "ldrex'cond 'rt, ['rn]"); int value = ReadExW(addr); set_register(rt, value); break; } case 1: { // Format(instr, "ldrexd'cond 'rt, ['rn]"); int* rn_data = ReadExDW(addr); set_dw_register(rt, rn_data); break; } case 2: { // Format(instr, "ldrexb'cond 'rt, ['rn]"); uint8_t value = ReadExBU(addr); set_register(rt, value); break; } case 3: { // Format(instr, "ldrexh'cond 'rt, ['rn]"); uint16_t value = ReadExHU(addr); set_register(rt, value); break; } default: UNREACHABLE(); break; } } else { // The instruction is documented as strex rd, rt, [rn], but the // "rt" register is using the rm bits. int rd = instr->RdValue(); int rt = instr->RmValue(); int rn = instr->RnValue(); DCHECK_NE(rd, rn); DCHECK_NE(rd, rt); int32_t addr = get_register(rn); switch (instr->Bits(22, 21)) { case 0: { // Format(instr, "strex'cond 'rd, 'rm, ['rn]"); int value = get_register(rt); int status = WriteExW(addr, value); set_register(rd, status); break; } case 1: { // Format(instr, "strexd'cond 'rd, 'rm, ['rn]"); DCHECK_EQ(rt % 2, 0); int32_t value1 = get_register(rt); int32_t value2 = get_register(rt + 1); int status = WriteExDW(addr, value1, value2); set_register(rd, status); break; } case 2: { // Format(instr, "strexb'cond 'rd, 'rm, ['rn]"); uint8_t value = get_register(rt); int status = WriteExB(addr, value); set_register(rd, status); break; } case 3: { // Format(instr, "strexh'cond 'rd, 'rm, ['rn]"); uint16_t value = get_register(rt); int status = WriteExH(addr, value); set_register(rd, status); break; } default: UNREACHABLE(); break; } } } else { UNIMPLEMENTED(); // Not used by V8. } } } else { // extra load/store instructions int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); int32_t addr = 0; if (instr->Bit(22) == 0) { int rm = instr->RmValue(); int32_t rm_val = get_register(rm); switch (instr->PUField()) { case da_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], -'rm"); DCHECK(!instr->HasW()); addr = rn_val; rn_val -= rm_val; set_register(rn, rn_val); break; } case ia_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], +'rm"); DCHECK(!instr->HasW()); addr = rn_val; rn_val += rm_val; set_register(rn, rn_val); break; } case db_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, -'rm]'w"); rn_val -= rm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } case ib_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, +'rm]'w"); rn_val += rm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } default: { // The PU field is a 2-bit field. UNREACHABLE(); break; } } } else { int32_t imm_val = (instr->ImmedHValue() << 4) | instr->ImmedLValue(); switch (instr->PUField()) { case da_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], #-'off8"); DCHECK(!instr->HasW()); addr = rn_val; rn_val -= imm_val; set_register(rn, rn_val); break; } case ia_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], #+'off8"); DCHECK(!instr->HasW()); addr = rn_val; rn_val += imm_val; set_register(rn, rn_val); break; } case db_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, #-'off8]'w"); rn_val -= imm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } case ib_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, #+'off8]'w"); rn_val += imm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } default: { // The PU field is a 2-bit field. UNREACHABLE(); break; } } } if (((instr->Bits(7, 4) & 0xD) == 0xD) && (instr->Bit(20) == 0)) { DCHECK_EQ(rd % 2, 0); if (instr->HasH()) { // The strd instruction. int32_t value1 = get_register(rd); int32_t value2 = get_register(rd+1); WriteDW(addr, value1, value2); } else { // The ldrd instruction. int* rn_data = ReadDW(addr); set_dw_register(rd, rn_data); } } else if (instr->HasH()) { if (instr->HasSign()) { if (instr->HasL()) { int16_t val = ReadH(addr); set_register(rd, val); } else { int16_t val = get_register(rd); WriteH(addr, val); } } else { if (instr->HasL()) { uint16_t val = ReadHU(addr); set_register(rd, val); } else { uint16_t val = get_register(rd); WriteH(addr, val); } } } else { // signed byte loads DCHECK(instr->HasSign()); DCHECK(instr->HasL()); int8_t val = ReadB(addr); set_register(rd, val); } return; } } else if ((type == 0) && instr->IsMiscType0()) { if ((instr->Bits(27, 23) == 2) && (instr->Bits(21, 20) == 2) && (instr->Bits(15, 4) == 0xF00)) { // MSR int rm = instr->RmValue(); DCHECK_NE(pc, rm); // UNPREDICTABLE SRegisterFieldMask sreg_and_mask = instr->BitField(22, 22) | instr->BitField(19, 16); SetSpecialRegister(sreg_and_mask, get_register(rm)); } else if ((instr->Bits(27, 23) == 2) && (instr->Bits(21, 20) == 0) && (instr->Bits(11, 0) == 0)) { // MRS int rd = instr->RdValue(); DCHECK_NE(pc, rd); // UNPREDICTABLE SRegister sreg = static_cast(instr->BitField(22, 22)); set_register(rd, GetFromSpecialRegister(sreg)); } else if (instr->Bits(22, 21) == 1) { int rm = instr->RmValue(); switch (instr->BitField(7, 4)) { case BX: set_pc(get_register(rm)); break; case BLX: { uint32_t old_pc = get_pc(); set_pc(get_register(rm)); set_register(lr, old_pc + kInstrSize); break; } case BKPT: { ArmDebugger dbg(this); PrintF("Simulator hit BKPT.\n"); dbg.Debug(); break; } default: UNIMPLEMENTED(); } } else if (instr->Bits(22, 21) == 3) { int rm = instr->RmValue(); int rd = instr->RdValue(); switch (instr->BitField(7, 4)) { case CLZ: { uint32_t bits = get_register(rm); int leading_zeros = 0; if (bits == 0) { leading_zeros = 32; } else { while ((bits & 0x80000000u) == 0) { bits <<= 1; leading_zeros++; } } set_register(rd, leading_zeros); break; } default: UNIMPLEMENTED(); } } else { PrintF("%08x\n", instr->InstructionBits()); UNIMPLEMENTED(); } } else if ((type == 1) && instr->IsNopLikeType1()) { if (instr->BitField(7, 0) == 0) { // NOP. } else if (instr->BitField(7, 0) == 20) { // CSDB. } else { PrintF("%08x\n", instr->InstructionBits()); UNIMPLEMENTED(); } } else { int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); int32_t shifter_operand = 0; bool shifter_carry_out = 0; if (type == 0) { shifter_operand = GetShiftRm(instr, &shifter_carry_out); } else { DCHECK_EQ(instr->TypeValue(), 1); shifter_operand = GetImm(instr, &shifter_carry_out); } int32_t alu_out; switch (instr->OpcodeField()) { case AND: { // Format(instr, "and'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "and'cond's 'rd, 'rn, 'imm"); alu_out = rn_val & shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case EOR: { // Format(instr, "eor'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "eor'cond's 'rd, 'rn, 'imm"); alu_out = rn_val ^ shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case SUB: { // Format(instr, "sub'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "sub'cond's 'rd, 'rn, 'imm"); alu_out = rn_val - shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(!BorrowFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); } break; } case RSB: { // Format(instr, "rsb'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "rsb'cond's 'rd, 'rn, 'imm"); alu_out = shifter_operand - rn_val; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(!BorrowFrom(shifter_operand, rn_val)); SetVFlag(OverflowFrom(alu_out, shifter_operand, rn_val, false)); } break; } case ADD: { // Format(instr, "add'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "add'cond's 'rd, 'rn, 'imm"); alu_out = rn_val + shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(CarryFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true)); } break; } case ADC: { // Format(instr, "adc'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "adc'cond's 'rd, 'rn, 'imm"); alu_out = rn_val + shifter_operand + GetCarry(); set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(CarryFrom(rn_val, shifter_operand, GetCarry())); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true)); } break; } case SBC: { // Format(instr, "sbc'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "sbc'cond's 'rd, 'rn, 'imm"); alu_out = (rn_val - shifter_operand) - (GetCarry() ? 0 : 1); set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(!BorrowFrom(rn_val, shifter_operand, GetCarry())); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); } break; } case RSC: { Format(instr, "rsc'cond's 'rd, 'rn, 'shift_rm"); Format(instr, "rsc'cond's 'rd, 'rn, 'imm"); break; } case TST: { if (instr->HasS()) { // Format(instr, "tst'cond 'rn, 'shift_rm"); // Format(instr, "tst'cond 'rn, 'imm"); alu_out = rn_val & shifter_operand; SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } else { // Format(instr, "movw'cond 'rd, 'imm"). alu_out = instr->ImmedMovwMovtValue(); set_register(rd, alu_out); } break; } case TEQ: { if (instr->HasS()) { // Format(instr, "teq'cond 'rn, 'shift_rm"); // Format(instr, "teq'cond 'rn, 'imm"); alu_out = rn_val ^ shifter_operand; SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } else { // Other instructions matching this pattern are handled in the // miscellaneous instructions part above. UNREACHABLE(); } break; } case CMP: { if (instr->HasS()) { // Format(instr, "cmp'cond 'rn, 'shift_rm"); // Format(instr, "cmp'cond 'rn, 'imm"); alu_out = rn_val - shifter_operand; SetNZFlags(alu_out); SetCFlag(!BorrowFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); } else { // Format(instr, "movt'cond 'rd, 'imm"). alu_out = (get_register(rd) & 0xFFFF) | (instr->ImmedMovwMovtValue() << 16); set_register(rd, alu_out); } break; } case CMN: { if (instr->HasS()) { // Format(instr, "cmn'cond 'rn, 'shift_rm"); // Format(instr, "cmn'cond 'rn, 'imm"); alu_out = rn_val + shifter_operand; SetNZFlags(alu_out); SetCFlag(CarryFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true)); } else { // Other instructions matching this pattern are handled in the // miscellaneous instructions part above. UNREACHABLE(); } break; } case ORR: { // Format(instr, "orr'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "orr'cond's 'rd, 'rn, 'imm"); alu_out = rn_val | shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case MOV: { // Format(instr, "mov'cond's 'rd, 'shift_rm"); // Format(instr, "mov'cond's 'rd, 'imm"); alu_out = shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case BIC: { // Format(instr, "bic'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "bic'cond's 'rd, 'rn, 'imm"); alu_out = rn_val & ~shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case MVN: { // Format(instr, "mvn'cond's 'rd, 'shift_rm"); // Format(instr, "mvn'cond's 'rd, 'imm"); alu_out = ~shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } default: { UNREACHABLE(); break; } } } } void Simulator::DecodeType2(Instruction* instr) { int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); int32_t im_val = instr->Offset12Value(); int32_t addr = 0; switch (instr->PUField()) { case da_x: { // Format(instr, "'memop'cond'b 'rd, ['rn], #-'off12"); DCHECK(!instr->HasW()); addr = rn_val; rn_val -= im_val; set_register(rn, rn_val); break; } case ia_x: { // Format(instr, "'memop'cond'b 'rd, ['rn], #+'off12"); DCHECK(!instr->HasW()); addr = rn_val; rn_val += im_val; set_register(rn, rn_val); break; } case db_x: { // Format(instr, "'memop'cond'b 'rd, ['rn, #-'off12]'w"); rn_val -= im_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } case ib_x: { // Format(instr, "'memop'cond'b 'rd, ['rn, #+'off12]'w"); rn_val += im_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } default: { UNREACHABLE(); break; } } if (instr->HasB()) { if (instr->HasL()) { byte val = ReadBU(addr); set_register(rd, val); } else { byte val = get_register(rd); WriteB(addr, val); } } else { if (instr->HasL()) { set_register(rd, ReadW(addr)); } else { WriteW(addr, get_register(rd)); } } } void Simulator::DecodeType3(Instruction* instr) { int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); bool shifter_carry_out = 0; int32_t shifter_operand = GetShiftRm(instr, &shifter_carry_out); int32_t addr = 0; switch (instr->PUField()) { case da_x: { DCHECK(!instr->HasW()); Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm"); UNIMPLEMENTED(); break; } case ia_x: { if (instr->Bit(4) == 0) { // Memop. } else { if (instr->Bit(5) == 0) { switch (instr->Bits(22, 21)) { case 0: if (instr->Bit(20) == 0) { if (instr->Bit(6) == 0) { // Pkhbt. uint32_t rn_val = get_register(rn); uint32_t rm_val = get_register(instr->RmValue()); int32_t shift = instr->Bits(11, 7); rm_val <<= shift; set_register(rd, (rn_val & 0xFFFF) | (rm_val & 0xFFFF0000U)); } else { // Pkhtb. uint32_t rn_val = get_register(rn); int32_t rm_val = get_register(instr->RmValue()); int32_t shift = instr->Bits(11, 7); if (shift == 0) { shift = 32; } rm_val >>= shift; set_register(rd, (rn_val & 0xFFFF0000U) | (rm_val & 0xFFFF)); } } else { UNIMPLEMENTED(); } break; case 1: UNIMPLEMENTED(); break; case 2: UNIMPLEMENTED(); break; case 3: { // Usat. int32_t sat_pos = instr->Bits(20, 16); int32_t sat_val = (1 << sat_pos) - 1; int32_t shift = instr->Bits(11, 7); int32_t shift_type = instr->Bit(6); int32_t rm_val = get_register(instr->RmValue()); if (shift_type == 0) { // LSL rm_val <<= shift; } else { // ASR rm_val >>= shift; } // If saturation occurs, the Q flag should be set in the CPSR. // There is no Q flag yet, and no instruction (MRS) to read the // CPSR directly. if (rm_val > sat_val) { rm_val = sat_val; } else if (rm_val < 0) { rm_val = 0; } set_register(rd, rm_val); break; } } } else { switch (instr->Bits(22, 21)) { case 0: UNIMPLEMENTED(); break; case 1: if (instr->Bits(9, 6) == 1) { if (instr->Bit(20) == 0) { if (instr->Bits(19, 16) == 0xF) { // Sxtb. int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, static_cast(rm_val)); } else { // Sxtab. int32_t rn_val = get_register(rn); int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + static_cast(rm_val)); } } else { if (instr->Bits(19, 16) == 0xF) { // Sxth. int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, static_cast(rm_val)); } else { // Sxtah. int32_t rn_val = get_register(rn); int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + static_cast(rm_val)); } } } else if (instr->Bits(27, 16) == 0x6BF && instr->Bits(11, 4) == 0xF3) { // Rev. uint32_t rm_val = get_register(instr->RmValue()); set_register(rd, ByteReverse(rm_val)); } else { UNREACHABLE(); } break; case 2: if ((instr->Bit(20) == 0) && (instr->Bits(9, 6) == 1)) { if (instr->Bits(19, 16) == 0xF) { // Uxtb16. uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, (rm_val & 0xFF) | (rm_val & 0xFF0000)); } else { UNIMPLEMENTED(); } } else { UNIMPLEMENTED(); } break; case 3: if ((instr->Bits(9, 6) == 1)) { if (instr->Bit(20) == 0) { if (instr->Bits(19, 16) == 0xF) { // Uxtb. uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, (rm_val & 0xFF)); } else { // Uxtab. uint32_t rn_val = get_register(rn); uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + (rm_val & 0xFF)); } } else { if (instr->Bits(19, 16) == 0xF) { // Uxth. uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, (rm_val & 0xFFFF)); } else { // Uxtah. uint32_t rn_val = get_register(rn); uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + (rm_val & 0xFFFF)); } } } else { // PU == 0b01, BW == 0b11, Bits(9, 6) != 0b0001 if ((instr->Bits(20, 16) == 0x1F) && (instr->Bits(11, 4) == 0xF3)) { // Rbit. uint32_t rm_val = get_register(instr->RmValue()); set_register(rd, base::bits::ReverseBits(rm_val)); } else { UNIMPLEMENTED(); } } break; } } return; } break; } case db_x: { if (instr->Bits(22, 20) == 0x5) { if (instr->Bits(7, 4) == 0x1) { int rm = instr->RmValue(); int32_t rm_val = get_register(rm); int rs = instr->RsValue(); int32_t rs_val = get_register(rs); if (instr->Bits(15, 12) == 0xF) { // SMMUL (in V8 notation matching ARM ISA format) // Format(instr, "smmul'cond 'rn, 'rm, 'rs"); rn_val = base::bits::SignedMulHigh32(rm_val, rs_val); } else { // SMMLA (in V8 notation matching ARM ISA format) // Format(instr, "smmla'cond 'rn, 'rm, 'rs, 'rd"); int rd = instr->RdValue(); int32_t rd_val = get_register(rd); rn_val = base::bits::SignedMulHighAndAdd32(rm_val, rs_val, rd_val); } set_register(rn, rn_val); return; } } if (instr->Bits(5, 4) == 0x1) { if ((instr->Bit(22) == 0x0) && (instr->Bit(20) == 0x1)) { // (s/u)div (in V8 notation matching ARM ISA format) rn = rm/rs // Format(instr, "'(s/u)div'cond'b 'rn, 'rm, 'rs); int rm = instr->RmValue(); int32_t rm_val = get_register(rm); int rs = instr->RsValue(); int32_t rs_val = get_register(rs); int32_t ret_val = 0; // udiv if (instr->Bit(21) == 0x1) { ret_val = bit_cast(base::bits::UnsignedDiv32( bit_cast(rm_val), bit_cast(rs_val))); } else { ret_val = base::bits::SignedDiv32(rm_val, rs_val); } set_register(rn, ret_val); return; } } // Format(instr, "'memop'cond'b 'rd, ['rn, -'shift_rm]'w"); addr = rn_val - shifter_operand; if (instr->HasW()) { set_register(rn, addr); } break; } case ib_x: { if (instr->HasW() && (instr->Bits(6, 4) == 0x5)) { uint32_t widthminus1 = static_cast(instr->Bits(20, 16)); uint32_t lsbit = static_cast(instr->Bits(11, 7)); uint32_t msbit = widthminus1 + lsbit; if (msbit <= 31) { if (instr->Bit(22)) { // ubfx - unsigned bitfield extract. uint32_t rm_val = static_cast(get_register(instr->RmValue())); uint32_t extr_val = rm_val << (31 - msbit); extr_val = extr_val >> (31 - widthminus1); set_register(instr->RdValue(), extr_val); } else { // sbfx - signed bitfield extract. int32_t rm_val = get_register(instr->RmValue()); int32_t extr_val = rm_val << (31 - msbit); extr_val = extr_val >> (31 - widthminus1); set_register(instr->RdValue(), extr_val); } } else { UNREACHABLE(); } return; } else if (!instr->HasW() && (instr->Bits(6, 4) == 0x1)) { uint32_t lsbit = static_cast(instr->Bits(11, 7)); uint32_t msbit = static_cast(instr->Bits(20, 16)); if (msbit >= lsbit) { // bfc or bfi - bitfield clear/insert. uint32_t rd_val = static_cast(get_register(instr->RdValue())); uint32_t bitcount = msbit - lsbit + 1; uint32_t mask = 0xFFFFFFFFu >> (32 - bitcount); rd_val &= ~(mask << lsbit); if (instr->RmValue() != 15) { // bfi - bitfield insert. uint32_t rm_val = static_cast(get_register(instr->RmValue())); rm_val &= mask; rd_val |= rm_val << lsbit; } set_register(instr->RdValue(), rd_val); } else { UNREACHABLE(); } return; } else { // Format(instr, "'memop'cond'b 'rd, ['rn, +'shift_rm]'w"); addr = rn_val + shifter_operand; if (instr->HasW()) { set_register(rn, addr); } } break; } default: { UNREACHABLE(); break; } } if (instr->HasB()) { if (instr->HasL()) { uint8_t byte = ReadB(addr); set_register(rd, byte); } else { uint8_t byte = get_register(rd); WriteB(addr, byte); } } else { if (instr->HasL()) { set_register(rd, ReadW(addr)); } else { WriteW(addr, get_register(rd)); } } } void Simulator::DecodeType4(Instruction* instr) { DCHECK_EQ(instr->Bit(22), 0); // only allowed to be set in privileged mode if (instr->HasL()) { // Format(instr, "ldm'cond'pu 'rn'w, 'rlist"); HandleRList(instr, true); } else { // Format(instr, "stm'cond'pu 'rn'w, 'rlist"); HandleRList(instr, false); } } void Simulator::DecodeType5(Instruction* instr) { // Format(instr, "b'l'cond 'target"); int off = (instr->SImmed24Value() << 2); intptr_t pc_address = get_pc(); if (instr->HasLink()) { set_register(lr, pc_address + kInstrSize); } int pc_reg = get_register(pc); set_pc(pc_reg + off); } void Simulator::DecodeType6(Instruction* instr) { DecodeType6CoprocessorIns(instr); } void Simulator::DecodeType7(Instruction* instr) { if (instr->Bit(24) == 1) { SoftwareInterrupt(instr); } else { switch (instr->CoprocessorValue()) { case 10: // Fall through. case 11: DecodeTypeVFP(instr); break; case 15: DecodeTypeCP15(instr); break; default: UNIMPLEMENTED(); } } } // void Simulator::DecodeTypeVFP(Instruction* instr) // The Following ARMv7 VFPv instructions are currently supported. // vmov :Sn = Rt // vmov :Rt = Sn // vcvt: Dd = Sm // vcvt: Sd = Dm // vcvt.f64.s32 Dd, Dd, # // Dd = vabs(Dm) // Sd = vabs(Sm) // Dd = vneg(Dm) // Sd = vneg(Sm) // Dd = vadd(Dn, Dm) // Sd = vadd(Sn, Sm) // Dd = vsub(Dn, Dm) // Sd = vsub(Sn, Sm) // Dd = vmul(Dn, Dm) // Sd = vmul(Sn, Sm) // Dd = vdiv(Dn, Dm) // Sd = vdiv(Sn, Sm) // vcmp(Dd, Dm) // vcmp(Sd, Sm) // Dd = vsqrt(Dm) // Sd = vsqrt(Sm) // vmrs // vdup.size Qd, Rt. void Simulator::DecodeTypeVFP(Instruction* instr) { DCHECK((instr->TypeValue() == 7) && (instr->Bit(24) == 0x0) ); DCHECK_EQ(instr->Bits(11, 9), 0x5); // Obtain single precision register codes. int m = instr->VFPMRegValue(kSinglePrecision); int d = instr->VFPDRegValue(kSinglePrecision); int n = instr->VFPNRegValue(kSinglePrecision); // Obtain double precision register codes. int vm = instr->VFPMRegValue(kDoublePrecision); int vd = instr->VFPDRegValue(kDoublePrecision); int vn = instr->VFPNRegValue(kDoublePrecision); if (instr->Bit(4) == 0) { if (instr->Opc1Value() == 0x7) { // Other data processing instructions if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x1)) { // vmov register to register. if (instr->SzValue() == 0x1) { uint32_t data[2]; get_d_register(vm, data); set_d_register(vd, data); } else { set_s_register(d, get_s_register(m)); } } else if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x3)) { // vabs if (instr->SzValue() == 0x1) { Float64 dm = get_double_from_d_register(vm); constexpr uint64_t kSignBit64 = uint64_t{1} << 63; Float64 dd = Float64::FromBits(dm.get_bits() & ~kSignBit64); dd = canonicalizeNaN(dd); set_d_register_from_double(vd, dd); } else { Float32 sm = get_float_from_s_register(m); constexpr uint32_t kSignBit32 = uint32_t{1} << 31; Float32 sd = Float32::FromBits(sm.get_bits() & ~kSignBit32); sd = canonicalizeNaN(sd); set_s_register_from_float(d, sd); } } else if ((instr->Opc2Value() == 0x1) && (instr->Opc3Value() == 0x1)) { // vneg if (instr->SzValue() == 0x1) { Float64 dm = get_double_from_d_register(vm); constexpr uint64_t kSignBit64 = uint64_t{1} << 63; Float64 dd = Float64::FromBits(dm.get_bits() ^ kSignBit64); dd = canonicalizeNaN(dd); set_d_register_from_double(vd, dd); } else { Float32 sm = get_float_from_s_register(m); constexpr uint32_t kSignBit32 = uint32_t{1} << 31; Float32 sd = Float32::FromBits(sm.get_bits() ^ kSignBit32); sd = canonicalizeNaN(sd); set_s_register_from_float(d, sd); } } else if ((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3)) { DecodeVCVTBetweenDoubleAndSingle(instr); } else if ((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) { DecodeVCVTBetweenFloatingPointAndInteger(instr); } else if ((instr->Opc2Value() == 0xA) && (instr->Opc3Value() == 0x3) && (instr->Bit(8) == 1)) { // vcvt.f64.s32 Dd, Dd, # int fraction_bits = 32 - ((instr->Bits(3, 0) << 1) | instr->Bit(5)); int fixed_value = get_sinteger_from_s_register(vd * 2); double divide = 1 << fraction_bits; set_d_register_from_double(vd, fixed_value / divide); } else if (((instr->Opc2Value() >> 1) == 0x6) && (instr->Opc3Value() & 0x1)) { DecodeVCVTBetweenFloatingPointAndInteger(instr); } else if (((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) && (instr->Opc3Value() & 0x1)) { DecodeVCMP(instr); } else if (((instr->Opc2Value() == 0x1)) && (instr->Opc3Value() == 0x3)) { // vsqrt lazily_initialize_fast_sqrt(isolate_); if (instr->SzValue() == 0x1) { double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = fast_sqrt(dm_value, isolate_); dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = fast_sqrt(sm_value, isolate_); sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else if (instr->Opc3Value() == 0x0) { // vmov immediate. if (instr->SzValue() == 0x1) { set_d_register_from_double(vd, instr->DoubleImmedVmov()); } else { // Cast double to float. float value = instr->DoubleImmedVmov().get_scalar(); set_s_register_from_float(d, value); } } else if (((instr->Opc2Value() == 0x6)) && (instr->Opc3Value() == 0x3)) { // vrintz - truncate if (instr->SzValue() == 0x1) { double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = trunc(dm_value); dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = truncf(sm_value); sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else { UNREACHABLE(); // Not used by V8. } } else if (instr->Opc1Value() == 0x3) { if (instr->Opc3Value() & 0x1) { // vsub if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value - dm_value; dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value - sm_value; sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else { // vadd if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value + dm_value; dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value + sm_value; sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } } else if ((instr->Opc1Value() == 0x2) && !(instr->Opc3Value() & 0x1)) { // vmul if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value * dm_value; dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value * sm_value; sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else if ((instr->Opc1Value() == 0x0)) { // vmla, vmls const bool is_vmls = (instr->Opc3Value() & 0x1); if (instr->SzValue() == 0x1) { const double dd_val = get_double_from_d_register(vd).get_scalar(); const double dn_val = get_double_from_d_register(vn).get_scalar(); const double dm_val = get_double_from_d_register(vm).get_scalar(); // Note: we do the mul and add/sub in separate steps to avoid getting a // result with too high precision. const double res = dn_val * dm_val; set_d_register_from_double(vd, res); if (is_vmls) { set_d_register_from_double(vd, canonicalizeNaN(dd_val - res)); } else { set_d_register_from_double(vd, canonicalizeNaN(dd_val + res)); } } else { const float sd_val = get_float_from_s_register(d).get_scalar(); const float sn_val = get_float_from_s_register(n).get_scalar(); const float sm_val = get_float_from_s_register(m).get_scalar(); // Note: we do the mul and add/sub in separate steps to avoid getting a // result with too high precision. const float res = sn_val * sm_val; set_s_register_from_float(d, res); if (is_vmls) { set_s_register_from_float(d, canonicalizeNaN(sd_val - res)); } else { set_s_register_from_float(d, canonicalizeNaN(sd_val + res)); } } } else if ((instr->Opc1Value() == 0x4) && !(instr->Opc3Value() & 0x1)) { // vdiv if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value / dm_value; div_zero_vfp_flag_ = (dm_value == 0); dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value / sm_value; div_zero_vfp_flag_ = (sm_value == 0); sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else { UNIMPLEMENTED(); // Not used by V8. } } else { if ((instr->VCValue() == 0x0) && (instr->VAValue() == 0x0)) { DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(instr); } else if ((instr->VLValue() == 0x0) && (instr->VCValue() == 0x1)) { if (instr->Bit(23) == 0) { // vmov (ARM core register to scalar) int vd = instr->VFPNRegValue(kDoublePrecision); int rt = instr->RtValue(); int opc1_opc2 = (instr->Bits(22, 21) << 2) | instr->Bits(6, 5); if ((opc1_opc2 & 0xB) == 0) { // NeonS32/NeonU32 uint32_t data[2]; get_d_register(vd, data); data[instr->Bit(21)] = get_register(rt); set_d_register(vd, data); } else { uint64_t data; get_d_register(vd, &data); uint64_t rt_value = get_register(rt); if ((opc1_opc2 & 0x8) != 0) { // NeonS8 / NeonU8 int i = opc1_opc2 & 0x7; int shift = i * kBitsPerByte; const uint64_t mask = 0xFF; data &= ~(mask << shift); data |= (rt_value & mask) << shift; set_d_register(vd, &data); } else if ((opc1_opc2 & 0x1) != 0) { // NeonS16 / NeonU16 int i = (opc1_opc2 >> 1) & 0x3; int shift = i * kBitsPerByte * kShortSize; const uint64_t mask = 0xFFFF; data &= ~(mask << shift); data |= (rt_value & mask) << shift; set_d_register(vd, &data); } else { UNREACHABLE(); // Not used by V8. } } } else { // vdup.size Qd, Rt. NeonSize size = Neon32; if (instr->Bit(5) != 0) size = Neon16; else if (instr->Bit(22) != 0) size = Neon8; int vd = instr->VFPNRegValue(kSimd128Precision); int rt = instr->RtValue(); uint32_t rt_value = get_register(rt); uint32_t q_data[4]; switch (size) { case Neon8: { rt_value &= 0xFF; uint8_t* dst = reinterpret_cast(q_data); for (int i = 0; i < 16; i++) { dst[i] = rt_value; } break; } case Neon16: { // Perform pairwise op. rt_value &= 0xFFFFu; uint32_t rt_rt = (rt_value << 16) | (rt_value & 0xFFFFu); for (int i = 0; i < 4; i++) { q_data[i] = rt_rt; } break; } case Neon32: { for (int i = 0; i < 4; i++) { q_data[i] = rt_value; } break; } default: UNREACHABLE(); break; } set_neon_register(vd, q_data); } } else if ((instr->VLValue() == 0x1) && (instr->VCValue() == 0x1)) { // vmov (scalar to ARM core register) int vn = instr->VFPNRegValue(kDoublePrecision); int rt = instr->RtValue(); int opc1_opc2 = (instr->Bits(22, 21) << 2) | instr->Bits(6, 5); uint64_t data; get_d_register(vn, &data); if ((opc1_opc2 & 0xB) == 0) { // NeonS32 / NeonU32 int32_t int_data[2]; memcpy(int_data, &data, sizeof(int_data)); set_register(rt, int_data[instr->Bit(21)]); } else { uint64_t data; get_d_register(vn, &data); bool u = instr->Bit(23) != 0; if ((opc1_opc2 & 0x8) != 0) { // NeonS8 / NeonU8 int i = opc1_opc2 & 0x7; int shift = i * kBitsPerByte; uint32_t scalar = (data >> shift) & 0xFFu; if (!u && (scalar & 0x80) != 0) scalar |= 0xFFFFFF00; set_register(rt, scalar); } else if ((opc1_opc2 & 0x1) != 0) { // NeonS16 / NeonU16 int i = (opc1_opc2 >> 1) & 0x3; int shift = i * kBitsPerByte * kShortSize; uint32_t scalar = (data >> shift) & 0xFFFFu; if (!u && (scalar & 0x8000) != 0) scalar |= 0xFFFF0000; set_register(rt, scalar); } else { UNREACHABLE(); // Not used by V8. } } } else if ((instr->VLValue() == 0x1) && (instr->VCValue() == 0x0) && (instr->VAValue() == 0x7) && (instr->Bits(19, 16) == 0x1)) { // vmrs uint32_t rt = instr->RtValue(); if (rt == 0xF) { Copy_FPSCR_to_APSR(); } else { // Emulate FPSCR from the Simulator flags. uint32_t fpscr = (n_flag_FPSCR_ << 31) | (z_flag_FPSCR_ << 30) | (c_flag_FPSCR_ << 29) | (v_flag_FPSCR_ << 28) | (FPSCR_default_NaN_mode_ << 25) | (inexact_vfp_flag_ << 4) | (underflow_vfp_flag_ << 3) | (overflow_vfp_flag_ << 2) | (div_zero_vfp_flag_ << 1) | (inv_op_vfp_flag_ << 0) | (FPSCR_rounding_mode_); set_register(rt, fpscr); } } else if ((instr->VLValue() == 0x0) && (instr->VCValue() == 0x0) && (instr->VAValue() == 0x7) && (instr->Bits(19, 16) == 0x1)) { // vmsr uint32_t rt = instr->RtValue(); if (rt == pc) { UNREACHABLE(); } else { uint32_t rt_value = get_register(rt); n_flag_FPSCR_ = (rt_value >> 31) & 1; z_flag_FPSCR_ = (rt_value >> 30) & 1; c_flag_FPSCR_ = (rt_value >> 29) & 1; v_flag_FPSCR_ = (rt_value >> 28) & 1; FPSCR_default_NaN_mode_ = (rt_value >> 25) & 1; inexact_vfp_flag_ = (rt_value >> 4) & 1; underflow_vfp_flag_ = (rt_value >> 3) & 1; overflow_vfp_flag_ = (rt_value >> 2) & 1; div_zero_vfp_flag_ = (rt_value >> 1) & 1; inv_op_vfp_flag_ = (rt_value >> 0) & 1; FPSCR_rounding_mode_ = static_cast((rt_value) & kVFPRoundingModeMask); } } else { UNIMPLEMENTED(); // Not used by V8. } } } void Simulator::DecodeTypeCP15(Instruction* instr) { DCHECK((instr->TypeValue() == 7) && (instr->Bit(24) == 0x0)); DCHECK_EQ(instr->CoprocessorValue(), 15); if (instr->Bit(4) == 1) { // mcr int crn = instr->Bits(19, 16); int crm = instr->Bits(3, 0); int opc1 = instr->Bits(23, 21); int opc2 = instr->Bits(7, 5); if ((opc1 == 0) && (crn == 7)) { // ARMv6 memory barrier operations. // Details available in ARM DDI 0406C.b, B3-1750. if (((crm == 10) && (opc2 == 5)) || // CP15DMB ((crm == 10) && (opc2 == 4)) || // CP15DSB ((crm == 5) && (opc2 == 4))) { // CP15ISB // These are ignored by the simulator for now. } else { UNIMPLEMENTED(); } } } else { UNIMPLEMENTED(); } } void Simulator::DecodeVMOVBetweenCoreAndSinglePrecisionRegisters( Instruction* instr) { DCHECK((instr->Bit(4) == 1) && (instr->VCValue() == 0x0) && (instr->VAValue() == 0x0)); int t = instr->RtValue(); int n = instr->VFPNRegValue(kSinglePrecision); bool to_arm_register = (instr->VLValue() == 0x1); if (to_arm_register) { int32_t int_value = get_sinteger_from_s_register(n); set_register(t, int_value); } else { int32_t rs_val = get_register(t); set_s_register_from_sinteger(n, rs_val); } } void Simulator::DecodeVCMP(Instruction* instr) { DCHECK((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7)); DCHECK(((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) && (instr->Opc3Value() & 0x1)); // Comparison. VFPRegPrecision precision = kSinglePrecision; if (instr->SzValue() == 0x1) { precision = kDoublePrecision; } int d = instr->VFPDRegValue(precision); int m = 0; if (instr->Opc2Value() == 0x4) { m = instr->VFPMRegValue(precision); } if (precision == kDoublePrecision) { double dd_value = get_double_from_d_register(d).get_scalar(); double dm_value = 0.0; if (instr->Opc2Value() == 0x4) { dm_value = get_double_from_d_register(m).get_scalar(); } // Raise exceptions for quiet NaNs if necessary. if (instr->Bit(7) == 1) { if (std::isnan(dd_value)) { inv_op_vfp_flag_ = true; } } Compute_FPSCR_Flags(dd_value, dm_value); } else { float sd_value = get_float_from_s_register(d).get_scalar(); float sm_value = 0.0; if (instr->Opc2Value() == 0x4) { sm_value = get_float_from_s_register(m).get_scalar(); } // Raise exceptions for quiet NaNs if necessary. if (instr->Bit(7) == 1) { if (std::isnan(sd_value)) { inv_op_vfp_flag_ = true; } } Compute_FPSCR_Flags(sd_value, sm_value); } } void Simulator::DecodeVCVTBetweenDoubleAndSingle(Instruction* instr) { DCHECK((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7)); DCHECK((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3)); VFPRegPrecision dst_precision = kDoublePrecision; VFPRegPrecision src_precision = kSinglePrecision; if (instr->SzValue() == 1) { dst_precision = kSinglePrecision; src_precision = kDoublePrecision; } int dst = instr->VFPDRegValue(dst_precision); int src = instr->VFPMRegValue(src_precision); if (dst_precision == kSinglePrecision) { double val = get_double_from_d_register(src).get_scalar(); set_s_register_from_float(dst, static_cast(val)); } else { float val = get_float_from_s_register(src).get_scalar(); set_d_register_from_double(dst, static_cast(val)); } } bool get_inv_op_vfp_flag(VFPRoundingMode mode, double val, bool unsigned_) { DCHECK((mode == RN) || (mode == RM) || (mode == RZ)); double max_uint = static_cast(0xFFFFFFFFu); double max_int = static_cast(kMaxInt); double min_int = static_cast(kMinInt); // Check for NaN. if (val != val) { return true; } // Check for overflow. This code works because 32bit integers can be // exactly represented by ieee-754 64bit floating-point values. switch (mode) { case RN: return unsigned_ ? (val >= (max_uint + 0.5)) || (val < -0.5) : (val >= (max_int + 0.5)) || (val < (min_int - 0.5)); case RM: return unsigned_ ? (val >= (max_uint + 1.0)) || (val < 0) : (val >= (max_int + 1.0)) || (val < min_int); case RZ: return unsigned_ ? (val >= (max_uint + 1.0)) || (val <= -1) : (val >= (max_int + 1.0)) || (val <= (min_int - 1.0)); default: UNREACHABLE(); } } // We call this function only if we had a vfp invalid exception. // It returns the correct saturated value. int VFPConversionSaturate(double val, bool unsigned_res) { if (val != val) { return 0; } else { if (unsigned_res) { return (val < 0) ? 0 : 0xFFFFFFFFu; } else { return (val < 0) ? kMinInt : kMaxInt; } } } int32_t Simulator::ConvertDoubleToInt(double val, bool unsigned_integer, VFPRoundingMode mode) { // TODO(jkummerow): These casts are undefined behavior if the integral // part of {val} does not fit into the destination type. int32_t result = unsigned_integer ? static_cast(val) : static_cast(val); inv_op_vfp_flag_ = get_inv_op_vfp_flag(mode, val, unsigned_integer); double abs_diff = unsigned_integer ? std::fabs(val - static_cast(result)) : std::fabs(val - result); inexact_vfp_flag_ = (abs_diff != 0); if (inv_op_vfp_flag_) { result = VFPConversionSaturate(val, unsigned_integer); } else { switch (mode) { case RN: { int val_sign = (val > 0) ? 1 : -1; if (abs_diff > 0.5) { result += val_sign; } else if (abs_diff == 0.5) { // Round to even if exactly halfway. result = ((result % 2) == 0) ? result : result + val_sign; } break; } case RM: result = result > val ? result - 1 : result; break; case RZ: // Nothing to do. break; default: UNREACHABLE(); } } return result; } void Simulator::DecodeVCVTBetweenFloatingPointAndInteger(Instruction* instr) { DCHECK((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7) && (instr->Bits(27, 23) == 0x1D)); DCHECK(((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) || (((instr->Opc2Value() >> 1) == 0x6) && (instr->Opc3Value() & 0x1))); // Conversion between floating-point and integer. bool to_integer = (instr->Bit(18) == 1); VFPRegPrecision src_precision = (instr->SzValue() == 1) ? kDoublePrecision : kSinglePrecision; if (to_integer) { // We are playing with code close to the C++ standard's limits below, // hence the very simple code and heavy checks. // // Note: // C++ defines default type casting from floating point to integer as // (close to) rounding toward zero ("fractional part discarded"). int dst = instr->VFPDRegValue(kSinglePrecision); int src = instr->VFPMRegValue(src_precision); // Bit 7 in vcvt instructions indicates if we should use the FPSCR rounding // mode or the default Round to Zero mode. VFPRoundingMode mode = (instr->Bit(7) != 1) ? FPSCR_rounding_mode_ : RZ; DCHECK((mode == RM) || (mode == RZ) || (mode == RN)); bool unsigned_integer = (instr->Bit(16) == 0); bool double_precision = (src_precision == kDoublePrecision); double val = double_precision ? get_double_from_d_register(src).get_scalar() : get_float_from_s_register(src).get_scalar(); int32_t temp = ConvertDoubleToInt(val, unsigned_integer, mode); // Update the destination register. set_s_register_from_sinteger(dst, temp); } else { bool unsigned_integer = (instr->Bit(7) == 0); int dst = instr->VFPDRegValue(src_precision); int src = instr->VFPMRegValue(kSinglePrecision); int val = get_sinteger_from_s_register(src); if (src_precision == kDoublePrecision) { if (unsigned_integer) { set_d_register_from_double( dst, static_cast(static_cast(val))); } else { set_d_register_from_double(dst, static_cast(val)); } } else { if (unsigned_integer) { set_s_register_from_float( dst, static_cast(static_cast(val))); } else { set_s_register_from_float(dst, static_cast(val)); } } } } // void Simulator::DecodeType6CoprocessorIns(Instruction* instr) // Decode Type 6 coprocessor instructions. // Dm = vmov(Rt, Rt2) // = vmov(Dm) // Ddst = MEM(Rbase + 4*offset). // MEM(Rbase + 4*offset) = Dsrc. void Simulator::DecodeType6CoprocessorIns(Instruction* instr) { DCHECK_EQ(instr->TypeValue(), 6); if (instr->CoprocessorValue() == 0xA) { switch (instr->OpcodeValue()) { case 0x8: case 0xA: case 0xC: case 0xE: { // Load and store single precision float to memory. int rn = instr->RnValue(); int vd = instr->VFPDRegValue(kSinglePrecision); int offset = instr->Immed8Value(); if (!instr->HasU()) { offset = -offset; } int32_t address = get_register(rn) + 4 * offset; // Load and store address for singles must be at least four-byte // aligned. DCHECK_EQ(address % 4, 0); if (instr->HasL()) { // Load single from memory: vldr. set_s_register_from_sinteger(vd, ReadW(address)); } else { // Store single to memory: vstr. WriteW(address, get_sinteger_from_s_register(vd)); } break; } case 0x4: case 0x5: case 0x6: case 0x7: case 0x9: case 0xB: // Load/store multiple single from memory: vldm/vstm. HandleVList(instr); break; default: UNIMPLEMENTED(); // Not used by V8. } } else if (instr->CoprocessorValue() == 0xB) { switch (instr->OpcodeValue()) { case 0x2: // Load and store double to two GP registers if (instr->Bits(7, 6) != 0 || instr->Bit(4) != 1) { UNIMPLEMENTED(); // Not used by V8. } else { int rt = instr->RtValue(); int rn = instr->RnValue(); int vm = instr->VFPMRegValue(kDoublePrecision); if (instr->HasL()) { uint32_t data[2]; get_d_register(vm, data); set_register(rt, data[0]); set_register(rn, data[1]); } else { int32_t data[] = { get_register(rt), get_register(rn) }; set_d_register(vm, reinterpret_cast(data)); } } break; case 0x8: case 0xA: case 0xC: case 0xE: { // Load and store double to memory. int rn = instr->RnValue(); int vd = instr->VFPDRegValue(kDoublePrecision); int offset = instr->Immed8Value(); if (!instr->HasU()) { offset = -offset; } int32_t address = get_register(rn) + 4 * offset; // Load and store address for doubles must be at least four-byte // aligned. DCHECK_EQ(address % 4, 0); if (instr->HasL()) { // Load double from memory: vldr. int32_t data[] = {ReadW(address), ReadW(address + 4)}; set_d_register(vd, reinterpret_cast(data)); } else { // Store double to memory: vstr. uint32_t data[2]; get_d_register(vd, data); WriteW(address, data[0]); WriteW(address + 4, data[1]); } break; } case 0x4: case 0x5: case 0x6: case 0x7: case 0x9: case 0xB: // Load/store multiple double from memory: vldm/vstm. HandleVList(instr); break; default: UNIMPLEMENTED(); // Not used by V8. } } else { UNIMPLEMENTED(); // Not used by V8. } } // Templated operations for NEON instructions. template U Widen(T value) { static_assert(sizeof(int64_t) > sizeof(T), "T must be int32_t or smaller"); static_assert(sizeof(U) > sizeof(T), "T must smaller than U"); return static_cast(value); } template U Narrow(T value) { static_assert(sizeof(int8_t) < sizeof(T), "T must be int16_t or larger"); static_assert(sizeof(U) < sizeof(T), "T must larger than U"); static_assert(std::is_unsigned() == std::is_unsigned(), "Signed-ness of T and U must match"); // Make sure value can be expressed in the smaller type; otherwise, the // casted result is implementation defined. DCHECK_LE(std::numeric_limits::min(), value); DCHECK_GE(std::numeric_limits::max(), value); return static_cast(value); } template T Clamp(int64_t value) { static_assert(sizeof(int64_t) > sizeof(T), "T must be int32_t or smaller"); int64_t min = static_cast(std::numeric_limits::min()); int64_t max = static_cast(std::numeric_limits::max()); int64_t clamped = std::max(min, std::min(max, value)); return static_cast(clamped); } template void Widen(Simulator* simulator, int Vd, int Vm) { static const int kLanes = 8 / sizeof(T); T src[kLanes]; U dst[kLanes]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kLanes; i++) { dst[i] = Widen(src[i]); } simulator->set_neon_register(Vd, dst); } template void Abs(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); T src[kElems]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kElems; i++) { src[i] = std::abs(src[i]); } simulator->set_neon_register(Vd, src); } template void Neg(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); T src[kElems]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kElems; i++) { src[i] = -src[i]; } simulator->set_neon_register(Vd, src); } template void SaturatingNarrow(Simulator* simulator, int Vd, int Vm) { static const int kLanes = 16 / sizeof(T); T src[kLanes]; U dst[kLanes]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kLanes; i++) { dst[i] = Narrow(Clamp(src[i])); } simulator->set_neon_register(Vd, dst); } template void AddSaturate(Simulator* simulator, int Vd, int Vm, int Vn) { static const int kLanes = 16 / sizeof(T); T src1[kLanes], src2[kLanes]; simulator->get_neon_register(Vn, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kLanes; i++) { src1[i] = Clamp(Widen(src1[i]) + Widen(src2[i])); } simulator->set_neon_register(Vd, src1); } template void SubSaturate(Simulator* simulator, int Vd, int Vm, int Vn) { static const int kLanes = 16 / sizeof(T); T src1[kLanes], src2[kLanes]; simulator->get_neon_register(Vn, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kLanes; i++) { src1[i] = Clamp(Widen(src1[i]) - Widen(src2[i])); } simulator->set_neon_register(Vd, src1); } template void Zip(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); static const int kPairs = kElems / 2; T src1[kElems], src2[kElems], dst1[kElems], dst2[kElems]; simulator->get_neon_register(Vd, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kPairs; i++) { dst1[i * 2] = src1[i]; dst1[i * 2 + 1] = src2[i]; dst2[i * 2] = src1[i + kPairs]; dst2[i * 2 + 1] = src2[i + kPairs]; } simulator->set_neon_register(Vd, dst1); simulator->set_neon_register(Vm, dst2); } template void Unzip(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); static const int kPairs = kElems / 2; T src1[kElems], src2[kElems], dst1[kElems], dst2[kElems]; simulator->get_neon_register(Vd, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kPairs; i++) { dst1[i] = src1[i * 2]; dst1[i + kPairs] = src2[i * 2]; dst2[i] = src1[i * 2 + 1]; dst2[i + kPairs] = src2[i * 2 + 1]; } simulator->set_neon_register(Vd, dst1); simulator->set_neon_register(Vm, dst2); } template void Transpose(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); static const int kPairs = kElems / 2; T src1[kElems], src2[kElems]; simulator->get_neon_register(Vd, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kPairs; i++) { std::swap(src1[2 * i + 1], src2[2 * i]); } simulator->set_neon_register(Vd, src1); simulator->set_neon_register
stop(s)\n"); PrintF(" stop unstop\n"); PrintF(" ignore the stop instruction at the current location\n"); PrintF(" from now on\n"); } else { PrintF("Unknown command: %s\n", cmd); } } } // Add all the breakpoints back to stop execution and enter the debugger // shell when hit. RedoBreakpoints(); #undef COMMAND_SIZE #undef ARG_SIZE #undef STR #undef XSTR } bool Simulator::ICacheMatch(void* one, void* two) { DCHECK_EQ(reinterpret_cast(one) & CachePage::kPageMask, 0); DCHECK_EQ(reinterpret_cast(two) & CachePage::kPageMask, 0); return one == two; } static uint32_t ICacheHash(void* key) { return static_cast(reinterpret_cast(key)) >> 2; } static bool AllOnOnePage(uintptr_t start, int size) { intptr_t start_page = (start & ~CachePage::kPageMask); intptr_t end_page = ((start + size) & ~CachePage::kPageMask); return start_page == end_page; } void Simulator::set_last_debugger_input(char* input) { DeleteArray(last_debugger_input_); last_debugger_input_ = input; } void Simulator::SetRedirectInstruction(Instruction* instruction) { instruction->SetInstructionBits(al | (0xF * B24) | kCallRtRedirected); } void Simulator::FlushICache(base::CustomMatcherHashMap* i_cache, void* start_addr, size_t size) { intptr_t start = reinterpret_cast(start_addr); int intra_line = (start & CachePage::kLineMask); start -= intra_line; size += intra_line; size = ((size - 1) | CachePage::kLineMask) + 1; int offset = (start & CachePage::kPageMask); while (!AllOnOnePage(start, size - 1)) { int bytes_to_flush = CachePage::kPageSize - offset; FlushOnePage(i_cache, start, bytes_to_flush); start += bytes_to_flush; size -= bytes_to_flush; DCHECK_EQ(0, start & CachePage::kPageMask); offset = 0; } if (size != 0) { FlushOnePage(i_cache, start, size); } } CachePage* Simulator::GetCachePage(base::CustomMatcherHashMap* i_cache, void* page) { base::HashMap::Entry* entry = i_cache->LookupOrInsert(page, ICacheHash(page)); if (entry->value == nullptr) { CachePage* new_page = new CachePage(); entry->value = new_page; } return reinterpret_cast(entry->value); } // Flush from start up to and not including start + size. void Simulator::FlushOnePage(base::CustomMatcherHashMap* i_cache, intptr_t start, int size) { DCHECK_LE(size, CachePage::kPageSize); DCHECK(AllOnOnePage(start, size - 1)); DCHECK_EQ(start & CachePage::kLineMask, 0); DCHECK_EQ(size & CachePage::kLineMask, 0); void* page = reinterpret_cast(start & (~CachePage::kPageMask)); int offset = (start & CachePage::kPageMask); CachePage* cache_page = GetCachePage(i_cache, page); char* valid_bytemap = cache_page->ValidityByte(offset); memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift); } void Simulator::CheckICache(base::CustomMatcherHashMap* i_cache, Instruction* instr) { intptr_t address = reinterpret_cast(instr); void* page = reinterpret_cast(address & (~CachePage::kPageMask)); void* line = reinterpret_cast(address & (~CachePage::kLineMask)); int offset = (address & CachePage::kPageMask); CachePage* cache_page = GetCachePage(i_cache, page); char* cache_valid_byte = cache_page->ValidityByte(offset); bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID); char* cached_line = cache_page->CachedData(offset & ~CachePage::kLineMask); if (cache_hit) { // Check that the data in memory matches the contents of the I-cache. CHECK_EQ(0, memcmp(reinterpret_cast(instr), cache_page->CachedData(offset), kInstrSize)); } else { // Cache miss. Load memory into the cache. memcpy(cached_line, line, CachePage::kLineLength); *cache_valid_byte = CachePage::LINE_VALID; } } Simulator::Simulator(Isolate* isolate) : isolate_(isolate) { // Set up simulator support first. Some of this information is needed to // setup the architecture state. size_t stack_size = 1 * 1024*1024; // allocate 1MB for stack stack_ = reinterpret_cast(malloc(stack_size)); pc_modified_ = false; icount_ = 0; break_pc_ = nullptr; break_instr_ = 0; // Set up architecture state. // All registers are initialized to zero to start with. for (int i = 0; i < num_registers; i++) { registers_[i] = 0; } n_flag_ = false; z_flag_ = false; c_flag_ = false; v_flag_ = false; // Initializing VFP registers. // All registers are initialized to zero to start with // even though s_registers_ & d_registers_ share the same // physical registers in the target. for (int i = 0; i < num_d_registers * 2; i++) { vfp_registers_[i] = 0; } n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = false; v_flag_FPSCR_ = false; FPSCR_rounding_mode_ = RN; FPSCR_default_NaN_mode_ = false; inv_op_vfp_flag_ = false; div_zero_vfp_flag_ = false; overflow_vfp_flag_ = false; underflow_vfp_flag_ = false; inexact_vfp_flag_ = false; // 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 // some buffer below. registers_[sp] = reinterpret_cast(stack_) + stack_size - 64; // The lr and pc are initialized to a known bad value that will cause an // access violation if the simulator ever tries to execute it. registers_[pc] = bad_lr; registers_[lr] = bad_lr; last_debugger_input_ = nullptr; } Simulator::~Simulator() { global_monitor_.Pointer()->RemoveProcessor(&global_monitor_processor_); free(stack_); } // Get the active Simulator for the current thread. Simulator* Simulator::current(Isolate* isolate) { v8::internal::Isolate::PerIsolateThreadData* isolate_data = isolate->FindOrAllocatePerThreadDataForThisThread(); DCHECK_NOT_NULL(isolate_data); Simulator* sim = isolate_data->simulator(); if (sim == nullptr) { // TODO(146): delete the simulator object when a thread/isolate goes away. sim = new Simulator(isolate); isolate_data->set_simulator(sim); } return sim; } // Sets the register in the architecture state. It will also deal with updating // Simulator internal state for special registers such as PC. void Simulator::set_register(int reg, int32_t value) { DCHECK((reg >= 0) && (reg < num_registers)); if (reg == pc) { pc_modified_ = true; } registers_[reg] = value; } // Get the register from the architecture state. This function does handle // the special case of accessing the PC register. int32_t Simulator::get_register(int reg) const { DCHECK((reg >= 0) && (reg < num_registers)); // Stupid code added to avoid bug in GCC. // See: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949 if (reg >= num_registers) return 0; // End stupid code. return registers_[reg] + ((reg == pc) ? Instruction::kPcLoadDelta : 0); } double Simulator::get_double_from_register_pair(int reg) { DCHECK((reg >= 0) && (reg < num_registers) && ((reg % 2) == 0)); double dm_val = 0.0; // Read the bits from the unsigned integer register_[] array // into the double precision floating point value and return it. char buffer[2 * sizeof(vfp_registers_[0])]; memcpy(buffer, ®isters_[reg], 2 * sizeof(registers_[0])); memcpy(&dm_val, buffer, 2 * sizeof(registers_[0])); return(dm_val); } void Simulator::set_register_pair_from_double(int reg, double* value) { DCHECK((reg >= 0) && (reg < num_registers) && ((reg % 2) == 0)); memcpy(registers_ + reg, value, sizeof(*value)); } void Simulator::set_dw_register(int dreg, const int* dbl) { DCHECK((dreg >= 0) && (dreg < num_d_registers)); registers_[dreg] = dbl[0]; registers_[dreg + 1] = dbl[1]; } void Simulator::get_d_register(int dreg, uint64_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value)); } void Simulator::set_d_register(int dreg, const uint64_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value)); } void Simulator::get_d_register(int dreg, uint32_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value) * 2); } void Simulator::set_d_register(int dreg, const uint32_t* value) { DCHECK((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters())); memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value) * 2); } template void Simulator::get_neon_register(int reg, T (&value)[SIZE / sizeof(T)]) { DCHECK(SIZE == kSimd128Size || SIZE == kDoubleSize); DCHECK_LE(0, reg); DCHECK_GT(SIZE == kSimd128Size ? num_q_registers : num_d_registers, reg); memcpy(value, vfp_registers_ + reg * (SIZE / 4), SIZE); } template void Simulator::set_neon_register(int reg, const T (&value)[SIZE / sizeof(T)]) { DCHECK(SIZE == kSimd128Size || SIZE == kDoubleSize); DCHECK_LE(0, reg); DCHECK_GT(SIZE == kSimd128Size ? num_q_registers : num_d_registers, reg); memcpy(vfp_registers_ + reg * (SIZE / 4), value, SIZE); } // Raw access to the PC register. void Simulator::set_pc(int32_t value) { pc_modified_ = true; registers_[pc] = value; } bool Simulator::has_bad_pc() const { return ((registers_[pc] == bad_lr) || (registers_[pc] == end_sim_pc)); } // Raw access to the PC register without the special adjustment when reading. int32_t Simulator::get_pc() const { return registers_[pc]; } // Getting from and setting into VFP registers. void Simulator::set_s_register(int sreg, unsigned int value) { DCHECK((sreg >= 0) && (sreg < num_s_registers)); vfp_registers_[sreg] = value; } unsigned int Simulator::get_s_register(int sreg) const { DCHECK((sreg >= 0) && (sreg < num_s_registers)); return vfp_registers_[sreg]; } template void Simulator::SetVFPRegister(int reg_index, const InputType& value) { unsigned bytes = register_size * sizeof(vfp_registers_[0]); DCHECK_EQ(sizeof(InputType), bytes); DCHECK_GE(reg_index, 0); if (register_size == 1) DCHECK(reg_index < num_s_registers); if (register_size == 2) DCHECK(reg_index < DwVfpRegister::NumRegisters()); memcpy(&vfp_registers_[reg_index * register_size], &value, bytes); } template ReturnType Simulator::GetFromVFPRegister(int reg_index) { unsigned bytes = register_size * sizeof(vfp_registers_[0]); DCHECK_EQ(sizeof(ReturnType), bytes); DCHECK_GE(reg_index, 0); if (register_size == 1) DCHECK(reg_index < num_s_registers); if (register_size == 2) DCHECK(reg_index < DwVfpRegister::NumRegisters()); ReturnType value; memcpy(&value, &vfp_registers_[register_size * reg_index], bytes); return value; } void Simulator::SetSpecialRegister(SRegisterFieldMask reg_and_mask, uint32_t value) { // Only CPSR_f is implemented. Of that, only N, Z, C and V are implemented. if ((reg_and_mask == CPSR_f) && ((value & ~kSpecialCondition) == 0)) { n_flag_ = ((value & (1 << 31)) != 0); z_flag_ = ((value & (1 << 30)) != 0); c_flag_ = ((value & (1 << 29)) != 0); v_flag_ = ((value & (1 << 28)) != 0); } else { UNIMPLEMENTED(); } } uint32_t Simulator::GetFromSpecialRegister(SRegister reg) { uint32_t result = 0; // Only CPSR_f is implemented. if (reg == CPSR) { if (n_flag_) result |= (1 << 31); if (z_flag_) result |= (1 << 30); if (c_flag_) result |= (1 << 29); if (v_flag_) result |= (1 << 28); } else { UNIMPLEMENTED(); } return result; } // Runtime FP routines take: // - two double arguments // - one double argument and zero or one integer arguments. // All are consructed here from r0-r3 or d0, d1 and r0. void Simulator::GetFpArgs(double* x, double* y, int32_t* z) { if (use_eabi_hardfloat()) { *x = get_double_from_d_register(0).get_scalar(); *y = get_double_from_d_register(1).get_scalar(); *z = get_register(0); } else { // Registers 0 and 1 -> x. *x = get_double_from_register_pair(0); // Register 2 and 3 -> y. *y = get_double_from_register_pair(2); // Register 2 -> z *z = get_register(2); } } // The return value is either in r0/r1 or d0. void Simulator::SetFpResult(const double& result) { if (use_eabi_hardfloat()) { char buffer[2 * sizeof(vfp_registers_[0])]; memcpy(buffer, &result, sizeof(buffer)); // Copy result to d0. memcpy(vfp_registers_, buffer, sizeof(buffer)); } else { char buffer[2 * sizeof(registers_[0])]; memcpy(buffer, &result, sizeof(buffer)); // Copy result to r0 and r1. memcpy(registers_, buffer, sizeof(buffer)); } } void Simulator::TrashCallerSaveRegisters() { // We don't trash the registers with the return value. registers_[2] = 0x50BAD4U; registers_[3] = 0x50BAD4U; registers_[12] = 0x50BAD4U; } int Simulator::ReadW(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); intptr_t* ptr = reinterpret_cast(addr); return *ptr; } int Simulator::ReadExW(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::Word); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); intptr_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::WriteW(int32_t addr, int value) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); intptr_t* ptr = reinterpret_cast(addr); *ptr = value; } int Simulator::WriteExW(int32_t addr, int value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::Word) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { intptr_t* ptr = reinterpret_cast(addr); *ptr = value; return 0; } else { return 1; } } uint16_t Simulator::ReadHU(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); uint16_t* ptr = reinterpret_cast(addr); return *ptr; } int16_t Simulator::ReadH(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); int16_t* ptr = reinterpret_cast(addr); return *ptr; } uint16_t Simulator::ReadExHU(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::HalfWord); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); uint16_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::WriteH(int32_t addr, uint16_t value) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); uint16_t* ptr = reinterpret_cast(addr); *ptr = value; } void Simulator::WriteH(int32_t addr, int16_t value) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); int16_t* ptr = reinterpret_cast(addr); *ptr = value; } int Simulator::WriteExH(int32_t addr, uint16_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::HalfWord) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { uint16_t* ptr = reinterpret_cast(addr); *ptr = value; return 0; } else { return 1; } } uint8_t Simulator::ReadBU(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); uint8_t* ptr = reinterpret_cast(addr); return *ptr; } int8_t Simulator::ReadB(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); int8_t* ptr = reinterpret_cast(addr); return *ptr; } uint8_t Simulator::ReadExBU(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::Byte); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); uint8_t* ptr = reinterpret_cast(addr); return *ptr; } void Simulator::WriteB(int32_t addr, uint8_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); uint8_t* ptr = reinterpret_cast(addr); *ptr = value; } void Simulator::WriteB(int32_t addr, int8_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); int8_t* ptr = reinterpret_cast(addr); *ptr = value; } int Simulator::WriteExB(int32_t addr, uint8_t value) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::Byte) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { uint8_t* ptr = reinterpret_cast(addr); *ptr = value; return 0; } else { return 1; } } int32_t* Simulator::ReadDW(int32_t addr) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoad(addr); int32_t* ptr = reinterpret_cast(addr); return ptr; } int32_t* Simulator::ReadExDW(int32_t addr) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyLoadExcl(addr, TransactionSize::DoubleWord); global_monitor_.Pointer()->NotifyLoadExcl_Locked(addr, &global_monitor_processor_); int32_t* ptr = reinterpret_cast(addr); return ptr; } void Simulator::WriteDW(int32_t addr, int32_t value1, int32_t value2) { // All supported ARM targets allow unaligned accesses, so we don't need to // check the alignment here. base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); local_monitor_.NotifyStore(addr); global_monitor_.Pointer()->NotifyStore_Locked(addr, &global_monitor_processor_); int32_t* ptr = reinterpret_cast(addr); *ptr++ = value1; *ptr = value2; } int Simulator::WriteExDW(int32_t addr, int32_t value1, int32_t value2) { base::LockGuard lock_guard(&global_monitor_.Pointer()->mutex); if (local_monitor_.NotifyStoreExcl(addr, TransactionSize::DoubleWord) && global_monitor_.Pointer()->NotifyStoreExcl_Locked( addr, &global_monitor_processor_)) { intptr_t* ptr = reinterpret_cast(addr); *ptr++ = value1; *ptr = value2; return 0; } else { return 1; } } // Returns the limit of the stack area to enable checking for stack overflows. uintptr_t Simulator::StackLimit(uintptr_t c_limit) const { // The simulator uses a separate JS stack. If we have exhausted the C stack, // we also drop down the JS limit to reflect the exhaustion on the JS stack. if (GetCurrentStackPosition() < c_limit) { return reinterpret_cast(get_sp()); } // Otherwise the limit is the JS stack. Leave a safety margin of 1024 bytes // to prevent overrunning the stack when pushing values. return reinterpret_cast(stack_) + 1024; } // Unsupported instructions use Format to print an error and stop execution. void Simulator::Format(Instruction* instr, const char* format) { PrintF("Simulator found unsupported instruction:\n 0x%08" V8PRIxPTR ": %s\n", reinterpret_cast(instr), format); UNIMPLEMENTED(); } // Checks if the current instruction should be executed based on its // condition bits. bool Simulator::ConditionallyExecute(Instruction* instr) { switch (instr->ConditionField()) { case eq: return z_flag_; case ne: return !z_flag_; case cs: return c_flag_; case cc: return !c_flag_; case mi: return n_flag_; case pl: return !n_flag_; case vs: return v_flag_; case vc: return !v_flag_; case hi: return c_flag_ && !z_flag_; case ls: return !c_flag_ || z_flag_; case ge: return n_flag_ == v_flag_; case lt: return n_flag_ != v_flag_; case gt: return !z_flag_ && (n_flag_ == v_flag_); case le: return z_flag_ || (n_flag_ != v_flag_); case al: return true; default: UNREACHABLE(); } return false; } // Calculate and set the Negative and Zero flags. void Simulator::SetNZFlags(int32_t val) { n_flag_ = (val < 0); z_flag_ = (val == 0); } // Set the Carry flag. void Simulator::SetCFlag(bool val) { c_flag_ = val; } // Set the oVerflow flag. void Simulator::SetVFlag(bool val) { v_flag_ = val; } // Calculate C flag value for additions. bool Simulator::CarryFrom(int32_t left, int32_t right, int32_t carry) { uint32_t uleft = static_cast(left); uint32_t uright = static_cast(right); uint32_t urest = 0xFFFFFFFFU - uleft; return (uright > urest) || (carry && (((uright + 1) > urest) || (uright > (urest - 1)))); } // Calculate C flag value for subtractions. bool Simulator::BorrowFrom(int32_t left, int32_t right, int32_t carry) { uint32_t uleft = static_cast(left); uint32_t uright = static_cast(right); return (uright > uleft) || (!carry && (((uright + 1) > uleft) || (uright > (uleft - 1)))); } // Calculate V flag value for additions and subtractions. bool Simulator::OverflowFrom(int32_t alu_out, int32_t left, int32_t right, bool addition) { bool overflow; if (addition) { // operands have the same sign overflow = ((left >= 0 && right >= 0) || (left < 0 && right < 0)) // and operands and result have different sign && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); } else { // operands have different signs overflow = ((left < 0 && right >= 0) || (left >= 0 && right < 0)) // and first operand and result have different signs && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); } return overflow; } // Support for VFP comparisons. void Simulator::Compute_FPSCR_Flags(float val1, float val2) { if (std::isnan(val1) || std::isnan(val2)) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = true; // All non-NaN cases. } else if (val1 == val2) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = true; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } else if (val1 < val2) { n_flag_FPSCR_ = true; z_flag_FPSCR_ = false; c_flag_FPSCR_ = false; v_flag_FPSCR_ = false; } else { // Case when (val1 > val2). n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } } void Simulator::Compute_FPSCR_Flags(double val1, double val2) { if (std::isnan(val1) || std::isnan(val2)) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = true; // All non-NaN cases. } else if (val1 == val2) { n_flag_FPSCR_ = false; z_flag_FPSCR_ = true; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } else if (val1 < val2) { n_flag_FPSCR_ = true; z_flag_FPSCR_ = false; c_flag_FPSCR_ = false; v_flag_FPSCR_ = false; } else { // Case when (val1 > val2). n_flag_FPSCR_ = false; z_flag_FPSCR_ = false; c_flag_FPSCR_ = true; v_flag_FPSCR_ = false; } } void Simulator::Copy_FPSCR_to_APSR() { n_flag_ = n_flag_FPSCR_; z_flag_ = z_flag_FPSCR_; c_flag_ = c_flag_FPSCR_; v_flag_ = v_flag_FPSCR_; } // Addressing Mode 1 - Data-processing operands: // Get the value based on the shifter_operand with register. int32_t Simulator::GetShiftRm(Instruction* instr, bool* carry_out) { ShiftOp shift = instr->ShiftField(); int shift_amount = instr->ShiftAmountValue(); int32_t result = get_register(instr->RmValue()); if (instr->Bit(4) == 0) { // by immediate if ((shift == ROR) && (shift_amount == 0)) { UNIMPLEMENTED(); return result; } else if (((shift == LSR) || (shift == ASR)) && (shift_amount == 0)) { shift_amount = 32; } switch (shift) { case ASR: { if (shift_amount == 0) { if (result < 0) { result = 0xFFFFFFFF; *carry_out = true; } else { result = 0; *carry_out = false; } } else { result >>= (shift_amount - 1); *carry_out = (result & 1) == 1; result >>= 1; } break; } case LSL: { if (shift_amount == 0) { *carry_out = c_flag_; } else { result <<= (shift_amount - 1); *carry_out = (result < 0); result <<= 1; } break; } case LSR: { if (shift_amount == 0) { result = 0; *carry_out = c_flag_; } else { uint32_t uresult = static_cast(result); uresult >>= (shift_amount - 1); *carry_out = (uresult & 1) == 1; uresult >>= 1; result = static_cast(uresult); } break; } case ROR: { if (shift_amount == 0) { *carry_out = c_flag_; } else { uint32_t left = static_cast(result) >> shift_amount; uint32_t right = static_cast(result) << (32 - shift_amount); result = right | left; *carry_out = (static_cast(result) >> 31) != 0; } break; } default: { UNREACHABLE(); break; } } } else { // by register int rs = instr->RsValue(); shift_amount = get_register(rs) & 0xFF; switch (shift) { case ASR: { if (shift_amount == 0) { *carry_out = c_flag_; } else if (shift_amount < 32) { result >>= (shift_amount - 1); *carry_out = (result & 1) == 1; result >>= 1; } else { DCHECK_GE(shift_amount, 32); if (result < 0) { *carry_out = true; result = 0xFFFFFFFF; } else { *carry_out = false; result = 0; } } break; } case LSL: { if (shift_amount == 0) { *carry_out = c_flag_; } else if (shift_amount < 32) { result <<= (shift_amount - 1); *carry_out = (result < 0); result <<= 1; } else if (shift_amount == 32) { *carry_out = (result & 1) == 1; result = 0; } else { DCHECK_GT(shift_amount, 32); *carry_out = false; result = 0; } break; } case LSR: { if (shift_amount == 0) { *carry_out = c_flag_; } else if (shift_amount < 32) { uint32_t uresult = static_cast(result); uresult >>= (shift_amount - 1); *carry_out = (uresult & 1) == 1; uresult >>= 1; result = static_cast(uresult); } else if (shift_amount == 32) { *carry_out = (result < 0); result = 0; } else { *carry_out = false; result = 0; } break; } case ROR: { if (shift_amount == 0) { *carry_out = c_flag_; } else { uint32_t left = static_cast(result) >> shift_amount; uint32_t right = static_cast(result) << (32 - shift_amount); result = right | left; *carry_out = (static_cast(result) >> 31) != 0; } break; } default: { UNREACHABLE(); break; } } } return result; } // Addressing Mode 1 - Data-processing operands: // Get the value based on the shifter_operand with immediate. int32_t Simulator::GetImm(Instruction* instr, bool* carry_out) { int rotate = instr->RotateValue() * 2; int immed8 = instr->Immed8Value(); int imm = base::bits::RotateRight32(immed8, rotate); *carry_out = (rotate == 0) ? c_flag_ : (imm < 0); return imm; } static int count_bits(int bit_vector) { int count = 0; while (bit_vector != 0) { if ((bit_vector & 1) != 0) { count++; } bit_vector >>= 1; } return count; } int32_t Simulator::ProcessPU(Instruction* instr, int num_regs, int reg_size, intptr_t* start_address, intptr_t* end_address) { int rn = instr->RnValue(); int32_t rn_val = get_register(rn); switch (instr->PUField()) { case da_x: { UNIMPLEMENTED(); break; } case ia_x: { *start_address = rn_val; *end_address = rn_val + (num_regs * reg_size) - reg_size; rn_val = rn_val + (num_regs * reg_size); break; } case db_x: { *start_address = rn_val - (num_regs * reg_size); *end_address = rn_val - reg_size; rn_val = *start_address; break; } case ib_x: { *start_address = rn_val + reg_size; *end_address = rn_val + (num_regs * reg_size); rn_val = *end_address; break; } default: { UNREACHABLE(); break; } } return rn_val; } // Addressing Mode 4 - Load and Store Multiple void Simulator::HandleRList(Instruction* instr, bool load) { int rlist = instr->RlistValue(); int num_regs = count_bits(rlist); intptr_t start_address = 0; intptr_t end_address = 0; int32_t rn_val = ProcessPU(instr, num_regs, kPointerSize, &start_address, &end_address); intptr_t* address = reinterpret_cast(start_address); // Catch null pointers a little earlier. DCHECK(start_address > 8191 || start_address < 0); int reg = 0; while (rlist != 0) { if ((rlist & 1) != 0) { if (load) { set_register(reg, *address); } else { *address = get_register(reg); } address += 1; } reg++; rlist >>= 1; } DCHECK(end_address == ((intptr_t)address) - 4); if (instr->HasW()) { set_register(instr->RnValue(), rn_val); } } // Addressing Mode 6 - Load and Store Multiple Coprocessor registers. void Simulator::HandleVList(Instruction* instr) { VFPRegPrecision precision = (instr->SzValue() == 0) ? kSinglePrecision : kDoublePrecision; int operand_size = (precision == kSinglePrecision) ? 4 : 8; bool load = (instr->VLValue() == 0x1); int vd; int num_regs; vd = instr->VFPDRegValue(precision); if (precision == kSinglePrecision) { num_regs = instr->Immed8Value(); } else { num_regs = instr->Immed8Value() / 2; } intptr_t start_address = 0; intptr_t end_address = 0; int32_t rn_val = ProcessPU(instr, num_regs, operand_size, &start_address, &end_address); intptr_t* address = reinterpret_cast(start_address); for (int reg = vd; reg < vd + num_regs; reg++) { if (precision == kSinglePrecision) { if (load) { set_s_register_from_sinteger(reg, ReadW(reinterpret_cast(address))); } else { WriteW(reinterpret_cast(address), get_sinteger_from_s_register(reg)); } address += 1; } else { if (load) { int32_t data[] = {ReadW(reinterpret_cast(address)), ReadW(reinterpret_cast(address + 1))}; set_d_register(reg, reinterpret_cast(data)); } else { uint32_t data[2]; get_d_register(reg, data); WriteW(reinterpret_cast(address), data[0]); WriteW(reinterpret_cast(address + 1), data[1]); } address += 2; } } DCHECK(reinterpret_cast(address) - operand_size == end_address); if (instr->HasW()) { set_register(instr->RnValue(), rn_val); } } // Calls into the V8 runtime are based on this very simple interface. // 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 // 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, int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7, int32_t arg8); // These prototypes handle the four types of FP calls. typedef int64_t (*SimulatorRuntimeCompareCall)(double darg0, double darg1); typedef double (*SimulatorRuntimeFPFPCall)(double darg0, double darg1); typedef double (*SimulatorRuntimeFPCall)(double darg0); typedef double (*SimulatorRuntimeFPIntCall)(double darg0, int32_t arg0); // This signature supports direct call in to API function native callback // (refer to InvocationCallback in v8.h). typedef void (*SimulatorRuntimeDirectApiCall)(int32_t arg0); typedef void (*SimulatorRuntimeProfilingApiCall)(int32_t arg0, void* arg1); // This signature supports direct call to accessor getter callback. typedef void (*SimulatorRuntimeDirectGetterCall)(int32_t arg0, int32_t arg1); typedef void (*SimulatorRuntimeProfilingGetterCall)( int32_t arg0, int32_t arg1, void* arg2); // Software interrupt instructions are used by the simulator to call into the // C-based V8 runtime. void Simulator::SoftwareInterrupt(Instruction* instr) { int svc = instr->SvcValue(); switch (svc) { case kCallRtRedirected: { // Check if stack is aligned. Error if not aligned is reported below to // include information on the function called. bool stack_aligned = (get_register(sp) & (::v8::internal::FLAG_sim_stack_alignment - 1)) == 0; Redirection* redirection = Redirection::FromInstruction(instr); int32_t arg0 = get_register(r0); int32_t arg1 = get_register(r1); int32_t arg2 = get_register(r2); int32_t arg3 = get_register(r3); int32_t* stack_pointer = reinterpret_cast(get_register(sp)); int32_t arg4 = stack_pointer[0]; int32_t arg5 = stack_pointer[1]; int32_t arg6 = stack_pointer[2]; int32_t arg7 = stack_pointer[3]; int32_t arg8 = stack_pointer[4]; STATIC_ASSERT(kMaxCParameters == 9); bool fp_call = (redirection->type() == ExternalReference::BUILTIN_FP_FP_CALL) || (redirection->type() == ExternalReference::BUILTIN_COMPARE_CALL) || (redirection->type() == ExternalReference::BUILTIN_FP_CALL) || (redirection->type() == ExternalReference::BUILTIN_FP_INT_CALL); // 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_lr = get_register(lr); intptr_t external = reinterpret_cast(redirection->external_function()); if (fp_call) { double dval0, dval1; // one or two double parameters int32_t ival; // zero or one integer parameters int64_t iresult = 0; // integer return value double dresult = 0; // double return value GetFpArgs(&dval0, &dval1, &ival); if (::v8::internal::FLAG_trace_sim || !stack_aligned) { SimulatorRuntimeCall generic_target = reinterpret_cast(external); switch (redirection->type()) { case ExternalReference::BUILTIN_FP_FP_CALL: case ExternalReference::BUILTIN_COMPARE_CALL: PrintF("Call to host function at %p with args %f, %f", reinterpret_cast(FUNCTION_ADDR(generic_target)), dval0, dval1); break; case ExternalReference::BUILTIN_FP_CALL: PrintF("Call to host function at %p with arg %f", reinterpret_cast(FUNCTION_ADDR(generic_target)), dval0); break; case ExternalReference::BUILTIN_FP_INT_CALL: PrintF("Call to host function at %p with args %f, %d", reinterpret_cast(FUNCTION_ADDR(generic_target)), dval0, ival); break; default: UNREACHABLE(); break; } if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); switch (redirection->type()) { case ExternalReference::BUILTIN_COMPARE_CALL: { SimulatorRuntimeCompareCall target = reinterpret_cast(external); iresult = target(dval0, dval1); set_register(r0, static_cast(iresult)); set_register(r1, static_cast(iresult >> 32)); break; } case ExternalReference::BUILTIN_FP_FP_CALL: { SimulatorRuntimeFPFPCall target = reinterpret_cast(external); dresult = target(dval0, dval1); SetFpResult(dresult); break; } case ExternalReference::BUILTIN_FP_CALL: { SimulatorRuntimeFPCall target = reinterpret_cast(external); dresult = target(dval0); SetFpResult(dresult); break; } case ExternalReference::BUILTIN_FP_INT_CALL: { SimulatorRuntimeFPIntCall target = reinterpret_cast(external); dresult = target(dval0, ival); SetFpResult(dresult); break; } default: UNREACHABLE(); break; } if (::v8::internal::FLAG_trace_sim || !stack_aligned) { switch (redirection->type()) { case ExternalReference::BUILTIN_COMPARE_CALL: PrintF("Returned %08x\n", static_cast(iresult)); break; case ExternalReference::BUILTIN_FP_FP_CALL: case ExternalReference::BUILTIN_FP_CALL: case ExternalReference::BUILTIN_FP_INT_CALL: PrintF("Returned %f\n", dresult); break; default: UNREACHABLE(); break; } } } else if (redirection->type() == ExternalReference::DIRECT_API_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x", reinterpret_cast(external), arg0); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeDirectApiCall target = reinterpret_cast(external); target(arg0); } else if ( redirection->type() == ExternalReference::PROFILING_API_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x %08x", reinterpret_cast(external), arg0, arg1); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeProfilingApiCall target = reinterpret_cast(external); target(arg0, Redirection::ReverseRedirection(arg1)); } else if ( redirection->type() == ExternalReference::DIRECT_GETTER_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x %08x", reinterpret_cast(external), arg0, arg1); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeDirectGetterCall target = reinterpret_cast(external); target(arg0, arg1); } else if ( redirection->type() == ExternalReference::PROFILING_GETTER_CALL) { if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF("Call to host function at %p args %08x %08x %08x", reinterpret_cast(external), arg0, arg1, arg2); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); SimulatorRuntimeProfilingGetterCall target = reinterpret_cast( external); target(arg0, arg1, Redirection::ReverseRedirection(arg2)); } else { // builtin call. DCHECK(redirection->type() == ExternalReference::BUILTIN_CALL || redirection->type() == ExternalReference::BUILTIN_CALL_PAIR); SimulatorRuntimeCall target = reinterpret_cast(external); if (::v8::internal::FLAG_trace_sim || !stack_aligned) { PrintF( "Call to host function at %p " "args %08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x", reinterpret_cast(FUNCTION_ADDR(target)), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); if (!stack_aligned) { PrintF(" with unaligned stack %08x\n", get_register(sp)); } PrintF("\n"); } CHECK(stack_aligned); int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); int32_t lo_res = static_cast(result); int32_t hi_res = static_cast(result >> 32); if (::v8::internal::FLAG_trace_sim) { PrintF("Returned %08x\n", lo_res); } set_register(r0, lo_res); set_register(r1, hi_res); } set_register(lr, saved_lr); set_pc(get_register(lr)); break; } case kBreakpoint: { ArmDebugger dbg(this); dbg.Debug(); break; } // stop uses all codes greater than 1 << 23. default: { if (svc >= (1 << 23)) { uint32_t code = svc & kStopCodeMask; if (isWatchedStop(code)) { IncreaseStopCounter(code); } // Stop if it is enabled, otherwise go on jumping over the stop // and the message address. if (isEnabledStop(code)) { ArmDebugger dbg(this); dbg.Stop(instr); } } else { // This is not a valid svc code. UNREACHABLE(); break; } } } } float Simulator::canonicalizeNaN(float value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr uint32_t kDefaultNaN = 0x7FC00000u; if (FPSCR_default_NaN_mode_ && std::isnan(value)) { value = bit_cast(kDefaultNaN); } return value; } Float32 Simulator::canonicalizeNaN(Float32 value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr Float32 kDefaultNaN = Float32::FromBits(0x7FC00000u); return FPSCR_default_NaN_mode_ && value.is_nan() ? kDefaultNaN : value; } double Simulator::canonicalizeNaN(double value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr uint64_t kDefaultNaN = uint64_t{0x7FF8000000000000}; if (FPSCR_default_NaN_mode_ && std::isnan(value)) { value = bit_cast(kDefaultNaN); } return value; } Float64 Simulator::canonicalizeNaN(Float64 value) { // Default NaN value, see "NaN handling" in "IEEE 754 standard implementation // choices" of the ARM Reference Manual. constexpr Float64 kDefaultNaN = Float64::FromBits(uint64_t{0x7FF8000000000000}); return FPSCR_default_NaN_mode_ && value.is_nan() ? kDefaultNaN : value; } // Stop helper functions. bool Simulator::isStopInstruction(Instruction* instr) { return (instr->Bits(27, 24) == 0xF) && (instr->SvcValue() >= kStopCode); } bool Simulator::isWatchedStop(uint32_t code) { DCHECK_LE(code, kMaxStopCode); return code < kNumOfWatchedStops; } bool Simulator::isEnabledStop(uint32_t code) { DCHECK_LE(code, kMaxStopCode); // Unwatched stops are always enabled. return !isWatchedStop(code) || !(watched_stops_[code].count & kStopDisabledBit); } void Simulator::EnableStop(uint32_t code) { DCHECK(isWatchedStop(code)); if (!isEnabledStop(code)) { watched_stops_[code].count &= ~kStopDisabledBit; } } void Simulator::DisableStop(uint32_t code) { DCHECK(isWatchedStop(code)); if (isEnabledStop(code)) { watched_stops_[code].count |= kStopDisabledBit; } } void Simulator::IncreaseStopCounter(uint32_t code) { DCHECK_LE(code, kMaxStopCode); DCHECK(isWatchedStop(code)); if ((watched_stops_[code].count & ~(1 << 31)) == 0x7FFFFFFF) { PrintF("Stop counter for code %i has overflowed.\n" "Enabling this code and reseting the counter to 0.\n", code); watched_stops_[code].count = 0; EnableStop(code); } else { watched_stops_[code].count++; } } // Print a stop status. void Simulator::PrintStopInfo(uint32_t code) { DCHECK_LE(code, kMaxStopCode); if (!isWatchedStop(code)) { PrintF("Stop not watched."); } else { const char* state = isEnabledStop(code) ? "Enabled" : "Disabled"; int32_t count = watched_stops_[code].count & ~kStopDisabledBit; // Don't print the state of unused breakpoints. if (count != 0) { if (watched_stops_[code].desc) { PrintF("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n", code, code, state, count, watched_stops_[code].desc); } else { PrintF("stop %i - 0x%x: \t%s, \tcounter = %i\n", code, code, state, count); } } } } // Handle execution based on instruction types. // Instruction types 0 and 1 are both rolled into one function because they // only differ in the handling of the shifter_operand. void Simulator::DecodeType01(Instruction* instr) { int type = instr->TypeValue(); if ((type == 0) && instr->IsSpecialType0()) { // multiply instruction or extra loads and stores if (instr->Bits(7, 4) == 9) { if (instr->Bit(24) == 0) { // Raw field decoding here. Multiply instructions have their Rd in // funny places. int rn = instr->RnValue(); int rm = instr->RmValue(); int rs = instr->RsValue(); int32_t rs_val = get_register(rs); int32_t rm_val = get_register(rm); if (instr->Bit(23) == 0) { if (instr->Bit(21) == 0) { // The MUL instruction description (A 4.1.33) refers to Rd as being // the destination for the operation, but it confusingly uses the // Rn field to encode it. // Format(instr, "mul'cond's 'rn, 'rm, 'rs"); int rd = rn; // Remap the rn field to the Rd register. int32_t alu_out = rm_val * rs_val; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); } } else { int rd = instr->RdValue(); int32_t acc_value = get_register(rd); if (instr->Bit(22) == 0) { // The MLA instruction description (A 4.1.28) refers to the order // of registers as "Rd, Rm, Rs, Rn". But confusingly it uses the // Rn field to encode the Rd register and the Rd field to encode // the Rn register. // Format(instr, "mla'cond's 'rn, 'rm, 'rs, 'rd"); int32_t mul_out = rm_val * rs_val; int32_t result = acc_value + mul_out; set_register(rn, result); } else { // Format(instr, "mls'cond's 'rn, 'rm, 'rs, 'rd"); int32_t mul_out = rm_val * rs_val; int32_t result = acc_value - mul_out; set_register(rn, result); } } } else { // The signed/long multiply instructions use the terms RdHi and RdLo // when referring to the target registers. They are mapped to the Rn // and Rd fields as follows: // RdLo == Rd // RdHi == Rn (This is confusingly stored in variable rd here // because the mul instruction from above uses the // Rn field to encode the Rd register. Good luck figuring // this out without reading the ARM instruction manual // at a very detailed level.) // Format(instr, "'um'al'cond's 'rd, 'rn, 'rs, 'rm"); int rd_hi = rn; // Remap the rn field to the RdHi register. int rd_lo = instr->RdValue(); int32_t hi_res = 0; int32_t lo_res = 0; if (instr->Bit(22) == 1) { int64_t left_op = static_cast(rm_val); int64_t right_op = static_cast(rs_val); uint64_t result = left_op * right_op; hi_res = static_cast(result >> 32); lo_res = static_cast(result & 0xFFFFFFFF); } else { // unsigned multiply uint64_t left_op = static_cast(rm_val); uint64_t right_op = static_cast(rs_val); uint64_t result = left_op * right_op; hi_res = static_cast(result >> 32); lo_res = static_cast(result & 0xFFFFFFFF); } set_register(rd_lo, lo_res); set_register(rd_hi, hi_res); if (instr->HasS()) { UNIMPLEMENTED(); } } } else { if (instr->Bits(24, 23) == 3) { if (instr->Bit(20) == 1) { // ldrex int rt = instr->RtValue(); int rn = instr->RnValue(); int32_t addr = get_register(rn); switch (instr->Bits(22, 21)) { case 0: { // Format(instr, "ldrex'cond 'rt, ['rn]"); int value = ReadExW(addr); set_register(rt, value); break; } case 1: { // Format(instr, "ldrexd'cond 'rt, ['rn]"); int* rn_data = ReadExDW(addr); set_dw_register(rt, rn_data); break; } case 2: { // Format(instr, "ldrexb'cond 'rt, ['rn]"); uint8_t value = ReadExBU(addr); set_register(rt, value); break; } case 3: { // Format(instr, "ldrexh'cond 'rt, ['rn]"); uint16_t value = ReadExHU(addr); set_register(rt, value); break; } default: UNREACHABLE(); break; } } else { // The instruction is documented as strex rd, rt, [rn], but the // "rt" register is using the rm bits. int rd = instr->RdValue(); int rt = instr->RmValue(); int rn = instr->RnValue(); DCHECK_NE(rd, rn); DCHECK_NE(rd, rt); int32_t addr = get_register(rn); switch (instr->Bits(22, 21)) { case 0: { // Format(instr, "strex'cond 'rd, 'rm, ['rn]"); int value = get_register(rt); int status = WriteExW(addr, value); set_register(rd, status); break; } case 1: { // Format(instr, "strexd'cond 'rd, 'rm, ['rn]"); DCHECK_EQ(rt % 2, 0); int32_t value1 = get_register(rt); int32_t value2 = get_register(rt + 1); int status = WriteExDW(addr, value1, value2); set_register(rd, status); break; } case 2: { // Format(instr, "strexb'cond 'rd, 'rm, ['rn]"); uint8_t value = get_register(rt); int status = WriteExB(addr, value); set_register(rd, status); break; } case 3: { // Format(instr, "strexh'cond 'rd, 'rm, ['rn]"); uint16_t value = get_register(rt); int status = WriteExH(addr, value); set_register(rd, status); break; } default: UNREACHABLE(); break; } } } else { UNIMPLEMENTED(); // Not used by V8. } } } else { // extra load/store instructions int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); int32_t addr = 0; if (instr->Bit(22) == 0) { int rm = instr->RmValue(); int32_t rm_val = get_register(rm); switch (instr->PUField()) { case da_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], -'rm"); DCHECK(!instr->HasW()); addr = rn_val; rn_val -= rm_val; set_register(rn, rn_val); break; } case ia_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], +'rm"); DCHECK(!instr->HasW()); addr = rn_val; rn_val += rm_val; set_register(rn, rn_val); break; } case db_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, -'rm]'w"); rn_val -= rm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } case ib_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, +'rm]'w"); rn_val += rm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } default: { // The PU field is a 2-bit field. UNREACHABLE(); break; } } } else { int32_t imm_val = (instr->ImmedHValue() << 4) | instr->ImmedLValue(); switch (instr->PUField()) { case da_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], #-'off8"); DCHECK(!instr->HasW()); addr = rn_val; rn_val -= imm_val; set_register(rn, rn_val); break; } case ia_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn], #+'off8"); DCHECK(!instr->HasW()); addr = rn_val; rn_val += imm_val; set_register(rn, rn_val); break; } case db_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, #-'off8]'w"); rn_val -= imm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } case ib_x: { // Format(instr, "'memop'cond'sign'h 'rd, ['rn, #+'off8]'w"); rn_val += imm_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } default: { // The PU field is a 2-bit field. UNREACHABLE(); break; } } } if (((instr->Bits(7, 4) & 0xD) == 0xD) && (instr->Bit(20) == 0)) { DCHECK_EQ(rd % 2, 0); if (instr->HasH()) { // The strd instruction. int32_t value1 = get_register(rd); int32_t value2 = get_register(rd+1); WriteDW(addr, value1, value2); } else { // The ldrd instruction. int* rn_data = ReadDW(addr); set_dw_register(rd, rn_data); } } else if (instr->HasH()) { if (instr->HasSign()) { if (instr->HasL()) { int16_t val = ReadH(addr); set_register(rd, val); } else { int16_t val = get_register(rd); WriteH(addr, val); } } else { if (instr->HasL()) { uint16_t val = ReadHU(addr); set_register(rd, val); } else { uint16_t val = get_register(rd); WriteH(addr, val); } } } else { // signed byte loads DCHECK(instr->HasSign()); DCHECK(instr->HasL()); int8_t val = ReadB(addr); set_register(rd, val); } return; } } else if ((type == 0) && instr->IsMiscType0()) { if ((instr->Bits(27, 23) == 2) && (instr->Bits(21, 20) == 2) && (instr->Bits(15, 4) == 0xF00)) { // MSR int rm = instr->RmValue(); DCHECK_NE(pc, rm); // UNPREDICTABLE SRegisterFieldMask sreg_and_mask = instr->BitField(22, 22) | instr->BitField(19, 16); SetSpecialRegister(sreg_and_mask, get_register(rm)); } else if ((instr->Bits(27, 23) == 2) && (instr->Bits(21, 20) == 0) && (instr->Bits(11, 0) == 0)) { // MRS int rd = instr->RdValue(); DCHECK_NE(pc, rd); // UNPREDICTABLE SRegister sreg = static_cast(instr->BitField(22, 22)); set_register(rd, GetFromSpecialRegister(sreg)); } else if (instr->Bits(22, 21) == 1) { int rm = instr->RmValue(); switch (instr->BitField(7, 4)) { case BX: set_pc(get_register(rm)); break; case BLX: { uint32_t old_pc = get_pc(); set_pc(get_register(rm)); set_register(lr, old_pc + kInstrSize); break; } case BKPT: { ArmDebugger dbg(this); PrintF("Simulator hit BKPT.\n"); dbg.Debug(); break; } default: UNIMPLEMENTED(); } } else if (instr->Bits(22, 21) == 3) { int rm = instr->RmValue(); int rd = instr->RdValue(); switch (instr->BitField(7, 4)) { case CLZ: { uint32_t bits = get_register(rm); int leading_zeros = 0; if (bits == 0) { leading_zeros = 32; } else { while ((bits & 0x80000000u) == 0) { bits <<= 1; leading_zeros++; } } set_register(rd, leading_zeros); break; } default: UNIMPLEMENTED(); } } else { PrintF("%08x\n", instr->InstructionBits()); UNIMPLEMENTED(); } } else if ((type == 1) && instr->IsNopLikeType1()) { if (instr->BitField(7, 0) == 0) { // NOP. } else if (instr->BitField(7, 0) == 20) { // CSDB. } else { PrintF("%08x\n", instr->InstructionBits()); UNIMPLEMENTED(); } } else { int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); int32_t shifter_operand = 0; bool shifter_carry_out = 0; if (type == 0) { shifter_operand = GetShiftRm(instr, &shifter_carry_out); } else { DCHECK_EQ(instr->TypeValue(), 1); shifter_operand = GetImm(instr, &shifter_carry_out); } int32_t alu_out; switch (instr->OpcodeField()) { case AND: { // Format(instr, "and'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "and'cond's 'rd, 'rn, 'imm"); alu_out = rn_val & shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case EOR: { // Format(instr, "eor'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "eor'cond's 'rd, 'rn, 'imm"); alu_out = rn_val ^ shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case SUB: { // Format(instr, "sub'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "sub'cond's 'rd, 'rn, 'imm"); alu_out = rn_val - shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(!BorrowFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); } break; } case RSB: { // Format(instr, "rsb'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "rsb'cond's 'rd, 'rn, 'imm"); alu_out = shifter_operand - rn_val; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(!BorrowFrom(shifter_operand, rn_val)); SetVFlag(OverflowFrom(alu_out, shifter_operand, rn_val, false)); } break; } case ADD: { // Format(instr, "add'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "add'cond's 'rd, 'rn, 'imm"); alu_out = rn_val + shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(CarryFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true)); } break; } case ADC: { // Format(instr, "adc'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "adc'cond's 'rd, 'rn, 'imm"); alu_out = rn_val + shifter_operand + GetCarry(); set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(CarryFrom(rn_val, shifter_operand, GetCarry())); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true)); } break; } case SBC: { // Format(instr, "sbc'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "sbc'cond's 'rd, 'rn, 'imm"); alu_out = (rn_val - shifter_operand) - (GetCarry() ? 0 : 1); set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(!BorrowFrom(rn_val, shifter_operand, GetCarry())); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); } break; } case RSC: { Format(instr, "rsc'cond's 'rd, 'rn, 'shift_rm"); Format(instr, "rsc'cond's 'rd, 'rn, 'imm"); break; } case TST: { if (instr->HasS()) { // Format(instr, "tst'cond 'rn, 'shift_rm"); // Format(instr, "tst'cond 'rn, 'imm"); alu_out = rn_val & shifter_operand; SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } else { // Format(instr, "movw'cond 'rd, 'imm"). alu_out = instr->ImmedMovwMovtValue(); set_register(rd, alu_out); } break; } case TEQ: { if (instr->HasS()) { // Format(instr, "teq'cond 'rn, 'shift_rm"); // Format(instr, "teq'cond 'rn, 'imm"); alu_out = rn_val ^ shifter_operand; SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } else { // Other instructions matching this pattern are handled in the // miscellaneous instructions part above. UNREACHABLE(); } break; } case CMP: { if (instr->HasS()) { // Format(instr, "cmp'cond 'rn, 'shift_rm"); // Format(instr, "cmp'cond 'rn, 'imm"); alu_out = rn_val - shifter_operand; SetNZFlags(alu_out); SetCFlag(!BorrowFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false)); } else { // Format(instr, "movt'cond 'rd, 'imm"). alu_out = (get_register(rd) & 0xFFFF) | (instr->ImmedMovwMovtValue() << 16); set_register(rd, alu_out); } break; } case CMN: { if (instr->HasS()) { // Format(instr, "cmn'cond 'rn, 'shift_rm"); // Format(instr, "cmn'cond 'rn, 'imm"); alu_out = rn_val + shifter_operand; SetNZFlags(alu_out); SetCFlag(CarryFrom(rn_val, shifter_operand)); SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true)); } else { // Other instructions matching this pattern are handled in the // miscellaneous instructions part above. UNREACHABLE(); } break; } case ORR: { // Format(instr, "orr'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "orr'cond's 'rd, 'rn, 'imm"); alu_out = rn_val | shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case MOV: { // Format(instr, "mov'cond's 'rd, 'shift_rm"); // Format(instr, "mov'cond's 'rd, 'imm"); alu_out = shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case BIC: { // Format(instr, "bic'cond's 'rd, 'rn, 'shift_rm"); // Format(instr, "bic'cond's 'rd, 'rn, 'imm"); alu_out = rn_val & ~shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } case MVN: { // Format(instr, "mvn'cond's 'rd, 'shift_rm"); // Format(instr, "mvn'cond's 'rd, 'imm"); alu_out = ~shifter_operand; set_register(rd, alu_out); if (instr->HasS()) { SetNZFlags(alu_out); SetCFlag(shifter_carry_out); } break; } default: { UNREACHABLE(); break; } } } } void Simulator::DecodeType2(Instruction* instr) { int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); int32_t im_val = instr->Offset12Value(); int32_t addr = 0; switch (instr->PUField()) { case da_x: { // Format(instr, "'memop'cond'b 'rd, ['rn], #-'off12"); DCHECK(!instr->HasW()); addr = rn_val; rn_val -= im_val; set_register(rn, rn_val); break; } case ia_x: { // Format(instr, "'memop'cond'b 'rd, ['rn], #+'off12"); DCHECK(!instr->HasW()); addr = rn_val; rn_val += im_val; set_register(rn, rn_val); break; } case db_x: { // Format(instr, "'memop'cond'b 'rd, ['rn, #-'off12]'w"); rn_val -= im_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } case ib_x: { // Format(instr, "'memop'cond'b 'rd, ['rn, #+'off12]'w"); rn_val += im_val; addr = rn_val; if (instr->HasW()) { set_register(rn, rn_val); } break; } default: { UNREACHABLE(); break; } } if (instr->HasB()) { if (instr->HasL()) { byte val = ReadBU(addr); set_register(rd, val); } else { byte val = get_register(rd); WriteB(addr, val); } } else { if (instr->HasL()) { set_register(rd, ReadW(addr)); } else { WriteW(addr, get_register(rd)); } } } void Simulator::DecodeType3(Instruction* instr) { int rd = instr->RdValue(); int rn = instr->RnValue(); int32_t rn_val = get_register(rn); bool shifter_carry_out = 0; int32_t shifter_operand = GetShiftRm(instr, &shifter_carry_out); int32_t addr = 0; switch (instr->PUField()) { case da_x: { DCHECK(!instr->HasW()); Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm"); UNIMPLEMENTED(); break; } case ia_x: { if (instr->Bit(4) == 0) { // Memop. } else { if (instr->Bit(5) == 0) { switch (instr->Bits(22, 21)) { case 0: if (instr->Bit(20) == 0) { if (instr->Bit(6) == 0) { // Pkhbt. uint32_t rn_val = get_register(rn); uint32_t rm_val = get_register(instr->RmValue()); int32_t shift = instr->Bits(11, 7); rm_val <<= shift; set_register(rd, (rn_val & 0xFFFF) | (rm_val & 0xFFFF0000U)); } else { // Pkhtb. uint32_t rn_val = get_register(rn); int32_t rm_val = get_register(instr->RmValue()); int32_t shift = instr->Bits(11, 7); if (shift == 0) { shift = 32; } rm_val >>= shift; set_register(rd, (rn_val & 0xFFFF0000U) | (rm_val & 0xFFFF)); } } else { UNIMPLEMENTED(); } break; case 1: UNIMPLEMENTED(); break; case 2: UNIMPLEMENTED(); break; case 3: { // Usat. int32_t sat_pos = instr->Bits(20, 16); int32_t sat_val = (1 << sat_pos) - 1; int32_t shift = instr->Bits(11, 7); int32_t shift_type = instr->Bit(6); int32_t rm_val = get_register(instr->RmValue()); if (shift_type == 0) { // LSL rm_val <<= shift; } else { // ASR rm_val >>= shift; } // If saturation occurs, the Q flag should be set in the CPSR. // There is no Q flag yet, and no instruction (MRS) to read the // CPSR directly. if (rm_val > sat_val) { rm_val = sat_val; } else if (rm_val < 0) { rm_val = 0; } set_register(rd, rm_val); break; } } } else { switch (instr->Bits(22, 21)) { case 0: UNIMPLEMENTED(); break; case 1: if (instr->Bits(9, 6) == 1) { if (instr->Bit(20) == 0) { if (instr->Bits(19, 16) == 0xF) { // Sxtb. int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, static_cast(rm_val)); } else { // Sxtab. int32_t rn_val = get_register(rn); int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + static_cast(rm_val)); } } else { if (instr->Bits(19, 16) == 0xF) { // Sxth. int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, static_cast(rm_val)); } else { // Sxtah. int32_t rn_val = get_register(rn); int32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + static_cast(rm_val)); } } } else if (instr->Bits(27, 16) == 0x6BF && instr->Bits(11, 4) == 0xF3) { // Rev. uint32_t rm_val = get_register(instr->RmValue()); set_register(rd, ByteReverse(rm_val)); } else { UNREACHABLE(); } break; case 2: if ((instr->Bit(20) == 0) && (instr->Bits(9, 6) == 1)) { if (instr->Bits(19, 16) == 0xF) { // Uxtb16. uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, (rm_val & 0xFF) | (rm_val & 0xFF0000)); } else { UNIMPLEMENTED(); } } else { UNIMPLEMENTED(); } break; case 3: if ((instr->Bits(9, 6) == 1)) { if (instr->Bit(20) == 0) { if (instr->Bits(19, 16) == 0xF) { // Uxtb. uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, (rm_val & 0xFF)); } else { // Uxtab. uint32_t rn_val = get_register(rn); uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + (rm_val & 0xFF)); } } else { if (instr->Bits(19, 16) == 0xF) { // Uxth. uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, (rm_val & 0xFFFF)); } else { // Uxtah. uint32_t rn_val = get_register(rn); uint32_t rm_val = get_register(instr->RmValue()); int32_t rotate = instr->Bits(11, 10); switch (rotate) { case 0: break; case 1: rm_val = (rm_val >> 8) | (rm_val << 24); break; case 2: rm_val = (rm_val >> 16) | (rm_val << 16); break; case 3: rm_val = (rm_val >> 24) | (rm_val << 8); break; } set_register(rd, rn_val + (rm_val & 0xFFFF)); } } } else { // PU == 0b01, BW == 0b11, Bits(9, 6) != 0b0001 if ((instr->Bits(20, 16) == 0x1F) && (instr->Bits(11, 4) == 0xF3)) { // Rbit. uint32_t rm_val = get_register(instr->RmValue()); set_register(rd, base::bits::ReverseBits(rm_val)); } else { UNIMPLEMENTED(); } } break; } } return; } break; } case db_x: { if (instr->Bits(22, 20) == 0x5) { if (instr->Bits(7, 4) == 0x1) { int rm = instr->RmValue(); int32_t rm_val = get_register(rm); int rs = instr->RsValue(); int32_t rs_val = get_register(rs); if (instr->Bits(15, 12) == 0xF) { // SMMUL (in V8 notation matching ARM ISA format) // Format(instr, "smmul'cond 'rn, 'rm, 'rs"); rn_val = base::bits::SignedMulHigh32(rm_val, rs_val); } else { // SMMLA (in V8 notation matching ARM ISA format) // Format(instr, "smmla'cond 'rn, 'rm, 'rs, 'rd"); int rd = instr->RdValue(); int32_t rd_val = get_register(rd); rn_val = base::bits::SignedMulHighAndAdd32(rm_val, rs_val, rd_val); } set_register(rn, rn_val); return; } } if (instr->Bits(5, 4) == 0x1) { if ((instr->Bit(22) == 0x0) && (instr->Bit(20) == 0x1)) { // (s/u)div (in V8 notation matching ARM ISA format) rn = rm/rs // Format(instr, "'(s/u)div'cond'b 'rn, 'rm, 'rs); int rm = instr->RmValue(); int32_t rm_val = get_register(rm); int rs = instr->RsValue(); int32_t rs_val = get_register(rs); int32_t ret_val = 0; // udiv if (instr->Bit(21) == 0x1) { ret_val = bit_cast(base::bits::UnsignedDiv32( bit_cast(rm_val), bit_cast(rs_val))); } else { ret_val = base::bits::SignedDiv32(rm_val, rs_val); } set_register(rn, ret_val); return; } } // Format(instr, "'memop'cond'b 'rd, ['rn, -'shift_rm]'w"); addr = rn_val - shifter_operand; if (instr->HasW()) { set_register(rn, addr); } break; } case ib_x: { if (instr->HasW() && (instr->Bits(6, 4) == 0x5)) { uint32_t widthminus1 = static_cast(instr->Bits(20, 16)); uint32_t lsbit = static_cast(instr->Bits(11, 7)); uint32_t msbit = widthminus1 + lsbit; if (msbit <= 31) { if (instr->Bit(22)) { // ubfx - unsigned bitfield extract. uint32_t rm_val = static_cast(get_register(instr->RmValue())); uint32_t extr_val = rm_val << (31 - msbit); extr_val = extr_val >> (31 - widthminus1); set_register(instr->RdValue(), extr_val); } else { // sbfx - signed bitfield extract. int32_t rm_val = get_register(instr->RmValue()); int32_t extr_val = rm_val << (31 - msbit); extr_val = extr_val >> (31 - widthminus1); set_register(instr->RdValue(), extr_val); } } else { UNREACHABLE(); } return; } else if (!instr->HasW() && (instr->Bits(6, 4) == 0x1)) { uint32_t lsbit = static_cast(instr->Bits(11, 7)); uint32_t msbit = static_cast(instr->Bits(20, 16)); if (msbit >= lsbit) { // bfc or bfi - bitfield clear/insert. uint32_t rd_val = static_cast(get_register(instr->RdValue())); uint32_t bitcount = msbit - lsbit + 1; uint32_t mask = 0xFFFFFFFFu >> (32 - bitcount); rd_val &= ~(mask << lsbit); if (instr->RmValue() != 15) { // bfi - bitfield insert. uint32_t rm_val = static_cast(get_register(instr->RmValue())); rm_val &= mask; rd_val |= rm_val << lsbit; } set_register(instr->RdValue(), rd_val); } else { UNREACHABLE(); } return; } else { // Format(instr, "'memop'cond'b 'rd, ['rn, +'shift_rm]'w"); addr = rn_val + shifter_operand; if (instr->HasW()) { set_register(rn, addr); } } break; } default: { UNREACHABLE(); break; } } if (instr->HasB()) { if (instr->HasL()) { uint8_t byte = ReadB(addr); set_register(rd, byte); } else { uint8_t byte = get_register(rd); WriteB(addr, byte); } } else { if (instr->HasL()) { set_register(rd, ReadW(addr)); } else { WriteW(addr, get_register(rd)); } } } void Simulator::DecodeType4(Instruction* instr) { DCHECK_EQ(instr->Bit(22), 0); // only allowed to be set in privileged mode if (instr->HasL()) { // Format(instr, "ldm'cond'pu 'rn'w, 'rlist"); HandleRList(instr, true); } else { // Format(instr, "stm'cond'pu 'rn'w, 'rlist"); HandleRList(instr, false); } } void Simulator::DecodeType5(Instruction* instr) { // Format(instr, "b'l'cond 'target"); int off = (instr->SImmed24Value() << 2); intptr_t pc_address = get_pc(); if (instr->HasLink()) { set_register(lr, pc_address + kInstrSize); } int pc_reg = get_register(pc); set_pc(pc_reg + off); } void Simulator::DecodeType6(Instruction* instr) { DecodeType6CoprocessorIns(instr); } void Simulator::DecodeType7(Instruction* instr) { if (instr->Bit(24) == 1) { SoftwareInterrupt(instr); } else { switch (instr->CoprocessorValue()) { case 10: // Fall through. case 11: DecodeTypeVFP(instr); break; case 15: DecodeTypeCP15(instr); break; default: UNIMPLEMENTED(); } } } // void Simulator::DecodeTypeVFP(Instruction* instr) // The Following ARMv7 VFPv instructions are currently supported. // vmov :Sn = Rt // vmov :Rt = Sn // vcvt: Dd = Sm // vcvt: Sd = Dm // vcvt.f64.s32 Dd, Dd, # // Dd = vabs(Dm) // Sd = vabs(Sm) // Dd = vneg(Dm) // Sd = vneg(Sm) // Dd = vadd(Dn, Dm) // Sd = vadd(Sn, Sm) // Dd = vsub(Dn, Dm) // Sd = vsub(Sn, Sm) // Dd = vmul(Dn, Dm) // Sd = vmul(Sn, Sm) // Dd = vdiv(Dn, Dm) // Sd = vdiv(Sn, Sm) // vcmp(Dd, Dm) // vcmp(Sd, Sm) // Dd = vsqrt(Dm) // Sd = vsqrt(Sm) // vmrs // vdup.size Qd, Rt. void Simulator::DecodeTypeVFP(Instruction* instr) { DCHECK((instr->TypeValue() == 7) && (instr->Bit(24) == 0x0) ); DCHECK_EQ(instr->Bits(11, 9), 0x5); // Obtain single precision register codes. int m = instr->VFPMRegValue(kSinglePrecision); int d = instr->VFPDRegValue(kSinglePrecision); int n = instr->VFPNRegValue(kSinglePrecision); // Obtain double precision register codes. int vm = instr->VFPMRegValue(kDoublePrecision); int vd = instr->VFPDRegValue(kDoublePrecision); int vn = instr->VFPNRegValue(kDoublePrecision); if (instr->Bit(4) == 0) { if (instr->Opc1Value() == 0x7) { // Other data processing instructions if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x1)) { // vmov register to register. if (instr->SzValue() == 0x1) { uint32_t data[2]; get_d_register(vm, data); set_d_register(vd, data); } else { set_s_register(d, get_s_register(m)); } } else if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x3)) { // vabs if (instr->SzValue() == 0x1) { Float64 dm = get_double_from_d_register(vm); constexpr uint64_t kSignBit64 = uint64_t{1} << 63; Float64 dd = Float64::FromBits(dm.get_bits() & ~kSignBit64); dd = canonicalizeNaN(dd); set_d_register_from_double(vd, dd); } else { Float32 sm = get_float_from_s_register(m); constexpr uint32_t kSignBit32 = uint32_t{1} << 31; Float32 sd = Float32::FromBits(sm.get_bits() & ~kSignBit32); sd = canonicalizeNaN(sd); set_s_register_from_float(d, sd); } } else if ((instr->Opc2Value() == 0x1) && (instr->Opc3Value() == 0x1)) { // vneg if (instr->SzValue() == 0x1) { Float64 dm = get_double_from_d_register(vm); constexpr uint64_t kSignBit64 = uint64_t{1} << 63; Float64 dd = Float64::FromBits(dm.get_bits() ^ kSignBit64); dd = canonicalizeNaN(dd); set_d_register_from_double(vd, dd); } else { Float32 sm = get_float_from_s_register(m); constexpr uint32_t kSignBit32 = uint32_t{1} << 31; Float32 sd = Float32::FromBits(sm.get_bits() ^ kSignBit32); sd = canonicalizeNaN(sd); set_s_register_from_float(d, sd); } } else if ((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3)) { DecodeVCVTBetweenDoubleAndSingle(instr); } else if ((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) { DecodeVCVTBetweenFloatingPointAndInteger(instr); } else if ((instr->Opc2Value() == 0xA) && (instr->Opc3Value() == 0x3) && (instr->Bit(8) == 1)) { // vcvt.f64.s32 Dd, Dd, # int fraction_bits = 32 - ((instr->Bits(3, 0) << 1) | instr->Bit(5)); int fixed_value = get_sinteger_from_s_register(vd * 2); double divide = 1 << fraction_bits; set_d_register_from_double(vd, fixed_value / divide); } else if (((instr->Opc2Value() >> 1) == 0x6) && (instr->Opc3Value() & 0x1)) { DecodeVCVTBetweenFloatingPointAndInteger(instr); } else if (((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) && (instr->Opc3Value() & 0x1)) { DecodeVCMP(instr); } else if (((instr->Opc2Value() == 0x1)) && (instr->Opc3Value() == 0x3)) { // vsqrt lazily_initialize_fast_sqrt(isolate_); if (instr->SzValue() == 0x1) { double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = fast_sqrt(dm_value, isolate_); dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = fast_sqrt(sm_value, isolate_); sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else if (instr->Opc3Value() == 0x0) { // vmov immediate. if (instr->SzValue() == 0x1) { set_d_register_from_double(vd, instr->DoubleImmedVmov()); } else { // Cast double to float. float value = instr->DoubleImmedVmov().get_scalar(); set_s_register_from_float(d, value); } } else if (((instr->Opc2Value() == 0x6)) && (instr->Opc3Value() == 0x3)) { // vrintz - truncate if (instr->SzValue() == 0x1) { double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = trunc(dm_value); dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = truncf(sm_value); sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else { UNREACHABLE(); // Not used by V8. } } else if (instr->Opc1Value() == 0x3) { if (instr->Opc3Value() & 0x1) { // vsub if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value - dm_value; dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value - sm_value; sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else { // vadd if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value + dm_value; dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value + sm_value; sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } } else if ((instr->Opc1Value() == 0x2) && !(instr->Opc3Value() & 0x1)) { // vmul if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value * dm_value; dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value * sm_value; sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else if ((instr->Opc1Value() == 0x0)) { // vmla, vmls const bool is_vmls = (instr->Opc3Value() & 0x1); if (instr->SzValue() == 0x1) { const double dd_val = get_double_from_d_register(vd).get_scalar(); const double dn_val = get_double_from_d_register(vn).get_scalar(); const double dm_val = get_double_from_d_register(vm).get_scalar(); // Note: we do the mul and add/sub in separate steps to avoid getting a // result with too high precision. const double res = dn_val * dm_val; set_d_register_from_double(vd, res); if (is_vmls) { set_d_register_from_double(vd, canonicalizeNaN(dd_val - res)); } else { set_d_register_from_double(vd, canonicalizeNaN(dd_val + res)); } } else { const float sd_val = get_float_from_s_register(d).get_scalar(); const float sn_val = get_float_from_s_register(n).get_scalar(); const float sm_val = get_float_from_s_register(m).get_scalar(); // Note: we do the mul and add/sub in separate steps to avoid getting a // result with too high precision. const float res = sn_val * sm_val; set_s_register_from_float(d, res); if (is_vmls) { set_s_register_from_float(d, canonicalizeNaN(sd_val - res)); } else { set_s_register_from_float(d, canonicalizeNaN(sd_val + res)); } } } else if ((instr->Opc1Value() == 0x4) && !(instr->Opc3Value() & 0x1)) { // vdiv if (instr->SzValue() == 0x1) { double dn_value = get_double_from_d_register(vn).get_scalar(); double dm_value = get_double_from_d_register(vm).get_scalar(); double dd_value = dn_value / dm_value; div_zero_vfp_flag_ = (dm_value == 0); dd_value = canonicalizeNaN(dd_value); set_d_register_from_double(vd, dd_value); } else { float sn_value = get_float_from_s_register(n).get_scalar(); float sm_value = get_float_from_s_register(m).get_scalar(); float sd_value = sn_value / sm_value; div_zero_vfp_flag_ = (sm_value == 0); sd_value = canonicalizeNaN(sd_value); set_s_register_from_float(d, sd_value); } } else { UNIMPLEMENTED(); // Not used by V8. } } else { if ((instr->VCValue() == 0x0) && (instr->VAValue() == 0x0)) { DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(instr); } else if ((instr->VLValue() == 0x0) && (instr->VCValue() == 0x1)) { if (instr->Bit(23) == 0) { // vmov (ARM core register to scalar) int vd = instr->VFPNRegValue(kDoublePrecision); int rt = instr->RtValue(); int opc1_opc2 = (instr->Bits(22, 21) << 2) | instr->Bits(6, 5); if ((opc1_opc2 & 0xB) == 0) { // NeonS32/NeonU32 uint32_t data[2]; get_d_register(vd, data); data[instr->Bit(21)] = get_register(rt); set_d_register(vd, data); } else { uint64_t data; get_d_register(vd, &data); uint64_t rt_value = get_register(rt); if ((opc1_opc2 & 0x8) != 0) { // NeonS8 / NeonU8 int i = opc1_opc2 & 0x7; int shift = i * kBitsPerByte; const uint64_t mask = 0xFF; data &= ~(mask << shift); data |= (rt_value & mask) << shift; set_d_register(vd, &data); } else if ((opc1_opc2 & 0x1) != 0) { // NeonS16 / NeonU16 int i = (opc1_opc2 >> 1) & 0x3; int shift = i * kBitsPerByte * kShortSize; const uint64_t mask = 0xFFFF; data &= ~(mask << shift); data |= (rt_value & mask) << shift; set_d_register(vd, &data); } else { UNREACHABLE(); // Not used by V8. } } } else { // vdup.size Qd, Rt. NeonSize size = Neon32; if (instr->Bit(5) != 0) size = Neon16; else if (instr->Bit(22) != 0) size = Neon8; int vd = instr->VFPNRegValue(kSimd128Precision); int rt = instr->RtValue(); uint32_t rt_value = get_register(rt); uint32_t q_data[4]; switch (size) { case Neon8: { rt_value &= 0xFF; uint8_t* dst = reinterpret_cast(q_data); for (int i = 0; i < 16; i++) { dst[i] = rt_value; } break; } case Neon16: { // Perform pairwise op. rt_value &= 0xFFFFu; uint32_t rt_rt = (rt_value << 16) | (rt_value & 0xFFFFu); for (int i = 0; i < 4; i++) { q_data[i] = rt_rt; } break; } case Neon32: { for (int i = 0; i < 4; i++) { q_data[i] = rt_value; } break; } default: UNREACHABLE(); break; } set_neon_register(vd, q_data); } } else if ((instr->VLValue() == 0x1) && (instr->VCValue() == 0x1)) { // vmov (scalar to ARM core register) int vn = instr->VFPNRegValue(kDoublePrecision); int rt = instr->RtValue(); int opc1_opc2 = (instr->Bits(22, 21) << 2) | instr->Bits(6, 5); uint64_t data; get_d_register(vn, &data); if ((opc1_opc2 & 0xB) == 0) { // NeonS32 / NeonU32 int32_t int_data[2]; memcpy(int_data, &data, sizeof(int_data)); set_register(rt, int_data[instr->Bit(21)]); } else { uint64_t data; get_d_register(vn, &data); bool u = instr->Bit(23) != 0; if ((opc1_opc2 & 0x8) != 0) { // NeonS8 / NeonU8 int i = opc1_opc2 & 0x7; int shift = i * kBitsPerByte; uint32_t scalar = (data >> shift) & 0xFFu; if (!u && (scalar & 0x80) != 0) scalar |= 0xFFFFFF00; set_register(rt, scalar); } else if ((opc1_opc2 & 0x1) != 0) { // NeonS16 / NeonU16 int i = (opc1_opc2 >> 1) & 0x3; int shift = i * kBitsPerByte * kShortSize; uint32_t scalar = (data >> shift) & 0xFFFFu; if (!u && (scalar & 0x8000) != 0) scalar |= 0xFFFF0000; set_register(rt, scalar); } else { UNREACHABLE(); // Not used by V8. } } } else if ((instr->VLValue() == 0x1) && (instr->VCValue() == 0x0) && (instr->VAValue() == 0x7) && (instr->Bits(19, 16) == 0x1)) { // vmrs uint32_t rt = instr->RtValue(); if (rt == 0xF) { Copy_FPSCR_to_APSR(); } else { // Emulate FPSCR from the Simulator flags. uint32_t fpscr = (n_flag_FPSCR_ << 31) | (z_flag_FPSCR_ << 30) | (c_flag_FPSCR_ << 29) | (v_flag_FPSCR_ << 28) | (FPSCR_default_NaN_mode_ << 25) | (inexact_vfp_flag_ << 4) | (underflow_vfp_flag_ << 3) | (overflow_vfp_flag_ << 2) | (div_zero_vfp_flag_ << 1) | (inv_op_vfp_flag_ << 0) | (FPSCR_rounding_mode_); set_register(rt, fpscr); } } else if ((instr->VLValue() == 0x0) && (instr->VCValue() == 0x0) && (instr->VAValue() == 0x7) && (instr->Bits(19, 16) == 0x1)) { // vmsr uint32_t rt = instr->RtValue(); if (rt == pc) { UNREACHABLE(); } else { uint32_t rt_value = get_register(rt); n_flag_FPSCR_ = (rt_value >> 31) & 1; z_flag_FPSCR_ = (rt_value >> 30) & 1; c_flag_FPSCR_ = (rt_value >> 29) & 1; v_flag_FPSCR_ = (rt_value >> 28) & 1; FPSCR_default_NaN_mode_ = (rt_value >> 25) & 1; inexact_vfp_flag_ = (rt_value >> 4) & 1; underflow_vfp_flag_ = (rt_value >> 3) & 1; overflow_vfp_flag_ = (rt_value >> 2) & 1; div_zero_vfp_flag_ = (rt_value >> 1) & 1; inv_op_vfp_flag_ = (rt_value >> 0) & 1; FPSCR_rounding_mode_ = static_cast((rt_value) & kVFPRoundingModeMask); } } else { UNIMPLEMENTED(); // Not used by V8. } } } void Simulator::DecodeTypeCP15(Instruction* instr) { DCHECK((instr->TypeValue() == 7) && (instr->Bit(24) == 0x0)); DCHECK_EQ(instr->CoprocessorValue(), 15); if (instr->Bit(4) == 1) { // mcr int crn = instr->Bits(19, 16); int crm = instr->Bits(3, 0); int opc1 = instr->Bits(23, 21); int opc2 = instr->Bits(7, 5); if ((opc1 == 0) && (crn == 7)) { // ARMv6 memory barrier operations. // Details available in ARM DDI 0406C.b, B3-1750. if (((crm == 10) && (opc2 == 5)) || // CP15DMB ((crm == 10) && (opc2 == 4)) || // CP15DSB ((crm == 5) && (opc2 == 4))) { // CP15ISB // These are ignored by the simulator for now. } else { UNIMPLEMENTED(); } } } else { UNIMPLEMENTED(); } } void Simulator::DecodeVMOVBetweenCoreAndSinglePrecisionRegisters( Instruction* instr) { DCHECK((instr->Bit(4) == 1) && (instr->VCValue() == 0x0) && (instr->VAValue() == 0x0)); int t = instr->RtValue(); int n = instr->VFPNRegValue(kSinglePrecision); bool to_arm_register = (instr->VLValue() == 0x1); if (to_arm_register) { int32_t int_value = get_sinteger_from_s_register(n); set_register(t, int_value); } else { int32_t rs_val = get_register(t); set_s_register_from_sinteger(n, rs_val); } } void Simulator::DecodeVCMP(Instruction* instr) { DCHECK((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7)); DCHECK(((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) && (instr->Opc3Value() & 0x1)); // Comparison. VFPRegPrecision precision = kSinglePrecision; if (instr->SzValue() == 0x1) { precision = kDoublePrecision; } int d = instr->VFPDRegValue(precision); int m = 0; if (instr->Opc2Value() == 0x4) { m = instr->VFPMRegValue(precision); } if (precision == kDoublePrecision) { double dd_value = get_double_from_d_register(d).get_scalar(); double dm_value = 0.0; if (instr->Opc2Value() == 0x4) { dm_value = get_double_from_d_register(m).get_scalar(); } // Raise exceptions for quiet NaNs if necessary. if (instr->Bit(7) == 1) { if (std::isnan(dd_value)) { inv_op_vfp_flag_ = true; } } Compute_FPSCR_Flags(dd_value, dm_value); } else { float sd_value = get_float_from_s_register(d).get_scalar(); float sm_value = 0.0; if (instr->Opc2Value() == 0x4) { sm_value = get_float_from_s_register(m).get_scalar(); } // Raise exceptions for quiet NaNs if necessary. if (instr->Bit(7) == 1) { if (std::isnan(sd_value)) { inv_op_vfp_flag_ = true; } } Compute_FPSCR_Flags(sd_value, sm_value); } } void Simulator::DecodeVCVTBetweenDoubleAndSingle(Instruction* instr) { DCHECK((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7)); DCHECK((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3)); VFPRegPrecision dst_precision = kDoublePrecision; VFPRegPrecision src_precision = kSinglePrecision; if (instr->SzValue() == 1) { dst_precision = kSinglePrecision; src_precision = kDoublePrecision; } int dst = instr->VFPDRegValue(dst_precision); int src = instr->VFPMRegValue(src_precision); if (dst_precision == kSinglePrecision) { double val = get_double_from_d_register(src).get_scalar(); set_s_register_from_float(dst, static_cast(val)); } else { float val = get_float_from_s_register(src).get_scalar(); set_d_register_from_double(dst, static_cast(val)); } } bool get_inv_op_vfp_flag(VFPRoundingMode mode, double val, bool unsigned_) { DCHECK((mode == RN) || (mode == RM) || (mode == RZ)); double max_uint = static_cast(0xFFFFFFFFu); double max_int = static_cast(kMaxInt); double min_int = static_cast(kMinInt); // Check for NaN. if (val != val) { return true; } // Check for overflow. This code works because 32bit integers can be // exactly represented by ieee-754 64bit floating-point values. switch (mode) { case RN: return unsigned_ ? (val >= (max_uint + 0.5)) || (val < -0.5) : (val >= (max_int + 0.5)) || (val < (min_int - 0.5)); case RM: return unsigned_ ? (val >= (max_uint + 1.0)) || (val < 0) : (val >= (max_int + 1.0)) || (val < min_int); case RZ: return unsigned_ ? (val >= (max_uint + 1.0)) || (val <= -1) : (val >= (max_int + 1.0)) || (val <= (min_int - 1.0)); default: UNREACHABLE(); } } // We call this function only if we had a vfp invalid exception. // It returns the correct saturated value. int VFPConversionSaturate(double val, bool unsigned_res) { if (val != val) { return 0; } else { if (unsigned_res) { return (val < 0) ? 0 : 0xFFFFFFFFu; } else { return (val < 0) ? kMinInt : kMaxInt; } } } int32_t Simulator::ConvertDoubleToInt(double val, bool unsigned_integer, VFPRoundingMode mode) { // TODO(jkummerow): These casts are undefined behavior if the integral // part of {val} does not fit into the destination type. int32_t result = unsigned_integer ? static_cast(val) : static_cast(val); inv_op_vfp_flag_ = get_inv_op_vfp_flag(mode, val, unsigned_integer); double abs_diff = unsigned_integer ? std::fabs(val - static_cast(result)) : std::fabs(val - result); inexact_vfp_flag_ = (abs_diff != 0); if (inv_op_vfp_flag_) { result = VFPConversionSaturate(val, unsigned_integer); } else { switch (mode) { case RN: { int val_sign = (val > 0) ? 1 : -1; if (abs_diff > 0.5) { result += val_sign; } else if (abs_diff == 0.5) { // Round to even if exactly halfway. result = ((result % 2) == 0) ? result : result + val_sign; } break; } case RM: result = result > val ? result - 1 : result; break; case RZ: // Nothing to do. break; default: UNREACHABLE(); } } return result; } void Simulator::DecodeVCVTBetweenFloatingPointAndInteger(Instruction* instr) { DCHECK((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7) && (instr->Bits(27, 23) == 0x1D)); DCHECK(((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) || (((instr->Opc2Value() >> 1) == 0x6) && (instr->Opc3Value() & 0x1))); // Conversion between floating-point and integer. bool to_integer = (instr->Bit(18) == 1); VFPRegPrecision src_precision = (instr->SzValue() == 1) ? kDoublePrecision : kSinglePrecision; if (to_integer) { // We are playing with code close to the C++ standard's limits below, // hence the very simple code and heavy checks. // // Note: // C++ defines default type casting from floating point to integer as // (close to) rounding toward zero ("fractional part discarded"). int dst = instr->VFPDRegValue(kSinglePrecision); int src = instr->VFPMRegValue(src_precision); // Bit 7 in vcvt instructions indicates if we should use the FPSCR rounding // mode or the default Round to Zero mode. VFPRoundingMode mode = (instr->Bit(7) != 1) ? FPSCR_rounding_mode_ : RZ; DCHECK((mode == RM) || (mode == RZ) || (mode == RN)); bool unsigned_integer = (instr->Bit(16) == 0); bool double_precision = (src_precision == kDoublePrecision); double val = double_precision ? get_double_from_d_register(src).get_scalar() : get_float_from_s_register(src).get_scalar(); int32_t temp = ConvertDoubleToInt(val, unsigned_integer, mode); // Update the destination register. set_s_register_from_sinteger(dst, temp); } else { bool unsigned_integer = (instr->Bit(7) == 0); int dst = instr->VFPDRegValue(src_precision); int src = instr->VFPMRegValue(kSinglePrecision); int val = get_sinteger_from_s_register(src); if (src_precision == kDoublePrecision) { if (unsigned_integer) { set_d_register_from_double( dst, static_cast(static_cast(val))); } else { set_d_register_from_double(dst, static_cast(val)); } } else { if (unsigned_integer) { set_s_register_from_float( dst, static_cast(static_cast(val))); } else { set_s_register_from_float(dst, static_cast(val)); } } } } // void Simulator::DecodeType6CoprocessorIns(Instruction* instr) // Decode Type 6 coprocessor instructions. // Dm = vmov(Rt, Rt2) // = vmov(Dm) // Ddst = MEM(Rbase + 4*offset). // MEM(Rbase + 4*offset) = Dsrc. void Simulator::DecodeType6CoprocessorIns(Instruction* instr) { DCHECK_EQ(instr->TypeValue(), 6); if (instr->CoprocessorValue() == 0xA) { switch (instr->OpcodeValue()) { case 0x8: case 0xA: case 0xC: case 0xE: { // Load and store single precision float to memory. int rn = instr->RnValue(); int vd = instr->VFPDRegValue(kSinglePrecision); int offset = instr->Immed8Value(); if (!instr->HasU()) { offset = -offset; } int32_t address = get_register(rn) + 4 * offset; // Load and store address for singles must be at least four-byte // aligned. DCHECK_EQ(address % 4, 0); if (instr->HasL()) { // Load single from memory: vldr. set_s_register_from_sinteger(vd, ReadW(address)); } else { // Store single to memory: vstr. WriteW(address, get_sinteger_from_s_register(vd)); } break; } case 0x4: case 0x5: case 0x6: case 0x7: case 0x9: case 0xB: // Load/store multiple single from memory: vldm/vstm. HandleVList(instr); break; default: UNIMPLEMENTED(); // Not used by V8. } } else if (instr->CoprocessorValue() == 0xB) { switch (instr->OpcodeValue()) { case 0x2: // Load and store double to two GP registers if (instr->Bits(7, 6) != 0 || instr->Bit(4) != 1) { UNIMPLEMENTED(); // Not used by V8. } else { int rt = instr->RtValue(); int rn = instr->RnValue(); int vm = instr->VFPMRegValue(kDoublePrecision); if (instr->HasL()) { uint32_t data[2]; get_d_register(vm, data); set_register(rt, data[0]); set_register(rn, data[1]); } else { int32_t data[] = { get_register(rt), get_register(rn) }; set_d_register(vm, reinterpret_cast(data)); } } break; case 0x8: case 0xA: case 0xC: case 0xE: { // Load and store double to memory. int rn = instr->RnValue(); int vd = instr->VFPDRegValue(kDoublePrecision); int offset = instr->Immed8Value(); if (!instr->HasU()) { offset = -offset; } int32_t address = get_register(rn) + 4 * offset; // Load and store address for doubles must be at least four-byte // aligned. DCHECK_EQ(address % 4, 0); if (instr->HasL()) { // Load double from memory: vldr. int32_t data[] = {ReadW(address), ReadW(address + 4)}; set_d_register(vd, reinterpret_cast(data)); } else { // Store double to memory: vstr. uint32_t data[2]; get_d_register(vd, data); WriteW(address, data[0]); WriteW(address + 4, data[1]); } break; } case 0x4: case 0x5: case 0x6: case 0x7: case 0x9: case 0xB: // Load/store multiple double from memory: vldm/vstm. HandleVList(instr); break; default: UNIMPLEMENTED(); // Not used by V8. } } else { UNIMPLEMENTED(); // Not used by V8. } } // Templated operations for NEON instructions. template U Widen(T value) { static_assert(sizeof(int64_t) > sizeof(T), "T must be int32_t or smaller"); static_assert(sizeof(U) > sizeof(T), "T must smaller than U"); return static_cast(value); } template U Narrow(T value) { static_assert(sizeof(int8_t) < sizeof(T), "T must be int16_t or larger"); static_assert(sizeof(U) < sizeof(T), "T must larger than U"); static_assert(std::is_unsigned() == std::is_unsigned(), "Signed-ness of T and U must match"); // Make sure value can be expressed in the smaller type; otherwise, the // casted result is implementation defined. DCHECK_LE(std::numeric_limits::min(), value); DCHECK_GE(std::numeric_limits::max(), value); return static_cast(value); } template T Clamp(int64_t value) { static_assert(sizeof(int64_t) > sizeof(T), "T must be int32_t or smaller"); int64_t min = static_cast(std::numeric_limits::min()); int64_t max = static_cast(std::numeric_limits::max()); int64_t clamped = std::max(min, std::min(max, value)); return static_cast(clamped); } template void Widen(Simulator* simulator, int Vd, int Vm) { static const int kLanes = 8 / sizeof(T); T src[kLanes]; U dst[kLanes]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kLanes; i++) { dst[i] = Widen(src[i]); } simulator->set_neon_register(Vd, dst); } template void Abs(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); T src[kElems]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kElems; i++) { src[i] = std::abs(src[i]); } simulator->set_neon_register(Vd, src); } template void Neg(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); T src[kElems]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kElems; i++) { src[i] = -src[i]; } simulator->set_neon_register(Vd, src); } template void SaturatingNarrow(Simulator* simulator, int Vd, int Vm) { static const int kLanes = 16 / sizeof(T); T src[kLanes]; U dst[kLanes]; simulator->get_neon_register(Vm, src); for (int i = 0; i < kLanes; i++) { dst[i] = Narrow(Clamp(src[i])); } simulator->set_neon_register(Vd, dst); } template void AddSaturate(Simulator* simulator, int Vd, int Vm, int Vn) { static const int kLanes = 16 / sizeof(T); T src1[kLanes], src2[kLanes]; simulator->get_neon_register(Vn, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kLanes; i++) { src1[i] = Clamp(Widen(src1[i]) + Widen(src2[i])); } simulator->set_neon_register(Vd, src1); } template void SubSaturate(Simulator* simulator, int Vd, int Vm, int Vn) { static const int kLanes = 16 / sizeof(T); T src1[kLanes], src2[kLanes]; simulator->get_neon_register(Vn, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kLanes; i++) { src1[i] = Clamp(Widen(src1[i]) - Widen(src2[i])); } simulator->set_neon_register(Vd, src1); } template void Zip(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); static const int kPairs = kElems / 2; T src1[kElems], src2[kElems], dst1[kElems], dst2[kElems]; simulator->get_neon_register(Vd, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kPairs; i++) { dst1[i * 2] = src1[i]; dst1[i * 2 + 1] = src2[i]; dst2[i * 2] = src1[i + kPairs]; dst2[i * 2 + 1] = src2[i + kPairs]; } simulator->set_neon_register(Vd, dst1); simulator->set_neon_register(Vm, dst2); } template void Unzip(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); static const int kPairs = kElems / 2; T src1[kElems], src2[kElems], dst1[kElems], dst2[kElems]; simulator->get_neon_register(Vd, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kPairs; i++) { dst1[i] = src1[i * 2]; dst1[i + kPairs] = src2[i * 2]; dst2[i] = src1[i * 2 + 1]; dst2[i + kPairs] = src2[i * 2 + 1]; } simulator->set_neon_register(Vd, dst1); simulator->set_neon_register(Vm, dst2); } template void Transpose(Simulator* simulator, int Vd, int Vm) { static const int kElems = SIZE / sizeof(T); static const int kPairs = kElems / 2; T src1[kElems], src2[kElems]; simulator->get_neon_register(Vd, src1); simulator->get_neon_register(Vm, src2); for (int i = 0; i < kPairs; i++) { std::swap(src1[2 * i + 1], src2[2 * i]); } simulator->set_neon_register(Vd, src1); simulator->set_neon_register