#pragma once
#include <cstdint>
#include <map>
#include <unordered_map>
#include <vector>
#include <string>
#include "ppsspp_config.h"
#include "Common/CommonTypes.h"
#include "Common/CodeBlock.h"
#include "Core/MIPS/MIPS.h"
#if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64)
const int MAX_JIT_BLOCK_EXITS = 4;
#else
const int MAX_JIT_BLOCK_EXITS = 8;
#endif
constexpr bool JIT_USE_COMPILEDHASH = true;
struct BlockCacheStats {
int numBlocks;
float avgBloat;
float minBloat;
u32 minBloatBlock;
float maxBloat;
u32 maxBloatBlock;
};
enum class DestroyType {
DESTROY,
INVALIDATE,
CLEAR,
};
struct JitBlock {
bool ContainsAddress(u32 em_address) const;
const u8 *checkedEntry;
const u8 *normalEntry;
u8 *exitPtrs[MAX_JIT_BLOCK_EXITS];
u32 exitAddress[MAX_JIT_BLOCK_EXITS];
u32 originalAddress;
MIPSOpcode originalFirstOpcode;
uint64_t compiledHash;
u16 codeSize;
u16 originalSize;
u16 blockNum;
bool invalid;
bool linkStatus[MAX_JIT_BLOCK_EXITS];
#ifdef USE_VTUNE
char blockName[32];
#endif
std::vector<u32> *proxyFor;
bool IsPureProxy() const {
return originalFirstOpcode.encoding == 0x68FF0000;
}
void SetPureProxy() {
originalFirstOpcode.encoding = 0x68FF0000;
}
};
typedef void (*CompiledCode)();
struct JitBlockDebugInfo {
uint32_t originalAddress;
std::vector<std::string> origDisasm;
std::vector<std::string> irDisasm;
std::vector<std::string> targetDisasm;
};
struct JitBlockMeta {
bool valid;
uint32_t addr;
uint32_t sizeInBytes;
};
struct JitBlockProfileStats {
int64_t executions;
int64_t totalNanos;
};
class JitBlockCacheDebugInterface {
public:
virtual int GetNumBlocks() const = 0;
virtual int GetBlockNumberFromStartAddress(u32 em_address, bool realBlocksOnly = true) const = 0;
virtual JitBlockDebugInfo GetBlockDebugInfo(int blockNum) const = 0;
virtual JitBlockMeta GetBlockMeta(int blockNum) const = 0;
virtual JitBlockProfileStats GetBlockProfileStats(int blockNum) const = 0;
virtual void ComputeStats(BlockCacheStats &bcStats) const = 0;
virtual bool IsValidBlock(int blockNum) const = 0;
virtual bool SupportsProfiling() const { return false; }
virtual ~JitBlockCacheDebugInterface() {}
};
class JitBlockCache : public JitBlockCacheDebugInterface {
public:
JitBlockCache(MIPSState *mipsState, CodeBlockCommon *codeBlock);
~JitBlockCache();
int AllocateBlock(u32 em_address);
void ProxyBlock(u32 rootAddress, u32 startAddress, u32 size, const u8 *codePtr);
void FinalizeBlock(int block_num, bool block_link);
void Clear();
void Init();
void Shutdown();
void Reset();
bool IsFull() const;
void ComputeStats(BlockCacheStats &bcStats) const override;
JitBlock *GetBlock(int block_num);
const JitBlock *GetBlock(int block_num) const;
int GetBlockNumberFromStartAddress(u32 em_address, bool realBlocksOnly = true) const override;
void GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers);
int GetBlockNumberFromAddress(u32 em_address);
int GetBlockNumberFromEmuHackOp(MIPSOpcode inst, bool ignoreBad = false) const;
u32 GetAddressFromBlockPtr(const u8 *ptr) const;
MIPSOpcode GetOriginalFirstOp(int block_num);
bool RangeMayHaveEmuHacks(u32 start, u32 end) const;
void InvalidateICache(u32 address, const u32 length);
void InvalidateChangedBlocks();
void DestroyBlock(int block_num, DestroyType type);
std::vector<u32> SaveAndClearEmuHackOps();
void RestoreSavedEmuHackOps(const std::vector<u32> &saved);
int GetNumBlocks() const override { return num_blocks_; }
bool IsValidBlock(int blockNum) const override { return blockNum >= 0 && blockNum < num_blocks_ && !blocks_[blockNum].invalid; }
JitBlockMeta GetBlockMeta(int blockNum) const override {
JitBlockMeta meta{};
if (IsValidBlock(blockNum)) {
meta.valid = true;
meta.addr = blocks_[blockNum].originalAddress;
meta.sizeInBytes = blocks_[blockNum].originalSize;
}
return meta;
}
JitBlockProfileStats GetBlockProfileStats(int blockNum) const override {
return JitBlockProfileStats{};
}
static int GetBlockExitSize();
JitBlockDebugInfo GetBlockDebugInfo(int blockNum) const override;
enum {
MAX_BLOCK_INSTRUCTIONS = 0x4000,
};
private:
void LinkBlockExits(int i);
void LinkBlock(int i);
void UnlinkBlock(int i);
void AddBlockMap(int block_num);
void RemoveBlockMap(int block_num);
MIPSOpcode GetEmuHackOpForBlock(int block_num) const;
CodeBlockCommon *codeBlock_;
JitBlock *blocks_ = nullptr;
std::unordered_multimap<u32, int> proxyBlockMap_;
int num_blocks_ = 0;
std::unordered_multimap<u32, int> links_to_;
std::map<std::pair<u32,u32>, u32> block_map_;
enum {
JITBLOCK_RANGE_SCRATCH = 0,
JITBLOCK_RANGE_RAMBOTTOM = 1,
JITBLOCK_RANGE_RAMTOP = 2,
JITBLOCK_RANGE_COUNT = 3,
};
std::pair<u32, u32> blockMemRanges_[3];
enum {
MAX_NUM_BLOCKS = 65536 * 4
};
};