#ifndef V8_EXECUTION_MIPS64_SIMULATOR_MIPS64_H_
#define V8_EXECUTION_MIPS64_SIMULATOR_MIPS64_H_
#include "src/common/globals.h"
template <typename T>
int Compare(const T& a, const T& b) {
if (a == b)
return 0;
else if (a < b)
return -1;
else
return 1;
}
template <typename T, typename = typename std::enable_if_t<std::is_signed_v<T>>>
T Nabs(T a) {
return a < 0 ? a : -a;
}
#if defined(USE_SIMULATOR)
#include "src/base/hashmap.h"
#include "src/base/strings.h"
#include "src/codegen/assembler.h"
#include "src/codegen/mips64/constants-mips64.h"
#include "src/execution/simulator-base.h"
#include "src/utils/allocation.h"
namespace v8 {
namespace internal {
class CachePage {
public:
static const int LINE_VALID = 0;
static const int LINE_INVALID = 1;
static const int kPageShift = 12;
static const int kPageSize = 1 << kPageShift;
static const int kPageMask = kPageSize - 1;
static const int kLineShift = 2;
static const int kLineLength = 1 << kLineShift;
static const int kLineMask = kLineLength - 1;
CachePage() { memset(&validity_map_, LINE_INVALID, sizeof(validity_map_)); }
char* ValidityByte(int offset) {
return &validity_map_[offset >> kLineShift];
}
char* CachedData(int offset) { return &data_[offset]; }
private:
char data_[kPageSize];
static const int kValidityMapSize = kPageSize >> kLineShift;
char validity_map_[kValidityMapSize];
};
class SimInstructionBase : public InstructionBase {
public:
Type InstructionType() const { return type_; }
inline Instruction* instr() const { return instr_; }
inline int32_t operand() const { return operand_; }
protected:
SimInstructionBase() : operand_(-1), instr_(nullptr), type_(kUnsupported) {}
explicit SimInstructionBase(Instruction* instr) {}
int32_t operand_;
Instruction* instr_;
Type type_;
private:
DISALLOW_ASSIGN(SimInstructionBase);
};
class SimInstruction : public InstructionGetters<SimInstructionBase> {
public:
SimInstruction() {}
explicit SimInstruction(Instruction* instr) { *this = instr; }
SimInstruction& operator=(Instruction* instr) {
operand_ = *reinterpret_cast<const int32_t*>(instr);
instr_ = instr;
type_ = InstructionBase::InstructionType();
DCHECK(reinterpret_cast<void*>(&operand_) == this);
return *this;
}
};
class Simulator : public SimulatorBase {
public:
friend class MipsDebugger;
enum Register {
no_reg = -1,
zero_reg = 0,
at,
v0,
v1,
a0,
a1,
a2,
a3,
a4,
a5,
a6,
a7,
t0,
t1,
t2,
t3,
s0,
s1,
s2,
s3,
s4,
s5,
s6,
s7,
t8,
t9,
k0,
k1,
gp,
sp,
s8,
ra,
LO,
HI,
pc,
kNumSimuRegisters,
fp = s8
};
enum FPURegister {
f0,
f1,
f2,
f3,
f4,
f5,
f6,
f7,
f8,
f9,
f10,
f11,
f12,
f13,
f14,
f15,
f16,
f17,
f18,
f19,
f20,
f21,
f22,
f23,
f24,
f25,
f26,
f27,
f28,
f29,
f30,
f31,
kNumFPURegisters
};
enum MSARegister {
w0,
w1,
w2,
w3,
w4,
w5,
w6,
w7,
w8,
w9,
w10,
w11,
w12,
w13,
w14,
w15,
w16,
w17,
w18,
w19,
w20,
w21,
w22,
w23,
w24,
w25,
w26,
w27,
w28,
w29,
w30,
w31,
kNumMSARegisters
};
explicit Simulator(Isolate* isolate);
~Simulator();
V8_EXPORT_PRIVATE static Simulator* current(v8::internal::Isolate* isolate);
void set_register(int reg, int64_t value);
void set_register_word(int reg, int32_t value);
void set_dw_register(int dreg, const int* dbl);
V8_EXPORT_PRIVATE int64_t get_register(int reg) const;
double get_double_from_register_pair(int reg);
void set_fpu_register(int fpureg, int64_t value);
void set_fpu_register_word(int fpureg, int32_t value);
void set_fpu_register_hi_word(int fpureg, int32_t value);
void set_fpu_register_float(int fpureg, float value);
void set_fpu_register_double(int fpureg, double value);
void set_fpu_register_invalid_result64(float original, float rounded);
void set_fpu_register_invalid_result(float original, float rounded);
void set_fpu_register_word_invalid_result(float original, float rounded);
void set_fpu_register_invalid_result64(double original, double rounded);
void set_fpu_register_invalid_result(double original, double rounded);
void set_fpu_register_word_invalid_result(double original, double rounded);
int64_t get_fpu_register(int fpureg) const;
int32_t get_fpu_register_word(int fpureg) const;
int32_t get_fpu_register_signed_word(int fpureg) const;
int32_t get_fpu_register_hi_word(int fpureg) const;
float get_fpu_register_float(int fpureg) const;
double get_fpu_register_double(int fpureg) const;
template <typename T>
void get_msa_register(int wreg, T* value);
template <typename T>
void set_msa_register(int wreg, const T* value);
void set_fcsr_bit(uint32_t cc, bool value);
bool test_fcsr_bit(uint32_t cc);
bool set_fcsr_round_error(double original, double rounded);
bool set_fcsr_round64_error(double original, double rounded);
bool set_fcsr_round_error(float original, float rounded);
bool set_fcsr_round64_error(float original, float rounded);
void round_according_to_fcsr(double toRound, double* rounded,
int32_t* rounded_int, double fs);
void round64_according_to_fcsr(double toRound, double* rounded,
int64_t* rounded_int, double fs);
void round_according_to_fcsr(float toRound, float* rounded,
int32_t* rounded_int, float fs);
void round64_according_to_fcsr(float toRound, float* rounded,
int64_t* rounded_int, float fs);
template <typename T_fp, typename T_int>
void round_according_to_msacsr(T_fp toRound, T_fp* rounded,
T_int* rounded_int);
void clear_fcsr_cause();
void set_fcsr_rounding_mode(FPURoundingMode mode);
void set_msacsr_rounding_mode(FPURoundingMode mode);
unsigned int get_fcsr_rounding_mode();
unsigned int get_msacsr_rounding_mode();
void set_pc(int64_t value);
V8_EXPORT_PRIVATE int64_t get_pc() const;
Address get_sp() const { return static_cast<Address>(get_register(sp)); }
uintptr_t StackLimit(uintptr_t c_limit) const;
uintptr_t StackBase() const;
base::Vector<uint8_t> GetCentralStackView() const;
static constexpr int JSStackLimitMargin() { return kAdditionalStackMargin; }
void IterateRegistersAndStack(::heap::base::StackVisitor* visitor);
void Execute();
class CallArgument {
public:
template <typename T>
explicit CallArgument(T argument) {
bits_ = 0;
DCHECK(sizeof(argument) <= sizeof(bits_));
bits_ = ConvertArg(argument);
type_ = GP_ARG;
}
explicit CallArgument(double argument) {
DCHECK(sizeof(argument) == sizeof(bits_));
memcpy(&bits_, &argument, sizeof(argument));
type_ = FP_ARG;
}
explicit CallArgument(float argument) {
double sNaN = std::numeric_limits<double>::signaling_NaN();
DCHECK(sizeof(sNaN) == sizeof(bits_));
memcpy(&bits_, &sNaN, sizeof(sNaN));
DCHECK(sizeof(argument) <= sizeof(bits_));
memcpy(&bits_, &argument, sizeof(argument));
type_ = FP_ARG;
}
static CallArgument End() { return CallArgument(); }
int64_t bits() const { return bits_; }
bool IsEnd() const { return type_ == NO_ARG; }
bool IsGP() const { return type_ == GP_ARG; }
bool IsFP() const { return type_ == FP_ARG; }
private:
enum CallArgumentType { GP_ARG, FP_ARG, NO_ARG };
int64_t bits_;
CallArgumentType type_;
CallArgument() { type_ = NO_ARG; }
};
template <typename Return, typename... Args>
Return Call(Address entry, Args... args) {
CallArgument call_args[] = {CallArgument(args)..., CallArgument::End()};
CallImpl(entry, call_args);
return ReadReturn<Return>();
}
double CallFP(Address entry, double d0, double d1);
V8_EXPORT_PRIVATE uintptr_t PushAddress(uintptr_t address);
V8_EXPORT_PRIVATE uintptr_t PopAddress();
void set_last_debugger_input(char* input);
char* last_debugger_input() { return last_debugger_input_; }
static void SetRedirectInstruction(Instruction* instruction);
static bool ICacheMatch(void* one, void* two);
static void FlushICache(base::CustomMatcherHashMap* i_cache, void* start,
size_t size);
bool has_bad_pc() const;
private:
enum special_values {
bad_ra = -1,
end_sim_pc = -2,
Unpredictable = 0xbadbeaf
};
V8_EXPORT_PRIVATE void CallImpl(Address entry, CallArgument* args);
void CallAnyCTypeFunction(Address target_address,
const EncodedCSignature& signature);
template <typename T>
typename std::enable_if_t<std::is_floating_point_v<T>, T> ReadReturn() {
return static_cast<T>(get_fpu_register_double(f0));
}
template <typename T>
typename std::enable_if_t<!std::is_floating_point_v<T>, T> ReadReturn() {
return ConvertReturn<T>(get_register(v0));
}
void Format(Instruction* instr, const char* format);
enum TraceType {
BYTE,
HALF,
WORD,
DWORD,
FLOAT,
DOUBLE,
FLOAT_DOUBLE,
WORD_DWORD
};
enum MSADataFormat { MSA_VECT = 0, MSA_BYTE, MSA_HALF, MSA_WORD, MSA_DWORD };
union msa_reg_t {
int8_t b[kMSALanesByte];
uint8_t ub[kMSALanesByte];
int16_t h[kMSALanesHalf];
uint16_t uh[kMSALanesHalf];
int32_t w[kMSALanesWord];
uint32_t uw[kMSALanesWord];
int64_t d[kMSALanesDword];
uint64_t ud[kMSALanesDword];
};
inline uint32_t ReadBU(int64_t addr);
inline int32_t ReadB(int64_t addr);
inline void WriteB(int64_t addr, uint8_t value);
inline void WriteB(int64_t addr, int8_t value);
inline uint16_t ReadHU(int64_t addr, Instruction* instr);
inline int16_t ReadH(int64_t addr, Instruction* instr);
inline void WriteH(int64_t addr, uint16_t value, Instruction* instr);
inline void WriteH(int64_t addr, int16_t value, Instruction* instr);
inline uint32_t ReadWU(int64_t addr, Instruction* instr);
inline int32_t ReadW(int64_t addr, Instruction* instr, TraceType t = WORD);
inline void WriteW(int64_t addr, int32_t value, Instruction* instr);
void WriteConditionalW(int64_t addr, int32_t value, Instruction* instr,
int32_t rt_reg);
inline int64_t Read2W(int64_t addr, Instruction* instr);
inline void Write2W(int64_t addr, int64_t value, Instruction* instr);
inline void WriteConditional2W(int64_t addr, int64_t value,
Instruction* instr, int32_t rt_reg);
inline double ReadD(int64_t addr, Instruction* instr);
inline void WriteD(int64_t addr, double value, Instruction* instr);
template <typename T>
T ReadMem(int64_t addr, Instruction* instr);
template <typename T>
void WriteMem(int64_t addr, T value, Instruction* instr);
inline void DieOrDebug();
void TraceRegWr(int64_t value, TraceType t = DWORD);
template <typename T>
void TraceMSARegWr(T* value, TraceType t);
template <typename T>
void TraceMSARegWr(T* value);
void TraceMemWr(int64_t addr, int64_t value, TraceType t);
void TraceMemRd(int64_t addr, int64_t value, TraceType t = DWORD);
template <typename T>
void TraceMemRd(int64_t addr, T value);
template <typename T>
void TraceMemWr(int64_t addr, T value);
inline int32_t GetDoubleHIW(double* addr);
inline int32_t GetDoubleLOW(double* addr);
inline int32_t SetDoubleHIW(double* addr);
inline int32_t SetDoubleLOW(double* addr);
SimInstruction instr_;
void DecodeTypeRegisterCOP1();
void DecodeTypeRegisterCOP1X();
void DecodeTypeRegisterSPECIAL();
void DecodeTypeRegisterSPECIAL2();
void DecodeTypeRegisterSPECIAL3();
void DecodeTypeRegisterSRsType();
void DecodeTypeRegisterDRsType();
void DecodeTypeRegisterWRsType();
void DecodeTypeRegisterLRsType();
int DecodeMsaDataFormat();
void DecodeTypeMsaI8();
void DecodeTypeMsaI5();
void DecodeTypeMsaI10();
void DecodeTypeMsaELM();
void DecodeTypeMsaBIT();
void DecodeTypeMsaMI10();
void DecodeTypeMsa3R();
void DecodeTypeMsa3RF();
void DecodeTypeMsaVec();
void DecodeTypeMsa2R();
void DecodeTypeMsa2RF();
template <typename T>
T MsaI5InstrHelper(uint32_t opcode, T ws, int32_t i5);
template <typename T>
T MsaBitInstrHelper(uint32_t opcode, T wd, T ws, int32_t m);
template <typename T>
T Msa3RInstrHelper(uint32_t opcode, T wd, T ws, T wt);
void DecodeTypeRegister();
inline int32_t rs_reg() const { return instr_.RsValue(); }
inline int64_t rs() const { return get_register(rs_reg()); }
inline uint64_t rs_u() const {
return static_cast<uint64_t>(get_register(rs_reg()));
}
inline int32_t rt_reg() const { return instr_.RtValue(); }
inline int64_t rt() const { return get_register(rt_reg()); }
inline uint64_t rt_u() const {
return static_cast<uint64_t>(get_register(rt_reg()));
}
inline int32_t rd_reg() const { return instr_.RdValue(); }
inline int32_t fr_reg() const { return instr_.FrValue(); }
inline int32_t fs_reg() const { return instr_.FsValue(); }
inline int32_t ft_reg() const { return instr_.FtValue(); }
inline int32_t fd_reg() const { return instr_.FdValue(); }
inline int32_t sa() const { return instr_.SaValue(); }
inline int32_t lsa_sa() const { return instr_.LsaSaValue(); }
inline int32_t ws_reg() const { return instr_.WsValue(); }
inline int32_t wt_reg() const { return instr_.WtValue(); }
inline int32_t wd_reg() const { return instr_.WdValue(); }
inline void SetResult(const int32_t rd_reg, const int64_t alu_out) {
set_register(rd_reg, alu_out);
TraceRegWr(alu_out);
}
inline void SetFPUWordResult(int32_t fd_reg, int32_t alu_out) {
set_fpu_register_word(fd_reg, alu_out);
TraceRegWr(get_fpu_register(fd_reg), WORD);
}
inline void SetFPUWordResult2(int32_t fd_reg, int32_t alu_out) {
set_fpu_register_word(fd_reg, alu_out);
TraceRegWr(get_fpu_register(fd_reg));
}
inline void SetFPUResult(int32_t fd_reg, int64_t alu_out) {
set_fpu_register(fd_reg, alu_out);
TraceRegWr(get_fpu_register(fd_reg));
}
inline void SetFPUResult2(int32_t fd_reg, int64_t alu_out) {
set_fpu_register(fd_reg, alu_out);
TraceRegWr(get_fpu_register(fd_reg), DOUBLE);
}
inline void SetFPUFloatResult(int32_t fd_reg, float alu_out) {
set_fpu_register_float(fd_reg, alu_out);
TraceRegWr(get_fpu_register(fd_reg), FLOAT);
}
inline void SetFPUDoubleResult(int32_t fd_reg, double alu_out) {
set_fpu_register_double(fd_reg, alu_out);
TraceRegWr(get_fpu_register(fd_reg), DOUBLE);
}
void DecodeTypeImmediate();
void DecodeTypeJump();
void SoftwareInterrupt();
void CheckForbiddenSlot(int64_t current_pc) {
Instruction* instr_after_compact_branch =
reinterpret_cast<Instruction*>(current_pc + kInstrSize);
if (instr_after_compact_branch->IsForbiddenAfterBranch()) {
FATAL(
"Error: Unexpected instruction 0x%08x immediately after a "
"compact branch instruction.",
*reinterpret_cast<uint32_t*>(instr_after_compact_branch));
}
}
bool IsWatchpoint(uint64_t code);
void PrintWatchpoint(uint64_t code);
void HandleStop(uint64_t code, Instruction* instr);
bool IsStopInstruction(Instruction* instr);
bool IsEnabledStop(uint64_t code);
void EnableStop(uint64_t code);
void DisableStop(uint64_t code);
void IncreaseStopCounter(uint64_t code);
void PrintStopInfo(uint64_t code);
void InstructionDecode(Instruction* instr);
void BranchDelayInstructionDecode(Instruction* instr) {
if (instr->InstructionBits() == nopInstr) {
return;
}
if (instr->IsForbiddenAfterBranch()) {
FATAL("Eror:Unexpected %i opcode in a branch delay slot.",
instr->OpcodeValue());
}
InstructionDecode(instr);
SNPrintF(trace_buf_, " ");
}
static void CheckICache(base::CustomMatcherHashMap* i_cache,
Instruction* instr);
static void FlushOnePage(base::CustomMatcherHashMap* i_cache, intptr_t start,
size_t size);
static CachePage* GetCachePage(base::CustomMatcherHashMap* i_cache,
void* page);
enum Exception {
none,
kIntegerOverflow,
kIntegerUnderflow,
kDivideByZero,
kNumExceptions
};
void SignalException(Exception e);
void GetFpArgs(double* x, double* y, int32_t* z);
void SetFpResult(const double& result);
void CallInternal(Address entry);
int64_t registers_[kNumSimuRegisters];
int64_t FPUregisters_[kNumFPURegisters * 2];
uint32_t FCSR_;
uint32_t MSACSR_;
uintptr_t stack_;
static const size_t kStackProtectionSize = KB;
static size_t AllocatedStackSize() {
return (v8_flags.sim_stack_size * KB) + (2 * kStackProtectionSize);
}
static size_t UsableStackSize() { return v8_flags.sim_stack_size * KB; }
uintptr_t stack_limit_;
static const int kAdditionalStackMargin = 4 * KB;
bool pc_modified_;
int64_t icount_;
int break_count_;
base::EmbeddedVector<char, 128> trace_buf_;
char* last_debugger_input_;
v8::internal::Isolate* isolate_;
Instruction* break_pc_;
Instr break_instr_;
static const uint32_t kStopDisabledBit = 1 << 31;
struct StopCountAndDesc {
uint32_t count;
char* desc;
};
StopCountAndDesc watched_stops_[kMaxStopCode + 1];
enum class MonitorAccess {
Open,
RMW,
};
enum class TransactionSize {
None = 0,
Word = 4,
DoubleWord = 8,
};
static const uintptr_t kExclusiveTaggedAddrMask = ~((1 << 3) - 1);
class LocalMonitor {
public:
LocalMonitor();
void NotifyLoad();
void NotifyLoadLinked(uintptr_t addr, TransactionSize size);
void NotifyStore();
bool NotifyStoreConditional(uintptr_t addr, TransactionSize size);
private:
void Clear();
MonitorAccess access_state_;
uintptr_t tagged_addr_;
TransactionSize size_;
};
class GlobalMonitor {
public:
class LinkedAddress {
public:
LinkedAddress();
private:
friend class GlobalMonitor;
void Clear_Locked();
void NotifyLoadLinked_Locked(uintptr_t addr);
void NotifyStore_Locked();
bool NotifyStoreConditional_Locked(uintptr_t addr,
bool is_requesting_thread);
MonitorAccess access_state_;
uintptr_t tagged_addr_;
LinkedAddress* next_;
LinkedAddress* prev_;
static const int kMaxFailureCounter = 5;
int failure_counter_;
};
base::Mutex mutex;
void NotifyLoadLinked_Locked(uintptr_t addr, LinkedAddress* linked_address);
void NotifyStore_Locked(LinkedAddress* linked_address);
bool NotifyStoreConditional_Locked(uintptr_t addr,
LinkedAddress* linked_address);
void RemoveLinkedAddress(LinkedAddress* linked_address);
static GlobalMonitor* Get();
private:
GlobalMonitor() = default;
friend class base::LeakyObject<GlobalMonitor>;
bool IsProcessorInLinkedList_Locked(LinkedAddress* linked_address) const;
void PrependProcessor_Locked(LinkedAddress* linked_address);
LinkedAddress* head_ = nullptr;
};
LocalMonitor local_monitor_;
GlobalMonitor::LinkedAddress global_monitor_thread_;
};
}
}
#endif
#endif