Files

259 lines
5.6 KiB
C++
Raw Permalink Normal View History

// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
2024-07-30 13:42:36 +02:00
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include <cstring>
#include <map>
#include "common/Assertions.h"
// Every potential jump point in the PS2's addressable memory has a BASEBLOCK
// associated with it. So that means a BASEBLOCK for every 4 bytes of PS2
// addressable memory. Yay!
struct BASEBLOCK
{
2015-10-23 19:33:51 +02:00
uptr m_pFnptr;
__inline uptr GetFnptr() const { return m_pFnptr; }
2021-08-30 01:38:13 -05:00
void __inline SetFnptr(uptr ptr) { m_pFnptr = ptr; }
};
// extra block info (only valid for start of fn)
struct BASEBLOCKEX
{
uptr fnptr;
2023-03-18 18:22:28 -05:00
u32 startpc;
u32 size; // The size in dwords (equivalent to the number of instructions)
u32 x86size; // The size in byte of the translated x86 instructions
// arm64 SL-1 loop residency (unused by the x86 rec, zeroed by insert()):
// the resident back-edge B is an INTERNAL branch to the block's loop-top
// label, so the entry redirect stub Remove() writes cannot catch it. When
// set, Remove() atomically repoints backedge_site to backedge_stub (the
// cold spill stub → DispatcherEvent) so a cleared self-loop exits its
// stale code at the next back-edge instead of the next event.
uptr backedge_site;
uptr backedge_stub;
#ifdef PCSX2_DEVBUILD
// Could be useful to instrument the block
//u32 visited; // number of times called
//u64 ltime; // regs it assumes to have set already
#endif
};
2021-08-30 01:38:13 -05:00
class BaseBlockArray
{
2011-04-13 16:32:47 +00:00
s32 _Reserved;
s32 _Size;
2021-08-30 01:38:13 -05:00
BASEBLOCKEX* blocks;
2011-04-13 16:32:47 +00:00
__fi void resize(s32 size)
{
pxAssert(size > 0);
2021-08-30 01:38:13 -05:00
BASEBLOCKEX* newMem = new BASEBLOCKEX[size];
if (blocks)
{
2014-11-24 10:00:46 +01:00
memcpy(newMem, blocks, _Reserved * sizeof(BASEBLOCKEX));
2011-04-13 16:32:47 +00:00
delete[] blocks;
}
blocks = newMem;
pxAssert(blocks != NULL);
}
2015-10-30 19:03:34 +01:00
void reserve(u32 size)
{
resize(size);
_Reserved = size;
}
2021-08-30 01:38:13 -05:00
2011-04-13 16:32:47 +00:00
public:
~BaseBlockArray()
{
2021-08-30 01:38:13 -05:00
if (blocks)
2011-04-13 16:32:47 +00:00
delete[] blocks;
}
2021-08-30 01:38:13 -05:00
BaseBlockArray(s32 size)
: _Reserved(0)
, _Size(0)
, blocks(NULL)
2011-04-13 16:32:47 +00:00
{
reserve(size);
2011-04-13 16:32:47 +00:00
}
2021-08-30 01:38:13 -05:00
BASEBLOCKEX* insert(u32 startpc, uptr fnptr)
2011-04-13 16:32:47 +00:00
{
2021-08-30 01:38:13 -05:00
if (_Size + 1 >= _Reserved)
{
2014-11-24 10:00:46 +01:00
reserve(_Reserved + 0x2000); // some games requires even more!
2011-04-13 16:32:47 +00:00
}
2015-10-30 19:03:34 +01:00
// Insert the the new BASEBLOCKEX by startpc order
2011-04-13 16:32:47 +00:00
int imin = 0, imax = _Size, imid;
2021-08-30 01:38:13 -05:00
while (imin < imax)
{
imid = (imin + imax) >> 1;
2011-04-13 16:32:47 +00:00
if (blocks[imid].startpc > startpc)
imax = imid;
else
imin = imid + 1;
}
2021-08-30 01:38:13 -05:00
2011-04-13 16:32:47 +00:00
pxAssert(imin == _Size || blocks[imin].startpc > startpc);
2021-08-30 01:38:13 -05:00
if (imin < _Size)
{
2011-04-13 16:32:47 +00:00
// make a hole for a new block.
memmove(blocks + imin + 1, blocks + imin, (_Size - imin) * sizeof(BASEBLOCKEX));
}
memset((blocks + imin), 0, sizeof(BASEBLOCKEX));
blocks[imin].startpc = startpc;
blocks[imin].fnptr = fnptr;
_Size++;
return &blocks[imin];
}
2021-08-30 01:38:13 -05:00
__fi BASEBLOCKEX& operator[](int idx) const
2011-04-13 16:32:47 +00:00
{
return *(blocks + idx);
}
void clear()
{
_Size = 0;
}
__fi u32 size() const
{
return _Size;
}
__fi void erase(s32 first, s32 last)
{
int range = last - first;
2021-08-30 01:38:13 -05:00
if (last < _Size)
{
2011-04-13 16:32:47 +00:00
memmove(blocks + first, blocks + last, (_Size - last) * sizeof(BASEBLOCKEX));
}
_Size -= range;
}
};
class BaseBlocks
{
protected:
typedef std::multimap<u32, uptr>::iterator linkiter_t;
// switch to a hash map later?
std::multimap<u32, uptr> links;
uptr recompiler;
2011-04-13 16:32:47 +00:00
BaseBlockArray blocks;
public:
2021-08-30 01:38:13 -05:00
BaseBlocks()
: recompiler(0)
, blocks(0x4000)
{
}
void SetJITCompile(const void *recompiler_)
{
recompiler = reinterpret_cast<uptr>(recompiler_);
}
BASEBLOCKEX* New(u32 startpc, uptr fnptr);
2021-08-30 01:38:13 -05:00
int LastIndex(u32 startpc) const;
2015-10-30 19:03:34 +01:00
//BASEBLOCKEX* GetByX86(uptr ip);
2009-03-13 18:23:28 +00:00
2021-08-30 01:38:13 -05:00
__fi int Index(u32 startpc) const
2009-03-13 18:23:28 +00:00
{
int idx = LastIndex(startpc);
2011-04-13 16:32:47 +00:00
if ((idx == -1) || (startpc < blocks[idx].startpc) ||
((blocks[idx].size) && (startpc >= blocks[idx].startpc + blocks[idx].size * 4)))
2009-03-13 18:23:28 +00:00
return -1;
else
return idx;
}
2010-08-09 04:10:38 +00:00
__fi BASEBLOCKEX* operator[](int idx)
{
if (idx < 0 || idx >= (int)blocks.size())
return 0;
2011-04-13 16:32:47 +00:00
return &blocks[idx];
}
2010-08-09 04:10:38 +00:00
__fi BASEBLOCKEX* Get(u32 startpc)
{
return (*this)[Index(startpc)];
}
2011-04-13 16:32:47 +00:00
__fi void Remove(int first, int last)
{
2011-04-13 16:32:47 +00:00
pxAssert(first <= last);
int idx = first;
2021-08-30 01:38:13 -05:00
do
{
2011-04-13 16:32:47 +00:00
pxAssert(idx <= last);
2011-04-13 16:32:47 +00:00
//u32 startpc = blocks[idx].startpc;
std::pair<linkiter_t, linkiter_t> range = links.equal_range(blocks[idx].startpc);
for (linkiter_t i = range.first; i != range.second; ++i)
*(u32*)i->second = recompiler - (i->second + 4);
2021-08-30 01:38:13 -05:00
if (IsDevBuild)
2011-04-13 16:32:47 +00:00
{
// Clear the first instruction to 0xcc (breakpoint), as a way to assert if some
// static jumps get left behind to this block. Note: Do not clear more than the
// first byte, since this code is called during exception handlers and event handlers
// both of which expect to be able to return to the recompiled code.
2021-08-30 01:38:13 -05:00
BASEBLOCKEX effu(blocks[idx]);
memset((void*)effu.fnptr, 0xcc, 1);
2011-04-13 16:32:47 +00:00
}
2021-08-30 01:38:13 -05:00
} while (idx++ < last);
// TODO: remove links from this block?
2011-04-13 16:32:47 +00:00
blocks.erase(first, last + 1);
}
void Link(u32 pc, s32* jumpptr);
2010-08-09 04:10:38 +00:00
__fi void Reset()
{
blocks.clear();
links.clear();
}
};
2021-08-30 01:38:13 -05:00
#define PC_GETBLOCK_(x, reclut) ((BASEBLOCK*)(reclut[((u32)(x)) >> 16] + (x) * (sizeof(BASEBLOCK) / 4)))
2020-06-24 17:27:17 -05:00
/**
* Add a page to the recompiler lookup table
*
* Will associate `reclut[pagebase + pageidx]` with `mapbase[mappage << 14]`
* Will associate `hwlut[pagebase + pageidx]` with `pageidx << 16`
*/
static inline void recLUT_SetPage(uptr reclut[0x10000], u32 hwlut[0x10000],
BASEBLOCK* mapbase, uint pagebase, uint pageidx, uint mappage)
{
// this value is in 64k pages!
uint page = pagebase + pageidx;
2021-08-30 01:38:13 -05:00
pxAssert(page < 0x10000);
2020-04-15 16:11:53 -05:00
reclut[page] = (uptr)&mapbase[((s32)mappage - (s32)page) << 14];
if (hwlut)
hwlut[page] = 0u - (pagebase << 16);
}
2021-08-30 01:38:13 -05:00
static_assert(sizeof(BASEBLOCK) == 8, "BASEBLOCK is not 8 bytes");