2014-04-05 18:30:08 +02:00
|
|
|
#pragma once
|
|
|
|
|
|
2018-05-04 23:01:27 +02:00
|
|
|
#include "Utilities/File.h"
|
2019-01-21 19:04:32 +01:00
|
|
|
#include "Utilities/mutex.h"
|
|
|
|
|
#include "Utilities/cond.h"
|
|
|
|
|
#include "Utilities/JIT.h"
|
2018-04-09 16:45:37 +02:00
|
|
|
#include "SPUThread.h"
|
2018-05-04 23:01:27 +02:00
|
|
|
#include <vector>
|
2018-04-30 18:44:01 +02:00
|
|
|
#include <bitset>
|
2018-05-04 23:01:27 +02:00
|
|
|
#include <memory>
|
2018-05-10 18:38:07 +02:00
|
|
|
#include <string>
|
2018-08-03 14:34:51 +02:00
|
|
|
#include <deque>
|
2018-05-04 23:01:27 +02:00
|
|
|
|
|
|
|
|
// Helper class
|
|
|
|
|
class spu_cache
|
|
|
|
|
{
|
|
|
|
|
fs::file m_file;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
spu_cache(const std::string& loc);
|
|
|
|
|
|
|
|
|
|
~spu_cache();
|
|
|
|
|
|
|
|
|
|
operator bool() const
|
|
|
|
|
{
|
|
|
|
|
return m_file.operator bool();
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-03 14:34:51 +02:00
|
|
|
std::deque<std::vector<u32>> get();
|
2018-05-04 23:01:27 +02:00
|
|
|
|
|
|
|
|
void add(const std::vector<u32>& func);
|
|
|
|
|
|
|
|
|
|
static void initialize();
|
|
|
|
|
};
|
2014-04-05 18:30:08 +02:00
|
|
|
|
2019-01-21 19:04:32 +01:00
|
|
|
// Helper class
|
|
|
|
|
class spu_runtime
|
|
|
|
|
{
|
2019-03-18 21:01:16 +01:00
|
|
|
mutable shared_mutex m_mutex;
|
|
|
|
|
|
|
|
|
|
mutable cond_variable m_cond;
|
|
|
|
|
|
|
|
|
|
mutable atomic_t<u64> m_passive_locks{0};
|
2019-01-21 19:04:32 +01:00
|
|
|
|
2019-03-18 21:01:16 +01:00
|
|
|
atomic_t<u64> m_reset_count{0};
|
2019-01-21 19:04:32 +01:00
|
|
|
|
|
|
|
|
// All functions
|
|
|
|
|
std::map<std::vector<u32>, spu_function_t> m_map;
|
|
|
|
|
|
|
|
|
|
// Debug module output location
|
|
|
|
|
std::string m_cache_path;
|
|
|
|
|
|
2019-01-28 18:23:26 +01:00
|
|
|
// Trampoline generation workload helper
|
|
|
|
|
struct work
|
|
|
|
|
{
|
|
|
|
|
u32 size;
|
2019-02-27 21:09:09 +01:00
|
|
|
u16 from;
|
|
|
|
|
u16 level;
|
2019-01-28 18:23:26 +01:00
|
|
|
u8* rel32;
|
|
|
|
|
std::map<std::vector<u32>, spu_function_t>::iterator beg;
|
|
|
|
|
std::map<std::vector<u32>, spu_function_t>::iterator end;
|
|
|
|
|
};
|
2019-03-18 21:01:16 +01:00
|
|
|
|
2019-01-28 18:23:26 +01:00
|
|
|
// Scratch vector
|
2019-03-18 21:01:16 +01:00
|
|
|
static thread_local std::vector<work> workload;
|
2019-01-28 18:23:26 +01:00
|
|
|
|
|
|
|
|
// Scratch vector
|
2019-03-18 21:01:16 +01:00
|
|
|
static thread_local std::vector<u32> addrv;
|
2019-01-28 18:23:26 +01:00
|
|
|
|
2019-01-21 19:04:32 +01:00
|
|
|
// Trampoline to spu_recompiler_base::dispatch
|
2019-03-18 17:36:08 +01:00
|
|
|
static const spu_function_t tr_dispatch;
|
2019-01-21 19:04:32 +01:00
|
|
|
|
2019-02-27 23:08:18 +01:00
|
|
|
// Trampoline to spu_recompiler_base::branch
|
2019-03-18 17:36:08 +01:00
|
|
|
static const spu_function_t tr_branch;
|
2019-02-27 23:08:18 +01:00
|
|
|
|
2019-01-21 19:04:32 +01:00
|
|
|
public:
|
|
|
|
|
spu_runtime();
|
|
|
|
|
|
2019-03-18 21:01:16 +01:00
|
|
|
const std::string& get_cache_path() const
|
|
|
|
|
{
|
|
|
|
|
return m_cache_path;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-21 19:04:32 +01:00
|
|
|
// Add compiled function and generate trampoline if necessary
|
2019-03-18 21:01:16 +01:00
|
|
|
bool add(u64 last_reset_count, void* where, spu_function_t compiled);
|
|
|
|
|
|
|
|
|
|
// Return opaque pointer for add()
|
|
|
|
|
void* find(u64 last_reset_count, const std::vector<u32>&);
|
2019-01-28 14:14:01 +01:00
|
|
|
|
2019-03-18 17:40:51 +01:00
|
|
|
// Find existing function
|
2019-03-18 21:01:16 +01:00
|
|
|
spu_function_t find(const se_t<u32, false>* ls, u32 addr) const;
|
2019-03-18 17:40:51 +01:00
|
|
|
|
2019-02-27 23:08:18 +01:00
|
|
|
// Generate a patchable trampoline to spu_recompiler_base::branch
|
|
|
|
|
spu_function_t make_branch_patchpoint(u32 target) const;
|
|
|
|
|
|
2019-03-18 21:01:16 +01:00
|
|
|
// reset() arg retriever, for race avoidance (can result in double reset)
|
|
|
|
|
u64 get_reset_count() const
|
|
|
|
|
{
|
|
|
|
|
return m_reset_count.load();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove all compiled function and free JIT memory
|
|
|
|
|
u64 reset(std::size_t last_reset_count);
|
|
|
|
|
|
2019-03-18 17:33:59 +01:00
|
|
|
// Handle cpu_flag::jit_return
|
2019-03-18 21:01:16 +01:00
|
|
|
void handle_return(spu_thread* _spu);
|
2019-03-18 17:33:59 +01:00
|
|
|
|
2019-01-28 14:14:01 +01:00
|
|
|
// All dispatchers (array allocated in jit memory)
|
|
|
|
|
static atomic_t<spu_function_t>* const g_dispatcher;
|
2019-03-18 21:01:16 +01:00
|
|
|
|
2019-03-25 19:31:16 +01:00
|
|
|
// Interpreter entry point
|
|
|
|
|
static spu_function_t g_interpreter;
|
|
|
|
|
|
2019-03-18 21:01:16 +01:00
|
|
|
struct passive_lock
|
|
|
|
|
{
|
|
|
|
|
spu_runtime& _this;
|
|
|
|
|
|
|
|
|
|
passive_lock(const passive_lock&) = delete;
|
|
|
|
|
|
|
|
|
|
passive_lock(spu_runtime& _this)
|
|
|
|
|
: _this(_this)
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard lock(_this.m_mutex);
|
|
|
|
|
_this.m_passive_locks++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~passive_lock()
|
|
|
|
|
{
|
|
|
|
|
_this.m_passive_locks--;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Exclusive lock within passive_lock scope
|
|
|
|
|
struct writer_lock
|
|
|
|
|
{
|
|
|
|
|
spu_runtime& _this;
|
|
|
|
|
bool notify = false;
|
|
|
|
|
|
|
|
|
|
writer_lock(const writer_lock&) = delete;
|
|
|
|
|
|
|
|
|
|
writer_lock(spu_runtime& _this)
|
|
|
|
|
: _this(_this)
|
|
|
|
|
{
|
|
|
|
|
// Temporarily release the passive lock
|
|
|
|
|
_this.m_passive_locks--;
|
|
|
|
|
_this.m_mutex.lock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~writer_lock()
|
|
|
|
|
{
|
|
|
|
|
_this.m_passive_locks++;
|
|
|
|
|
_this.m_mutex.unlock();
|
|
|
|
|
|
|
|
|
|
if (notify)
|
|
|
|
|
{
|
|
|
|
|
_this.m_cond.notify_all();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct reader_lock
|
|
|
|
|
{
|
|
|
|
|
const spu_runtime& _this;
|
|
|
|
|
|
|
|
|
|
reader_lock(const reader_lock&) = delete;
|
|
|
|
|
|
|
|
|
|
reader_lock(const spu_runtime& _this)
|
|
|
|
|
: _this(_this)
|
|
|
|
|
{
|
|
|
|
|
_this.m_passive_locks--;
|
|
|
|
|
_this.m_mutex.lock_shared();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~reader_lock()
|
|
|
|
|
{
|
|
|
|
|
_this.m_passive_locks++;
|
|
|
|
|
_this.m_mutex.unlock_shared();
|
|
|
|
|
}
|
|
|
|
|
};
|
2019-01-21 19:04:32 +01:00
|
|
|
};
|
|
|
|
|
|
2018-04-09 16:45:37 +02:00
|
|
|
// SPU Recompiler instance base class
|
2016-04-14 01:09:41 +02:00
|
|
|
class spu_recompiler_base
|
2014-04-05 18:30:08 +02:00
|
|
|
{
|
2019-04-30 23:06:42 +02:00
|
|
|
public:
|
|
|
|
|
enum : u8
|
|
|
|
|
{
|
|
|
|
|
s_reg_lr = 0,
|
|
|
|
|
s_reg_sp = 1,
|
|
|
|
|
s_reg_80 = 80,
|
|
|
|
|
s_reg_127 = 127,
|
|
|
|
|
|
|
|
|
|
s_reg_mfc_eal,
|
|
|
|
|
s_reg_mfc_lsa,
|
|
|
|
|
s_reg_mfc_tag,
|
|
|
|
|
s_reg_mfc_size,
|
|
|
|
|
|
|
|
|
|
// Max number of registers (for m_regmod)
|
|
|
|
|
s_reg_max
|
|
|
|
|
};
|
|
|
|
|
|
2015-08-26 04:54:06 +02:00
|
|
|
protected:
|
2019-03-18 17:33:59 +01:00
|
|
|
std::shared_ptr<spu_runtime> m_spurt;
|
|
|
|
|
|
2018-04-09 16:45:37 +02:00
|
|
|
u32 m_pos;
|
2018-05-02 20:49:19 +02:00
|
|
|
u32 m_size;
|
2014-04-05 18:30:08 +02:00
|
|
|
|
2018-05-10 18:38:07 +02:00
|
|
|
// Bit indicating start of the block
|
2018-04-30 18:44:01 +02:00
|
|
|
std::bitset<0x10000> m_block_info;
|
|
|
|
|
|
2018-05-10 18:38:07 +02:00
|
|
|
// GPR modified by the instruction (-1 = not set)
|
|
|
|
|
std::array<u8, 0x10000> m_regmod;
|
|
|
|
|
|
2019-04-28 02:36:17 +02:00
|
|
|
std::array<u8, 0x10000> m_use_ra;
|
|
|
|
|
std::array<u8, 0x10000> m_use_rb;
|
|
|
|
|
std::array<u8, 0x10000> m_use_rc;
|
|
|
|
|
|
2018-06-10 14:46:01 +02:00
|
|
|
// List of possible targets for the instruction (entry shouldn't exist for simple instructions)
|
2018-05-10 18:38:07 +02:00
|
|
|
std::unordered_map<u32, std::basic_string<u32>, value_hash<u32, 2>> m_targets;
|
|
|
|
|
|
2018-06-10 14:46:01 +02:00
|
|
|
// List of block predecessors
|
2018-05-13 19:34:11 +02:00
|
|
|
std::unordered_map<u32, std::basic_string<u32>, value_hash<u32, 2>> m_preds;
|
|
|
|
|
|
2018-06-10 14:46:01 +02:00
|
|
|
// List of function entry points and return points (set after BRSL, BRASL, BISL, BISLED)
|
|
|
|
|
std::bitset<0x10000> m_entry_info;
|
|
|
|
|
|
2019-04-30 23:06:42 +02:00
|
|
|
// Basic block information
|
|
|
|
|
struct block_info
|
|
|
|
|
{
|
|
|
|
|
// Address of the chunk entry point (chunk this block belongs to)
|
|
|
|
|
u32 chunk = 0x40000;
|
|
|
|
|
|
|
|
|
|
// Number of instructions
|
|
|
|
|
u16 size = 0;
|
|
|
|
|
|
|
|
|
|
// Bit mask of the registers modified in the block
|
|
|
|
|
std::bitset<s_reg_max> reg_mod{};
|
|
|
|
|
|
|
|
|
|
// Bit mask of the registers used (before modified)
|
|
|
|
|
std::bitset<s_reg_max> reg_use{};
|
|
|
|
|
|
|
|
|
|
// Single source of the reg value (dominating block address within the same chunk) or a negative number
|
|
|
|
|
std::array<u32, s_reg_max> reg_origin, reg_origin_abs;
|
|
|
|
|
|
|
|
|
|
// All possible successor blocks
|
|
|
|
|
std::basic_string<u32> targets;
|
|
|
|
|
|
|
|
|
|
// All predeccessor blocks
|
|
|
|
|
std::basic_string<u32> preds;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Sorted basic block info
|
|
|
|
|
std::map<u32, block_info> m_bbs;
|
|
|
|
|
|
|
|
|
|
// Advanced block (chunk) information
|
|
|
|
|
struct chunk_info
|
|
|
|
|
{
|
|
|
|
|
// Flag set for synthetic chunks (false for call targets and return locations)
|
|
|
|
|
bool joinable = false;
|
|
|
|
|
|
|
|
|
|
// List of basic blocks
|
|
|
|
|
std::basic_string<u32> bbs;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Sorted chunk info
|
|
|
|
|
std::map<u32, chunk_info> m_chunks;
|
2018-06-10 14:46:01 +02:00
|
|
|
|
2018-05-04 23:01:27 +02:00
|
|
|
std::shared_ptr<spu_cache> m_cache;
|
|
|
|
|
|
2018-06-01 23:33:48 +02:00
|
|
|
private:
|
|
|
|
|
// For private use
|
|
|
|
|
std::bitset<0x10000> m_bits;
|
|
|
|
|
|
2019-03-22 02:19:33 +01:00
|
|
|
// Result of analyse(), to avoid copying and allocation
|
|
|
|
|
std::vector<u32> result;
|
|
|
|
|
|
2015-08-26 04:54:06 +02:00
|
|
|
public:
|
2018-05-03 14:55:45 +02:00
|
|
|
spu_recompiler_base();
|
2018-04-09 16:45:37 +02:00
|
|
|
|
2017-01-25 00:22:19 +01:00
|
|
|
virtual ~spu_recompiler_base();
|
2014-04-05 18:30:08 +02:00
|
|
|
|
2018-05-04 23:01:27 +02:00
|
|
|
// Initialize
|
|
|
|
|
virtual void init() = 0;
|
|
|
|
|
|
2019-03-18 21:01:16 +01:00
|
|
|
// Compile function (may fail)
|
2019-05-01 13:56:41 +02:00
|
|
|
virtual spu_function_t compile(u64 last_reset_count, const std::vector<u32>&) = 0;
|
2019-03-18 21:01:16 +01:00
|
|
|
|
|
|
|
|
// Compile function, handle failure
|
|
|
|
|
void make_function(const std::vector<u32>&);
|
2018-04-09 16:45:37 +02:00
|
|
|
|
2018-05-03 14:55:45 +02:00
|
|
|
// Default dispatch function fallback (second arg is unused)
|
2018-10-11 00:17:19 +02:00
|
|
|
static void dispatch(spu_thread&, void*, u8* rip);
|
2018-04-09 16:45:37 +02:00
|
|
|
|
2018-05-03 14:55:45 +02:00
|
|
|
// Target for the unresolved patch point (second arg is unused)
|
2018-10-11 00:17:19 +02:00
|
|
|
static void branch(spu_thread&, void*, u8* rip);
|
2018-04-09 16:45:37 +02:00
|
|
|
|
2019-03-22 02:19:33 +01:00
|
|
|
// Get the function data at specified address
|
|
|
|
|
const std::vector<u32>& analyse(const be_t<u32>* ls, u32 lsa);
|
2015-05-27 05:11:59 +02:00
|
|
|
|
2018-06-10 14:46:01 +02:00
|
|
|
// Print analyser internal state
|
|
|
|
|
void dump(std::string& out);
|
|
|
|
|
|
2019-03-18 17:33:59 +01:00
|
|
|
// Get SPU Runtime
|
|
|
|
|
spu_runtime& get_runtime()
|
|
|
|
|
{
|
|
|
|
|
if (!m_spurt)
|
|
|
|
|
{
|
|
|
|
|
init();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return *m_spurt;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-09 16:45:37 +02:00
|
|
|
// Create recompiler instance (ASMJIT)
|
2018-05-03 14:55:45 +02:00
|
|
|
static std::unique_ptr<spu_recompiler_base> make_asmjit_recompiler();
|
2018-05-02 20:49:19 +02:00
|
|
|
|
|
|
|
|
// Create recompiler instance (LLVM)
|
2019-03-25 19:31:16 +01:00
|
|
|
static std::unique_ptr<spu_recompiler_base> make_llvm_recompiler(u8 magn = 0);
|
2014-04-08 17:10:07 +02:00
|
|
|
};
|