PowerPC recompiler rework (#641)
Some checks failed
Build check / build (push) Failing after 0s
Generate translation template / generate-pot (push) Failing after 0s

This commit is contained in:
Exzap 2025-04-26 17:59:32 +02:00 committed by GitHub
parent 06233e3462
commit b089ae5b32
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
54 changed files with 15433 additions and 12397 deletions

View file

@ -1,4 +1,4 @@
#include <vector>
#pragma once
#define PPC_REC_CODE_AREA_START (0x00000000) // lower bound of executable memory area. Recompiler expects this address to be 0
#define PPC_REC_CODE_AREA_END (0x10000000) // upper bound of executable memory area
@ -6,336 +6,113 @@
#define PPC_REC_ALIGN_TO_4MB(__v) (((__v)+4*1024*1024-1)&~(4*1024*1024-1))
#define PPC_REC_MAX_VIRTUAL_GPR (40) // enough to store 32 GPRs + a few SPRs + temp registers (usually only 1-2)
#define PPC_REC_MAX_VIRTUAL_GPR (40 + 32) // enough to store 32 GPRs + a few SPRs + temp registers (usually only 1-2)
typedef struct
struct ppcRecRange_t
{
uint32 ppcAddress;
uint32 ppcSize;
//void* x86Start;
//size_t x86Size;
void* storedRange;
}ppcRecRange_t;
};
typedef struct
struct PPCRecFunction_t
{
uint32 ppcAddress;
uint32 ppcSize; // ppc code size of function
void* x86Code; // pointer to x86 code
size_t x86Size;
std::vector<ppcRecRange_t> list_ranges;
}PPCRecFunction_t;
#define PPCREC_IML_OP_FLAG_SIGNEXTEND (1<<0)
#define PPCREC_IML_OP_FLAG_SWITCHENDIAN (1<<1)
#define PPCREC_IML_OP_FLAG_NOT_EXPANDED (1<<2) // set single-precision load instructions to indicate that the value should not be rounded to double-precision
#define PPCREC_IML_OP_FLAG_UNUSED (1<<7) // used to mark instructions that are not used
typedef struct
{
uint8 type;
uint8 operation;
uint8 crRegister; // set to 0xFF if not set, not all IML instruction types support cr.
uint8 crMode; // only used when crRegister is valid, used to differentiate between various forms of condition flag set/clear behavior
uint32 crIgnoreMask; // bit set for every respective CR bit that doesn't need to be updated
uint32 associatedPPCAddress; // ppc address that is associated with this instruction
union
{
struct
{
uint8 _padding[7];
}padding;
struct
{
// R (op) A [update cr* in mode *]
uint8 registerResult;
uint8 registerA;
}op_r_r;
struct
{
// R = A (op) B [update cr* in mode *]
uint8 registerResult;
uint8 registerA;
uint8 registerB;
}op_r_r_r;
struct
{
// R = A (op) immS32 [update cr* in mode *]
uint8 registerResult;
uint8 registerA;
sint32 immS32;
}op_r_r_s32;
struct
{
// R/F = NAME or NAME = R/F
uint8 registerIndex;
uint8 copyWidth;
uint32 name;
uint8 flags;
}op_r_name;
struct
{
// R (op) s32 [update cr* in mode *]
uint8 registerIndex;
sint32 immS32;
}op_r_immS32;
struct
{
uint32 address;
uint8 flags;
}op_jumpmark;
struct
{
uint32 param;
uint32 param2;
uint16 paramU16;
}op_macro;
struct
{
uint32 jumpmarkAddress;
bool jumpAccordingToSegment; //PPCRecImlSegment_t* destinationSegment; // if set, this replaces jumpmarkAddress
uint8 condition; // only used when crRegisterIndex is 8 or above (update: Apparently only used to mark jumps without a condition? -> Cleanup)
uint8 crRegisterIndex;
uint8 crBitIndex;
bool bitMustBeSet;
}op_conditionalJump;
struct
{
uint8 registerData;
uint8 registerMem;
uint8 registerMem2;
uint8 registerGQR;
uint8 copyWidth;
//uint8 flags;
struct
{
bool swapEndian : 1;
bool signExtend : 1;
bool notExpanded : 1; // for floats
}flags2;
uint8 mode; // transfer mode (copy width, ps0/ps1 behavior)
sint32 immS32;
}op_storeLoad;
struct
{
struct
{
uint8 registerMem;
sint32 immS32;
}src;
struct
{
uint8 registerMem;
sint32 immS32;
}dst;
uint8 copyWidth;
}op_mem2mem;
struct
{
uint8 registerResult;
uint8 registerOperand;
uint8 flags;
}op_fpr_r_r;
struct
{
uint8 registerResult;
uint8 registerOperandA;
uint8 registerOperandB;
uint8 flags;
}op_fpr_r_r_r;
struct
{
uint8 registerResult;
uint8 registerOperandA;
uint8 registerOperandB;
uint8 registerOperandC;
uint8 flags;
}op_fpr_r_r_r_r;
struct
{
uint8 registerResult;
//uint8 flags;
}op_fpr_r;
struct
{
uint32 ppcAddress;
uint32 x64Offset;
}op_ppcEnter;
struct
{
uint8 crD; // crBitIndex (result)
uint8 crA; // crBitIndex
uint8 crB; // crBitIndex
}op_cr;
// conditional operations (emitted if supported by target platform)
struct
{
// r_s32
uint8 registerIndex;
sint32 immS32;
// condition
uint8 crRegisterIndex;
uint8 crBitIndex;
bool bitMustBeSet;
}op_conditional_r_s32;
};
}PPCRecImlInstruction_t;
typedef struct _PPCRecImlSegment_t PPCRecImlSegment_t;
typedef struct _ppcRecompilerSegmentPoint_t
{
sint32 index;
PPCRecImlSegment_t* imlSegment;
_ppcRecompilerSegmentPoint_t* next;
_ppcRecompilerSegmentPoint_t* prev;
}ppcRecompilerSegmentPoint_t;
struct raLivenessLocation_t
{
sint32 index;
bool isRead;
bool isWrite;
raLivenessLocation_t() = default;
raLivenessLocation_t(sint32 index, bool isRead, bool isWrite)
: index(index), isRead(isRead), isWrite(isWrite) {};
};
struct raLivenessSubrangeLink_t
{
struct raLivenessSubrange_t* prev;
struct raLivenessSubrange_t* next;
};
#include "Cafe/HW/Espresso/Recompiler/IML/IMLInstruction.h"
#include "Cafe/HW/Espresso/Recompiler/IML/IMLSegment.h"
struct raLivenessSubrange_t
{
struct raLivenessRange_t* range;
PPCRecImlSegment_t* imlSegment;
ppcRecompilerSegmentPoint_t start;
ppcRecompilerSegmentPoint_t end;
// dirty state tracking
bool _noLoad;
bool hasStore;
bool hasStoreDelayed;
// next
raLivenessSubrange_t* subrangeBranchTaken;
raLivenessSubrange_t* subrangeBranchNotTaken;
// processing
uint32 lastIterationIndex;
// instruction locations
std::vector<raLivenessLocation_t> list_locations;
// linked list (subranges with same GPR virtual register)
raLivenessSubrangeLink_t link_sameVirtualRegisterGPR;
// linked list (all subranges for this segment)
raLivenessSubrangeLink_t link_segmentSubrangesGPR;
};
struct raLivenessRange_t
{
sint32 virtualRegister;
sint32 physicalRegister;
sint32 name;
std::vector<raLivenessSubrange_t*> list_subranges;
};
struct PPCSegmentRegisterAllocatorInfo_t
{
// analyzer stage
bool isPartOfProcessedLoop{}; // used during loop detection
sint32 lastIterationIndex{};
// linked lists
raLivenessSubrange_t* linkedList_allSubranges{};
raLivenessSubrange_t* linkedList_perVirtualGPR[PPC_REC_MAX_VIRTUAL_GPR]{};
};
struct PPCRecVGPRDistances_t
{
struct _RegArrayEntry
{
sint32 usageStart{};
sint32 usageEnd{};
}reg[PPC_REC_MAX_VIRTUAL_GPR];
bool isProcessed[PPC_REC_MAX_VIRTUAL_GPR]{};
};
typedef struct _PPCRecImlSegment_t
{
sint32 momentaryIndex{}; // index in segment list, generally not kept up to date except if needed (necessary for loop detection)
sint32 startOffset{}; // offset to first instruction in iml instruction list
sint32 count{}; // number of instructions in segment
uint32 ppcAddress{}; // ppc address (0xFFFFFFFF if not associated with an address)
uint32 x64Offset{}; // x64 code offset of segment start
uint32 cycleCount{}; // number of PPC cycles required to execute this segment (roughly)
// list of intermediate instructions in this segment
PPCRecImlInstruction_t* imlList{};
sint32 imlListSize{};
sint32 imlListCount{};
// segment link
_PPCRecImlSegment_t* nextSegmentBranchNotTaken{}; // this is also the default for segments where there is no branch
_PPCRecImlSegment_t* nextSegmentBranchTaken{};
bool nextSegmentIsUncertain{};
sint32 loopDepth{};
//sList_t* list_prevSegments;
std::vector<_PPCRecImlSegment_t*> list_prevSegments{};
// PPC range of segment
uint32 ppcAddrMin{};
uint32 ppcAddrMax{};
// enterable segments
bool isEnterable{}; // this segment can be entered from outside the recompiler (no preloaded registers necessary)
uint32 enterPPCAddress{}; // used if isEnterable is true
// jump destination segments
bool isJumpDestination{}; // segment is a destination for one or more (conditional) jumps
uint32 jumpDestinationPPCAddress{};
// PPC FPR use mask
bool ppcFPRUsed[32]{}; // same as ppcGPRUsed, but for FPR
// CR use mask
uint32 crBitsInput{}; // bits that are expected to be set from the previous segment (read in this segment but not overwritten)
uint32 crBitsRead{}; // all bits that are read in this segment
uint32 crBitsWritten{}; // bits that are written in this segment
// register allocator info
PPCSegmentRegisterAllocatorInfo_t raInfo{};
PPCRecVGPRDistances_t raDistances{};
bool raRangeExtendProcessed{};
// segment points
ppcRecompilerSegmentPoint_t* segmentPointList{};
}PPCRecImlSegment_t;
struct IMLInstruction* PPCRecompilerImlGen_generateNewEmptyInstruction(struct ppcImlGenContext_t* ppcImlGenContext);
struct ppcImlGenContext_t
{
PPCRecFunction_t* functionRef;
class PPCFunctionBoundaryTracker* boundaryTracker;
uint32* currentInstruction;
uint32 ppcAddressOfCurrentInstruction;
IMLSegment* currentOutputSegment;
struct PPCBasicBlockInfo* currentBasicBlock{};
// fpr mode
bool LSQE{ true };
bool PSE{ true };
// cycle counter
uint32 cyclesSinceLastBranch; // used to track ppc cycles
// temporary general purpose registers
uint32 mappedRegister[PPC_REC_MAX_VIRTUAL_GPR];
// temporary floating point registers (single and double precision)
uint32 mappedFPRRegister[256];
// list of intermediate instructions
PPCRecImlInstruction_t* imlList;
sint32 imlListSize;
sint32 imlListCount;
std::unordered_map<IMLName, IMLReg> mappedRegs;
uint32 GetMaxRegId() const
{
if (mappedRegs.empty())
return 0;
return mappedRegs.size()-1;
}
// list of segments
PPCRecImlSegment_t** segmentList;
sint32 segmentListSize;
sint32 segmentListCount;
std::vector<IMLSegment*> segmentList2;
// code generation control
bool hasFPUInstruction; // if true, PPCEnter macro will create FP_UNAVAIL checks -> Not needed in user mode
// register allocator info
struct
{
std::vector<raLivenessRange_t*> list_ranges;
}raInfo;
// analysis info
struct
{
bool modifiesGQR[8];
}tracking;
// debug helpers
uint32 debug_entryPPCAddress{0};
~ppcImlGenContext_t()
{
for (IMLSegment* imlSegment : segmentList2)
delete imlSegment;
segmentList2.clear();
}
// append raw instruction
IMLInstruction& emitInst()
{
return *PPCRecompilerImlGen_generateNewEmptyInstruction(this);
}
IMLSegment* NewSegment()
{
IMLSegment* seg = new IMLSegment();
segmentList2.emplace_back(seg);
return seg;
}
size_t GetSegmentIndex(IMLSegment* seg)
{
for (size_t i = 0; i < segmentList2.size(); i++)
{
if (segmentList2[i] == seg)
return i;
}
cemu_assert_error();
return 0;
}
IMLSegment* InsertSegment(size_t index)
{
IMLSegment* newSeg = new IMLSegment();
segmentList2.insert(segmentList2.begin() + index, 1, newSeg);
return newSeg;
}
std::span<IMLSegment*> InsertSegments(size_t index, size_t count)
{
segmentList2.insert(segmentList2.begin() + index, count, {});
for (size_t i = index; i < (index + count); i++)
segmentList2[i] = new IMLSegment();
return { segmentList2.data() + index, count};
}
void UpdateSegmentIndices()
{
for (size_t i = 0; i < segmentList2.size(); i++)
segmentList2[i]->momentaryIndex = (sint32)i;
}
};
typedef void ATTR_MS_ABI (*PPCREC_JUMP_ENTRY)();
@ -385,8 +162,6 @@ extern void ATTR_MS_ABI (*PPCRecompiler_leaveRecompilerCode_unvisited)();
#define PPC_REC_INVALID_FUNCTION ((PPCRecFunction_t*)-1)
// todo - move some of the stuff above into PPCRecompilerInternal.h
// recompiler interface
void PPCRecompiler_recompileIfUnvisited(uint32 enterAddress);