mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-24 03:00:48 +02:00
Merge pull request #182 from Sonicadvance1/IRLoader
IR Loader unit tests and surrounding support!
This commit is contained in:
commit
4e19bea7ae
34 files changed
+1329
-1059
No files matched your search
@@ -45,9 +45,14 @@ jobs:
|
||||
# Execute the build. You can specify a specific target with "--target <NAME>"
|
||||
run: cmake --build . --config $BUILD_TYPE
|
||||
|
||||
- name: Tests
|
||||
|
||||
- name: ASM Tests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
# Execute the unit tests
|
||||
run: ctest -j`nproc`
|
||||
run: cmake --build . --config $BUILD_TYPE --target asm_tests
|
||||
|
||||
- name: IR Tests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
# Execute the unit tests
|
||||
run: cmake --build . --config $BUILD_TYPE --target ir_tests
|
||||
+1
@@ -97,6 +97,7 @@ set (SRCS
|
||||
Interface/Memory/MemMapper.cpp
|
||||
Interface/Memory/SharedMem.cpp
|
||||
Interface/IR/IR.cpp
|
||||
Interface/IR/IREmitter.cpp
|
||||
Interface/IR/PassManager.cpp
|
||||
Interface/IR/Passes/ConstProp.cpp
|
||||
Interface/IR/Passes/DeadCodeElimination.cpp
|
||||
|
||||
+9
-3
@@ -128,6 +128,10 @@ namespace FEXCore::Context {
|
||||
: FrontendDecoder {this}
|
||||
, SyscallHandler {FEXCore::CreateHandler(OperatingMode::MODE_64BIT, this)} {
|
||||
FallbackCPUFactory = FEXCore::Core::DefaultFallbackCore::CPUCreationFactory;
|
||||
PassManager.RegisterExitHandler([this]() {
|
||||
ShouldStop = true;
|
||||
});
|
||||
PassManager.RegisterSyscallHandler(SyscallHandler.get());
|
||||
PassManager.AddDefaultPasses();
|
||||
PassManager.AddDefaultValidationPasses();
|
||||
#ifdef BLOCKSTATS
|
||||
@@ -381,8 +385,10 @@ namespace FEXCore::Context {
|
||||
Thread->CPUBackend->Initialize();
|
||||
Thread->FallbackBackend->Initialize();
|
||||
|
||||
auto IRHandler = [Thread](uint64_t Addr, FEXCore::IR::IRListView<true> *IR) -> void {
|
||||
Thread->IRLists.try_emplace(Addr, IR);
|
||||
auto IRHandler = [this, Thread](uint64_t Addr, IR::IREmitter *IR) -> void {
|
||||
// Run the passmanager over the IR from the dispatcher
|
||||
PassManager.Run(IR);
|
||||
Thread->IRLists.try_emplace(Addr, IR->CreateIRCopy());
|
||||
};
|
||||
|
||||
LocalLoader->AddIR(IRHandler);
|
||||
@@ -447,7 +453,7 @@ namespace FEXCore::Context {
|
||||
IR::RegisterAllocationPass *Context::GetRegisterAllocatorPass() {
|
||||
if (!RAPass) {
|
||||
RAPass = IR::CreateRegisterAllocationPass();
|
||||
PassManager.InsertPass(RAPass);
|
||||
PassManager.InsertRAPass(RAPass);
|
||||
}
|
||||
|
||||
return RAPass;
|
||||
|
||||
@@ -4065,7 +4065,9 @@ void* FEXCore::CPU::LLVMJITCore::CompileCode(FEXCore::IR::IRListView<true> const
|
||||
|
||||
JITState.Functions.emplace_back(Engine);
|
||||
|
||||
DebugData->HostCodeSize = JITState.MemManager->GetLastCodeAllocation();
|
||||
if (DebugData) {
|
||||
DebugData->HostCodeSize = JITState.MemManager->GetLastCodeAllocation();
|
||||
}
|
||||
void *FunctionPtr = reinterpret_cast<void*>(Engine->getFunctionAddress(FunctionName.str()));
|
||||
|
||||
return FunctionPtr;
|
||||
|
||||
+2
-91
@@ -3314,33 +3314,6 @@ void OpDispatchBuilder::CMPXCHGPairOp(OpcodeArgs) {
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_ZF_LOC>(ZFResult);
|
||||
}
|
||||
|
||||
OpDispatchBuilder::IRPair<IROp_CodeBlock> OpDispatchBuilder::CreateNewCodeBlock() {
|
||||
auto OldCursor = GetWriteCursor();
|
||||
SetWriteCursor(CodeBlocks.back());
|
||||
|
||||
auto CodeNode = CreateCodeNode();
|
||||
|
||||
auto NewNode = _Dummy();
|
||||
SetCodeNodeBegin(CodeNode, NewNode);
|
||||
|
||||
auto EndBlock = _EndBlock(0);
|
||||
SetCodeNodeLast(CodeNode, EndBlock);
|
||||
|
||||
if (CurrentCodeBlock) {
|
||||
LinkCodeBlocks(CurrentCodeBlock, CodeNode);
|
||||
}
|
||||
|
||||
SetWriteCursor(OldCursor);
|
||||
|
||||
return CodeNode;
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::SetCurrentCodeBlock(OrderedNode *Node) {
|
||||
CurrentCodeBlock = Node;
|
||||
LogMan::Throw::A(Node->Op(Data.Begin())->Op == OP_CODEBLOCK, "Node wasn't codeblock. It was '%s'", std::string(IR::GetName(Node->Op(Data.Begin())->Op)).c_str());
|
||||
SetWriteCursor(Node->Op(Data.Begin())->CW<IROp_CodeBlock>()->Begin.GetNode(ListData.Begin()));
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::CreateJumpBlocks(std::vector<FEXCore::Frontend::Decoder::DecodedBlocks> const *Blocks) {
|
||||
OrderedNode *PrevCodeBlock{};
|
||||
for (auto &Target : *Blocks) {
|
||||
@@ -3387,7 +3360,6 @@ void OpDispatchBuilder::Finalize() {
|
||||
_StoreContext(GPRClass, 8, offsetof(FEXCore::Core::CPUState, rip), _Constant(Handler.first));
|
||||
_ExitFunction();
|
||||
}
|
||||
CodeBlocks.clear();
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::ExitFunction() {
|
||||
@@ -3657,24 +3629,16 @@ void OpDispatchBuilder::StoreResult(FEXCore::IR::RegisterClassType Class, FEXCor
|
||||
}
|
||||
|
||||
OpDispatchBuilder::OpDispatchBuilder(FEXCore::Context::Context *ctx)
|
||||
: CTX {ctx}
|
||||
, Data {8 * 1024 * 1024}
|
||||
, ListData {8 * 1024 * 1024} {
|
||||
: CTX {ctx} {
|
||||
ResetWorkingList();
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::ResetWorkingList() {
|
||||
Data.Reset();
|
||||
ListData.Reset();
|
||||
CodeBlocks.clear();
|
||||
IREmitter::ResetWorkingList();
|
||||
JumpTargets.clear();
|
||||
BlockSetRIP = false;
|
||||
CurrentWriteCursor = nullptr;
|
||||
// This is necessary since we do "null" pointer checks
|
||||
InvalidNode = reinterpret_cast<OrderedNode*>(ListData.Allocate(sizeof(OrderedNode)));
|
||||
DecodeFailure = false;
|
||||
ShouldDump = false;
|
||||
CurrentCodeBlock = nullptr;
|
||||
}
|
||||
|
||||
template<unsigned BitOffset>
|
||||
@@ -5564,59 +5528,6 @@ void OpDispatchBuilder::UnimplementedOp(OpcodeArgs) {
|
||||
|
||||
#undef OpcodeArgs
|
||||
|
||||
void OpDispatchBuilder::ReplaceAllUsesWithInclusive(OrderedNode *Node, OrderedNode *NewNode, IR::NodeWrapperIterator After, IR::NodeWrapperIterator End) {
|
||||
uintptr_t ListBegin = ListData.Begin();
|
||||
uintptr_t DataBegin = Data.Begin();
|
||||
|
||||
while (After != End) {
|
||||
OrderedNodeWrapper *WrapperOp = After();
|
||||
OrderedNode *RealNode = WrapperOp->GetNode(ListBegin);
|
||||
FEXCore::IR::IROp_Header *IROp = RealNode->Op(DataBegin);
|
||||
|
||||
uint8_t NumArgs = IR::GetArgs(IROp->Op);
|
||||
for (uint8_t i = 0; i < NumArgs; ++i) {
|
||||
if (IROp->Args[i].ID() == Node->Wrapped(ListBegin).ID()) {
|
||||
Node->RemoveUse();
|
||||
NewNode->AddUse();
|
||||
IROp->Args[i].NodeOffset = NewNode->Wrapped(ListBegin).NodeOffset;
|
||||
}
|
||||
}
|
||||
|
||||
++After;
|
||||
}
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::ReplaceNodeArgument(OrderedNode *Node, uint8_t Arg, OrderedNode *NewArg) {
|
||||
uintptr_t ListBegin = ListData.Begin();
|
||||
uintptr_t DataBegin = Data.Begin();
|
||||
|
||||
FEXCore::IR::IROp_Header *IROp = Node->Op(DataBegin);
|
||||
OrderedNodeWrapper OldArgWrapper = IROp->Args[Arg];
|
||||
OrderedNode *OldArg = OldArgWrapper.GetNode(ListBegin);
|
||||
OldArg->RemoveUse();
|
||||
NewArg->AddUse();
|
||||
IROp->Args[Arg].NodeOffset = NewArg->Wrapped(ListBegin).NodeOffset;
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::RemoveArgUses(OrderedNode *Node) {
|
||||
uintptr_t ListBegin = ListData.Begin();
|
||||
uintptr_t DataBegin = Data.Begin();
|
||||
|
||||
FEXCore::IR::IROp_Header *IROp = Node->Op(DataBegin);
|
||||
|
||||
uint8_t NumArgs = IR::GetArgs(IROp->Op);
|
||||
for (uint8_t i = 0; i < NumArgs; ++i) {
|
||||
auto ArgNode = IROp->Args[i].GetNode(ListBegin);
|
||||
ArgNode->RemoveUse();
|
||||
}
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::Remove(OrderedNode *Node) {
|
||||
RemoveArgUses(Node);
|
||||
|
||||
Node->Unlink(ListData.Begin());
|
||||
}
|
||||
|
||||
void InstallOpcodeHandlers() {
|
||||
const std::vector<std::tuple<uint8_t, uint8_t, X86Tables::OpDispatchPtr>> BaseOpTable = {
|
||||
// Instructions
|
||||
|
||||
+2
-495
@@ -6,6 +6,7 @@
|
||||
#include <FEXCore/Debug/X86Tables.h>
|
||||
#include <FEXCore/IR/IntrusiveIRList.h>
|
||||
#include <FEXCore/IR/IR.h>
|
||||
#include <FEXCore/IR/IREmitter.h>
|
||||
|
||||
#include "LogManager.h"
|
||||
|
||||
@@ -18,7 +19,7 @@ namespace FEXCore::IR {
|
||||
class Pass;
|
||||
class PassManager;
|
||||
|
||||
class OpDispatchBuilder final {
|
||||
class OpDispatchBuilder final : public IREmitter {
|
||||
friend class FEXCore::IR::Pass;
|
||||
friend class FEXCore::IR::PassManager;
|
||||
public:
|
||||
@@ -52,7 +53,6 @@ public:
|
||||
}
|
||||
|
||||
bool FinishOp(uint64_t NextRIP, bool LastOp) {
|
||||
|
||||
// If we are switching to a new block and this current block has yet to set a RIP
|
||||
// Then we need to insert an unconditional jump from the current block to the one we are going to
|
||||
// This happens most frequently when an instruction jumps backwards to another location
|
||||
@@ -83,8 +83,6 @@ public:
|
||||
|
||||
OpDispatchBuilder(FEXCore::Context::Context *ctx);
|
||||
|
||||
IRListView<false> ViewIR() { return IRListView<false>(&Data, &ListData); }
|
||||
IRListView<true> *CreateIRCopy() { return new IRListView<true>(&Data, &ListData); }
|
||||
void ResetWorkingList();
|
||||
bool HadDecodeFailure() { return DecodeFailure; }
|
||||
|
||||
@@ -337,468 +335,13 @@ public:
|
||||
|
||||
#undef OpcodeArgs
|
||||
|
||||
/**
|
||||
* @name IR allocation routines
|
||||
*
|
||||
* @{ */
|
||||
|
||||
// These handlers add cost to the constructor and destructor
|
||||
// If it becomes an issue then blow them away
|
||||
// GCC also generates some pretty atrocious code around these
|
||||
// Use Clang!
|
||||
#define IROP_ALLOCATE_HELPERS
|
||||
#define IROP_DISPATCH_HELPERS
|
||||
#include <FEXCore/IR/IRDefines.inc>
|
||||
|
||||
IRPair<IROp_Constant> _Constant(uint8_t Size, uint64_t Constant) {
|
||||
auto Op = AllocateOp<IROp_Constant, IROps::OP_CONSTANT>();
|
||||
Op.first->Constant = Constant;
|
||||
Op.first->Header.Size = Size / 8;
|
||||
Op.first->Header.ElementSize = Size / 8;
|
||||
Op.first->Header.NumArgs = 0;
|
||||
Op.first->Header.HasDest = true;
|
||||
return Op;
|
||||
}
|
||||
IRPair<IROp_VBitcast> _VBitcast(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
auto Op = AllocateOp<IROp_VBitcast, IROps::OP_VBITCAST>();
|
||||
Op.first->Header.Size = RegisterSize / 8;
|
||||
Op.first->Header.ElementSize = ElementSize;
|
||||
Op.first->Header.NumArgs = 1;
|
||||
Op.first->Header.HasDest = true;
|
||||
Op.first->Header.Args[0] = ssa0->Wrapped(ListData.Begin());
|
||||
ssa0->AddUse();
|
||||
return Op;
|
||||
}
|
||||
IRPair<IROp_LoadContext> _LoadContext(uint8_t Size, uint32_t Offset, RegisterClassType Class) {
|
||||
return _LoadContext(Offset, Class, Size);
|
||||
}
|
||||
IRPair<IROp_StoreContext> _StoreContext(RegisterClassType Class, uint8_t Size, uint32_t Offset, OrderedNode *ssa0) {
|
||||
return _StoreContext(ssa0, Offset, Class, Size);
|
||||
}
|
||||
IRPair<IROp_Bfe> _Bfe(uint8_t Width, uint8_t lsb, OrderedNode *ssa0) {
|
||||
return _Bfe(ssa0, Width, lsb);
|
||||
}
|
||||
IRPair<IROp_Bfi> _Bfi(uint8_t Width, uint8_t lsb, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _Bfi(ssa0, ssa1, Width, lsb);
|
||||
}
|
||||
IRPair<IROp_StoreMem> _StoreMem(FEXCore::IR::RegisterClassType Class, uint8_t Size, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t Align = 1) {
|
||||
return _StoreMem(ssa0, ssa1, Size, Align, Class);
|
||||
}
|
||||
IRPair<IROp_LoadMem> _LoadMem(FEXCore::IR::RegisterClassType Class, uint8_t Size, OrderedNode *ssa0, uint8_t Align = 1) {
|
||||
return _LoadMem(ssa0, Size, Align, Class);
|
||||
}
|
||||
IRPair<IROp_Select> _Select(uint8_t Cond, OrderedNode *ssa0, OrderedNode *ssa1, OrderedNode *ssa2, OrderedNode *ssa3) {
|
||||
return _Select(ssa0, ssa1, ssa2, ssa3, {Cond});
|
||||
}
|
||||
IRPair<IROp_Sext> _Sext(uint8_t SrcSize, OrderedNode *ssa0) {
|
||||
return _Sext(ssa0, SrcSize);
|
||||
}
|
||||
IRPair<IROp_Zext> _Zext(uint8_t SrcSize, OrderedNode *ssa0) {
|
||||
return _Zext(ssa0, SrcSize);
|
||||
}
|
||||
IRPair<IROp_VInsElement> _VInsElement(uint8_t RegisterSize, uint8_t ElementSize, uint8_t DestIdx, uint8_t SrcIdx, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VInsElement(ssa0, ssa1, DestIdx, SrcIdx, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VInsScalarElement> _VInsScalarElement(uint8_t RegisterSize, uint8_t ElementSize, uint8_t DestIdx, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VInsScalarElement(ssa0, ssa1, DestIdx, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VExtractElement> _VExtractElement(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t Index) {
|
||||
return _VExtractElement(ssa0, Index, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VAnd> _VAnd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VAnd(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VAdd> _VAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VAdd(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSub> _VSub(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSub(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUQAdd> _VUQAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUQAdd(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUQSub> _VUQSub(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUQSub(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSQAdd> _VSQAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSQAdd(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSQSub> _VSQSub(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSQSub(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VAddP> _VAddP(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VAddP(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUMin> _VUMin(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUMin(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSMin> _VSMin(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSMin(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUMax> _VUMax(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUMax(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSMax> _VSMax(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSMax(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VZip> _VZip(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VZip(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VZip2> _VZip2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VZip2(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VCMPEQ> _VCMPEQ(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VCMPEQ(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VCMPGT> _VCMPGT(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VCMPGT(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFAdd> _VFAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFAdd(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFAddP> _VFAddP(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFAddP(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFCMPEQ> _VFCMPEQ(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFCMPEQ(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFCMPNEQ> _VFCMPNEQ(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFCMPNEQ(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFCMPLT> _VFCMPLT(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFCMPLT(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFCMPGT> _VFCMPGT(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFCMPGT(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFCMPLE> _VFCMPLE(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFCMPLE(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFCMPUNO> _VFCMPUNO(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFCMPUNO(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFCMPORD> _VFCMPORD(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFCMPORD(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUShl> _VUShl(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUShl(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUShlS> _VUShlS(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUShlS(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUShrS> _VUShrS(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUShrS(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSShrS> _VSShrS(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSShrS(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUShr> _VUShr(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUShr(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSShr> _VSShr(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSShr(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VExtr> _VExtr(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t Index) {
|
||||
return _VExtr(ssa0, ssa1, Index, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSLI> _VSLI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t ByteShift) {
|
||||
return _VSLI(ssa0, ByteShift, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSRI> _VSRI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t ByteShift) {
|
||||
return _VSRI(ssa0, ByteShift, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUShrI> _VUShrI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) {
|
||||
return _VUShrI(ssa0, BitShift, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSShrI> _VSShrI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) {
|
||||
return _VSShrI(ssa0, BitShift, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUShrNI> _VUShrNI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) {
|
||||
return _VUShrNI(ssa0, BitShift, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUShrNI2> _VUShrNI2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t BitShift) {
|
||||
return _VUShrNI2(ssa0, ssa1, BitShift, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VShlI> _VShlI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) {
|
||||
return _VShlI(ssa0, BitShift, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFSqrt> _VFSqrt(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VFSqrt(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFRSqrt> _VFRSqrt(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VFRSqrt(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VNeg> _VNeg(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VNeg(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFNeg> _VFNeg(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VFNeg(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VNot> _VNot(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VNot(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSQXTN> _VSQXTN(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VSQXTN(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSQXTN2> _VSQXTN2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSQXTN2(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSQXTUN> _VSQXTUN(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VSQXTUN(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSQXTUN2> _VSQXTUN2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSQXTUN2(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VCastFromGPR> _VCastFromGPR(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VCastFromGPR(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VExtractToGPR> _VExtractToGPR(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t Index) {
|
||||
return _VExtractToGPR(ssa0, Index, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VInsGPR> _VInsGPR(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t Index) {
|
||||
return _VInsGPR(ssa0, ssa1, Index, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_Vector_FToF> _Vector_FToF(uint8_t RegisterSize, uint8_t DstElementSize, uint8_t SrcElementSize, OrderedNode *ssa0) {
|
||||
return _Vector_FToF(ssa0, SrcElementSize, RegisterSize, DstElementSize);
|
||||
}
|
||||
IRPair<IROp_Float_FToF> _Float_FToF(uint8_t DstElementSize, uint8_t SrcElementSize, OrderedNode *ssa0) {
|
||||
return _Float_FToF(ssa0, SrcElementSize, DstElementSize);
|
||||
}
|
||||
IRPair<IROp_VUMul> _VUMul(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUMul(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSMul> _VSMul(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSMul(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUMull> _VUMull(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUMull(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSMull> _VSMull(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSMull(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUMull2> _VUMull2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUMull2(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSMull2> _VSMull2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSMull2(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSXTL> _VSXTL(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VSXTL(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSXTL2> _VSXTL2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VSXTL2(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUXTL> _VUXTL(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VUXTL(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUXTL2> _VUXTL2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VUXTL2(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_Jump> _Jump() {
|
||||
return _Jump(InvalidNode);
|
||||
}
|
||||
IRPair<IROp_CondJump> _CondJump(OrderedNode *ssa0) {
|
||||
return _CondJump(ssa0, InvalidNode, InvalidNode);
|
||||
}
|
||||
|
||||
IRPair<IROp_Phi> _Phi() {
|
||||
return _Phi(InvalidNode, InvalidNode, 0);
|
||||
}
|
||||
|
||||
IRPair<IROp_PhiValue> _PhiValue(OrderedNode *Value, OrderedNode *Block) {
|
||||
return _PhiValue(Value, Block, InvalidNode);
|
||||
}
|
||||
|
||||
OrderedNode *Invalid() {
|
||||
return InvalidNode;
|
||||
}
|
||||
|
||||
void AddPhiValue(IR::IROp_Phi *Phi, OrderedNode *Value) {
|
||||
// Got to do some bookkeeping first
|
||||
Value->AddUse();
|
||||
auto ValueIROp = Value->Op(Data.Begin())->C<IR::IROp_PhiValue>()->Value.GetNode(ListData.Begin())->Op(Data.Begin());
|
||||
Phi->Header.Size = ValueIROp->Size;
|
||||
Phi->Header.ElementSize = ValueIROp->ElementSize;
|
||||
|
||||
if (!Phi->PhiBegin.ID()) {
|
||||
Phi->PhiBegin = Phi->PhiEnd = Value->Wrapped(ListData.Begin());
|
||||
return;
|
||||
}
|
||||
auto PhiValueEndNode = Phi->PhiEnd.GetNode(ListData.Begin());
|
||||
auto PhiValueEndOp = PhiValueEndNode->Op(Data.Begin())->CW<IR::IROp_PhiValue>();
|
||||
PhiValueEndOp->Next = Value->Wrapped(ListData.Begin());
|
||||
}
|
||||
|
||||
void SetJumpTarget(IR::IROp_Jump *Op, OrderedNode *Target) {
|
||||
LogMan::Throw::A(Target->Op(Data.Begin())->Op == OP_CODEBLOCK,
|
||||
"Tried setting Jump target to %%ssa%d %s",
|
||||
Target->Wrapped(ListData.Begin()).ID(),
|
||||
std::string(IR::GetName(Target->Op(Data.Begin())->Op)).c_str());
|
||||
|
||||
Op->Header.Args[0].NodeOffset = Target->Wrapped(ListData.Begin()).NodeOffset;
|
||||
}
|
||||
void SetTrueJumpTarget(IR::IROp_CondJump *Op, OrderedNode *Target) {
|
||||
LogMan::Throw::A(Target->Op(Data.Begin())->Op == OP_CODEBLOCK,
|
||||
"Tried setting CondJump target to %%ssa%d %s",
|
||||
Target->Wrapped(ListData.Begin()).ID(),
|
||||
std::string(IR::GetName(Target->Op(Data.Begin())->Op)).c_str());
|
||||
|
||||
Op->Header.Args[1].NodeOffset = Target->Wrapped(ListData.Begin()).NodeOffset;
|
||||
}
|
||||
void SetFalseJumpTarget(IR::IROp_CondJump *Op, OrderedNode *Target) {
|
||||
LogMan::Throw::A(Target->Op(Data.Begin())->Op == OP_CODEBLOCK,
|
||||
"Tried setting CondJump target to %%ssa%d %s",
|
||||
Target->Wrapped(ListData.Begin()).ID(),
|
||||
std::string(IR::GetName(Target->Op(Data.Begin())->Op)).c_str());
|
||||
|
||||
Op->Header.Args[2].NodeOffset = Target->Wrapped(ListData.Begin()).NodeOffset;
|
||||
}
|
||||
|
||||
void SetJumpTarget(IRPair<IROp_Jump> Op, OrderedNode *Target) {
|
||||
LogMan::Throw::A(Target->Op(Data.Begin())->Op == OP_CODEBLOCK,
|
||||
"Tried setting Jump target to %%ssa%d %s",
|
||||
Target->Wrapped(ListData.Begin()).ID(),
|
||||
std::string(IR::GetName(Target->Op(Data.Begin())->Op)).c_str());
|
||||
|
||||
Op.first->Header.Args[0].NodeOffset = Target->Wrapped(ListData.Begin()).NodeOffset;
|
||||
}
|
||||
void SetTrueJumpTarget(IRPair<IROp_CondJump> Op, OrderedNode *Target) {
|
||||
LogMan::Throw::A(Target->Op(Data.Begin())->Op == OP_CODEBLOCK,
|
||||
"Tried setting CondJump target to %%ssa%d %s",
|
||||
Target->Wrapped(ListData.Begin()).ID(),
|
||||
std::string(IR::GetName(Target->Op(Data.Begin())->Op)).c_str());
|
||||
Op.first->Header.Args[1].NodeOffset = Target->Wrapped(ListData.Begin()).NodeOffset;
|
||||
}
|
||||
void SetFalseJumpTarget(IRPair<IROp_CondJump> Op, OrderedNode *Target) {
|
||||
LogMan::Throw::A(Target->Op(Data.Begin())->Op == OP_CODEBLOCK,
|
||||
"Tried setting CondJump target to %%ssa%d %s",
|
||||
Target->Wrapped(ListData.Begin()).ID(),
|
||||
std::string(IR::GetName(Target->Op(Data.Begin())->Op)).c_str());
|
||||
Op.first->Header.Args[2].NodeOffset = Target->Wrapped(ListData.Begin()).NodeOffset;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
bool IsValueConstant(OrderedNodeWrapper ssa, uint64_t *Constant) {
|
||||
OrderedNode *RealNode = ssa.GetNode(ListData.Begin());
|
||||
FEXCore::IR::IROp_Header *IROp = RealNode->Op(Data.Begin());
|
||||
if (IROp->Op == OP_CONSTANT) {
|
||||
auto Op = IROp->C<IR::IROp_Constant>();
|
||||
*Constant = Op->Constant;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is fairly special in how it operates
|
||||
// Since the node is decoupled from the backing op then we can swap out the backing op without much overhead
|
||||
// This can potentially cause problems where multiple nodes are pointing to the same IROp
|
||||
OrderedNode *ReplaceAllUsesWith(OrderedNode *Node, IROp_Header *Op) {
|
||||
RemoveArgUses(Node);
|
||||
Node->Header.Value.SetOffset(Data.Begin(), reinterpret_cast<uintptr_t>(Op));
|
||||
return Node;
|
||||
}
|
||||
|
||||
// This is similar to the previous op except that we pass in a node
|
||||
// This takes the op backing in the new node and replaces the node in the other node
|
||||
// Again can cause problems where things are pointing to NewNode and haven't been decoupled
|
||||
OrderedNode *ReplaceAllUsesWith(OrderedNode *Node, OrderedNode *NewNode) {
|
||||
RemoveArgUses(Node);
|
||||
Node->Header.Value.NodeOffset = NewNode->Header.Value.NodeOffset;
|
||||
return Node;
|
||||
}
|
||||
|
||||
void ReplaceAllUsesWithInclusive(OrderedNode *Node, OrderedNode *NewNode, IR::NodeWrapperIterator After, IR::NodeWrapperIterator End);
|
||||
void ReplaceNodeArgument(OrderedNode *Node, uint8_t Arg, OrderedNode *NewArg);
|
||||
|
||||
void Remove(OrderedNode *Node);
|
||||
|
||||
void SetPackedRFLAG(bool Lower8, OrderedNode *Src);
|
||||
OrderedNode *GetPackedRFLAG(bool Lower8);
|
||||
|
||||
void CopyData(OpDispatchBuilder const &rhs) {
|
||||
LogMan::Throw::A(rhs.Data.BackingSize() <= Data.BackingSize(), "Trying to take ownership of data that is too large");
|
||||
LogMan::Throw::A(rhs.ListData.BackingSize() <= ListData.BackingSize(), "Trying to take ownership of data that is too large");
|
||||
Data.CopyData(rhs.Data);
|
||||
ListData.CopyData(rhs.ListData);
|
||||
InvalidNode = rhs.InvalidNode;
|
||||
CurrentWriteCursor = rhs.CurrentWriteCursor;
|
||||
CodeBlocks = rhs.CodeBlocks;
|
||||
}
|
||||
|
||||
void SetWriteCursor(OrderedNode *Node) {
|
||||
CurrentWriteCursor = Node;
|
||||
}
|
||||
|
||||
OrderedNode *GetWriteCursor() {
|
||||
return CurrentWriteCursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This creates an orphaned code node
|
||||
* The IROp backing is in the correct list but the OrderedNode lives outside of the list
|
||||
*
|
||||
* XXX: This is because we don't want code blocks to interleave with current instruction IR ops currently
|
||||
* We can change this behaviour once we remove the old BeginBlock/EndBlock types
|
||||
*
|
||||
* @return OrderedNode
|
||||
*/
|
||||
IRPair<IROp_CodeBlock> CreateCodeNode() {
|
||||
auto CodeNode = _CodeBlock(InvalidNode, InvalidNode, InvalidNode);
|
||||
CodeBlocks.emplace_back(CodeNode);
|
||||
return CodeNode;
|
||||
}
|
||||
|
||||
void SetCodeNodeBegin(OrderedNode *CodeNode, OrderedNode *Begin) {
|
||||
FEXCore::IR::IROp_CodeBlock *IROp = CodeNode->Op(Data.Begin())->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
LogMan::Throw::A(IROp->Header.Op == IROps::OP_CODEBLOCK, "Invalid");
|
||||
IROp->Begin = Begin->Wrapped(ListData.Begin());
|
||||
}
|
||||
|
||||
void SetCodeNodeLast(OrderedNode *CodeNode, OrderedNode *Last) {
|
||||
FEXCore::IR::IROp_CodeBlock *IROp = CodeNode->Op(Data.Begin())->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
LogMan::Throw::A(IROp->Header.Op == IROps::OP_CODEBLOCK, "Invalid");
|
||||
IROp->Last = Last->Wrapped(ListData.Begin());
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Links codeblocks together
|
||||
* Codeblocks are singly linked so we need to walk the list forward if the linked block isn't isn't the last
|
||||
*
|
||||
* eq.
|
||||
* CodeNode->Next -> Next
|
||||
* to
|
||||
* CodeNode->Next -> New -> Next
|
||||
*
|
||||
* @{ */
|
||||
/** @} */
|
||||
void LinkCodeBlocks(OrderedNode *CodeNode, OrderedNode *Next) {
|
||||
FEXCore::IR::IROp_CodeBlock *CurrentIROp = CodeNode->Op(Data.Begin())->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
LogMan::Throw::A(CurrentIROp->Header.Op == IROps::OP_CODEBLOCK, "Invalid");
|
||||
|
||||
OrderedNodeWrapper OldNext = CurrentIROp->Next;
|
||||
// First thing is to assign CodeNode->Next to the incoming node
|
||||
{
|
||||
CurrentIROp->Next = Next->Wrapped(ListData.Begin());
|
||||
}
|
||||
|
||||
// Second thing is to assign the incoming node's Next to what was in CodeNode->Next
|
||||
{
|
||||
FEXCore::IR::IROp_CodeBlock *NextIROp = Next->Op(Data.Begin())->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
auto NewOldNext = NextIROp->Next;
|
||||
NextIROp->Next = OldNext;
|
||||
OldNext = NewOldNext;
|
||||
}
|
||||
}
|
||||
|
||||
IRPair<IROp_CodeBlock> CreateNewCodeBlock();
|
||||
void SetCurrentCodeBlock(OrderedNode *Node);
|
||||
|
||||
void SetMultiblock(bool _Multiblock) { Multiblock = _Multiblock; }
|
||||
bool GetMultiblock() { return Multiblock; }
|
||||
|
||||
private:
|
||||
void RemoveArgUses(OrderedNode *Node);
|
||||
bool DecodeFailure{false};
|
||||
|
||||
OrderedNode *LoadSource(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp const& Op, FEXCore::X86Tables::DecodedOperand const& Operand, uint32_t Flags, int8_t Align, bool LoadData = true, bool ForceLoad = false);
|
||||
@@ -836,33 +379,6 @@ private:
|
||||
OrderedNode * GetX87Top();
|
||||
void SetX87Top(OrderedNode *Value);
|
||||
|
||||
OrderedNode *CreateNode(IROp_Header *Op) {
|
||||
uintptr_t ListBegin = ListData.Begin();
|
||||
size_t Size = sizeof(OrderedNode);
|
||||
void *Ptr = ListData.Allocate(Size);
|
||||
OrderedNode *Node = new (Ptr) OrderedNode();
|
||||
Node->Header.Value.SetOffset(Data.Begin(), reinterpret_cast<uintptr_t>(Op));
|
||||
|
||||
if (CurrentWriteCursor) {
|
||||
CurrentWriteCursor->append(ListBegin, Node);
|
||||
}
|
||||
CurrentWriteCursor = Node;
|
||||
return Node;
|
||||
}
|
||||
|
||||
OrderedNode *GetNode(uint32_t SSANode) {
|
||||
uintptr_t ListBegin = ListData.Begin();
|
||||
OrderedNode *Node = reinterpret_cast<OrderedNode *>(ListBegin + SSANode * sizeof(OrderedNode));
|
||||
return Node;
|
||||
}
|
||||
|
||||
OrderedNode *EmplaceOrphanedNode(OrderedNode *OldNode) {
|
||||
size_t Size = sizeof(OrderedNode);
|
||||
OrderedNode *Ptr = reinterpret_cast<OrderedNode*>(ListData.Allocate(Size));
|
||||
memcpy(Ptr, OldNode, Size);
|
||||
return Ptr;
|
||||
}
|
||||
|
||||
bool DestIsLockedMem(FEXCore::X86Tables::DecodedOp Op) {
|
||||
return Op->Dest.TypeNone.Type !=FEXCore::X86Tables::DecodedOperand::TYPE_GPR && (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_LOCK);
|
||||
}
|
||||
@@ -870,15 +386,6 @@ private:
|
||||
void CreateJumpBlocks(std::vector<FEXCore::Frontend::Decoder::DecodedBlocks> const *Blocks);
|
||||
bool BlockSetRIP {false};
|
||||
|
||||
OrderedNode *CurrentWriteCursor = nullptr;
|
||||
|
||||
// These could be combined with a little bit of work to be more efficient with memory usage. Isn't a big deal
|
||||
IntrusiveAllocator Data;
|
||||
IntrusiveAllocator ListData;
|
||||
|
||||
OrderedNode *InvalidNode;
|
||||
OrderedNode *CurrentCodeBlock{};
|
||||
std::vector<OrderedNode*> CodeBlocks;
|
||||
bool Multiblock{};
|
||||
uint64_t Entry;
|
||||
};
|
||||
|
||||
+64
-2
@@ -280,7 +280,10 @@
|
||||
"HasDest": true,
|
||||
"DestClass": "GPR",
|
||||
"DestSize": "GetOpSize(ssa0)",
|
||||
"SSAArgs": "1"
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Value"
|
||||
]
|
||||
},
|
||||
|
||||
"CycleCounter": {
|
||||
@@ -300,6 +303,9 @@
|
||||
},
|
||||
|
||||
"LoadContext": {
|
||||
"Desc": ["Loads a value from the context with offset",
|
||||
"Dest = Ctx[Offset]"
|
||||
],
|
||||
"OpClass": "Memory",
|
||||
"HasDest": true,
|
||||
"DestClass": "Complex",
|
||||
@@ -314,8 +320,14 @@
|
||||
},
|
||||
|
||||
"StoreContext": {
|
||||
"Desc": ["Stores a value to the context with offset",
|
||||
"Ctx[Offset] = Value"
|
||||
],
|
||||
"OpClass": "Memory",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Value"
|
||||
],
|
||||
"DestSize": "Size",
|
||||
"HelperArgs": [
|
||||
"uint8_t", "Size"
|
||||
@@ -327,8 +339,14 @@
|
||||
},
|
||||
|
||||
"LoadContextIndexed": {
|
||||
"Desc": ["Loads a value from the context with offset and indexed by SSA value",
|
||||
"Dest = Ctx[BaseOffset + Index * Stride]"
|
||||
],
|
||||
"OpClass": "Memory",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Index"
|
||||
],
|
||||
"HasDest": true,
|
||||
"DestClass": "Complex",
|
||||
"DestSize": "Size",
|
||||
@@ -341,8 +359,15 @@
|
||||
},
|
||||
|
||||
"StoreContextIndexed": {
|
||||
"Desc": ["Stores a value to the context with offset and indexed by SSA value",
|
||||
"Ctx[BaseOffset + Index * Stride] = Value"
|
||||
],
|
||||
"OpClass": "Memory",
|
||||
"SSAArgs": "2",
|
||||
"SSANames": [
|
||||
"Value",
|
||||
"Index"
|
||||
],
|
||||
"Args": [
|
||||
"uint8_t", "Size",
|
||||
"uint32_t", "BaseOffset",
|
||||
@@ -352,8 +377,15 @@
|
||||
},
|
||||
|
||||
"SpillRegister": {
|
||||
"Desc": ["Spills an SSA value to memory",
|
||||
"Spill slots are register allocated and has live ranges calculated to handle slot calculation",
|
||||
"```diff\n- !Don't use this op. It is for RA to handle spilling and filling!\n```"
|
||||
],
|
||||
"OpClass": "Memory",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Value"
|
||||
],
|
||||
"Args": [
|
||||
"uint32_t", "Slot",
|
||||
"RegisterClassType", "Class"
|
||||
@@ -361,6 +393,11 @@
|
||||
},
|
||||
|
||||
"FillRegister": {
|
||||
"Desc": ["Fills a register from a spill slot",
|
||||
"Spill slots are register allocated and has live ranges calculated to handle slot calculation",
|
||||
"```diff\n- !Don't use this op. It is for RA to handle spilling and filling!\n```"
|
||||
],
|
||||
|
||||
"OpClass": "Memory",
|
||||
"HasDest": true,
|
||||
"DestClass": "Complex",
|
||||
@@ -371,6 +408,9 @@
|
||||
},
|
||||
|
||||
"LoadFlag": {
|
||||
"Desc": ["Loads an x86-64 flag from the context object",
|
||||
"Specialized to allow flexible implementation of flag handling"
|
||||
],
|
||||
"OpClass": "Memory",
|
||||
"HasDest": true,
|
||||
"DestClass": "GPR",
|
||||
@@ -381,8 +421,14 @@
|
||||
},
|
||||
|
||||
"StoreFlag": {
|
||||
"Desc": ["Stores 1-bit of the flag in to the specified x86-64 flag",
|
||||
"Specialized to allow flexible implementation of flag handling"
|
||||
],
|
||||
"OpClass": "Memory",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Value"
|
||||
],
|
||||
"Args": [
|
||||
"uint32_t", "Flag"
|
||||
]
|
||||
@@ -393,7 +439,16 @@
|
||||
"HasDest": true,
|
||||
"DestClass": "GPR",
|
||||
"FixedDestSize": "8",
|
||||
"SSAArgs": "7"
|
||||
"SSAArgs": "7",
|
||||
"SSANames": [
|
||||
"SyscallID",
|
||||
"Arg0",
|
||||
"Arg1",
|
||||
"Arg2",
|
||||
"Arg3",
|
||||
"Arg4",
|
||||
"Arg5"
|
||||
]
|
||||
},
|
||||
|
||||
"LoadMem": {
|
||||
@@ -402,6 +457,9 @@
|
||||
"DestClass": "Complex",
|
||||
"DestSize": "Size",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Addr"
|
||||
],
|
||||
"Args": [
|
||||
"uint8_t", "Size",
|
||||
"uint8_t", "Align",
|
||||
@@ -412,6 +470,10 @@
|
||||
"StoreMem": {
|
||||
"OpClass": "Memory",
|
||||
"SSAArgs": "2",
|
||||
"SSANames": [
|
||||
"Addr",
|
||||
"Value"
|
||||
],
|
||||
"Args": [
|
||||
"uint8_t", "Size",
|
||||
"uint8_t", "Align",
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
#include <FEXCore/IR/IREmitter.h>
|
||||
|
||||
namespace FEXCore::IR {
|
||||
void IREmitter::ResetWorkingList() {
|
||||
Data.Reset();
|
||||
ListData.Reset();
|
||||
CodeBlocks.clear();
|
||||
CurrentWriteCursor = nullptr;
|
||||
// This is necessary since we do "null" pointer checks
|
||||
InvalidNode = reinterpret_cast<OrderedNode*>(ListData.Allocate(sizeof(OrderedNode)));
|
||||
CurrentCodeBlock = nullptr;
|
||||
}
|
||||
|
||||
void IREmitter::ReplaceAllUsesWithInclusive(OrderedNode *Node, OrderedNode *NewNode, IR::NodeWrapperIterator After, IR::NodeWrapperIterator End) {
|
||||
uintptr_t ListBegin = ListData.Begin();
|
||||
uintptr_t DataBegin = Data.Begin();
|
||||
|
||||
while (After != End) {
|
||||
OrderedNodeWrapper *WrapperOp = After();
|
||||
OrderedNode *RealNode = WrapperOp->GetNode(ListBegin);
|
||||
FEXCore::IR::IROp_Header *IROp = RealNode->Op(DataBegin);
|
||||
|
||||
uint8_t NumArgs = IR::GetArgs(IROp->Op);
|
||||
for (uint8_t i = 0; i < NumArgs; ++i) {
|
||||
if (IROp->Args[i].ID() == Node->Wrapped(ListBegin).ID()) {
|
||||
Node->RemoveUse();
|
||||
NewNode->AddUse();
|
||||
IROp->Args[i].NodeOffset = NewNode->Wrapped(ListBegin).NodeOffset;
|
||||
}
|
||||
}
|
||||
|
||||
++After;
|
||||
}
|
||||
}
|
||||
|
||||
void IREmitter::ReplaceNodeArgument(OrderedNode *Node, uint8_t Arg, OrderedNode *NewArg) {
|
||||
uintptr_t ListBegin = ListData.Begin();
|
||||
uintptr_t DataBegin = Data.Begin();
|
||||
|
||||
FEXCore::IR::IROp_Header *IROp = Node->Op(DataBegin);
|
||||
OrderedNodeWrapper OldArgWrapper = IROp->Args[Arg];
|
||||
OrderedNode *OldArg = OldArgWrapper.GetNode(ListBegin);
|
||||
OldArg->RemoveUse();
|
||||
NewArg->AddUse();
|
||||
IROp->Args[Arg].NodeOffset = NewArg->Wrapped(ListBegin).NodeOffset;
|
||||
}
|
||||
|
||||
void IREmitter::RemoveArgUses(OrderedNode *Node) {
|
||||
uintptr_t ListBegin = ListData.Begin();
|
||||
uintptr_t DataBegin = Data.Begin();
|
||||
|
||||
FEXCore::IR::IROp_Header *IROp = Node->Op(DataBegin);
|
||||
|
||||
uint8_t NumArgs = IR::GetArgs(IROp->Op);
|
||||
for (uint8_t i = 0; i < NumArgs; ++i) {
|
||||
auto ArgNode = IROp->Args[i].GetNode(ListBegin);
|
||||
ArgNode->RemoveUse();
|
||||
}
|
||||
}
|
||||
|
||||
void IREmitter::Remove(OrderedNode *Node) {
|
||||
RemoveArgUses(Node);
|
||||
|
||||
Node->Unlink(ListData.Begin());
|
||||
}
|
||||
|
||||
IREmitter::IRPair<IROp_CodeBlock> IREmitter::CreateNewCodeBlock() {
|
||||
auto OldCursor = GetWriteCursor();
|
||||
SetWriteCursor(CodeBlocks.back());
|
||||
|
||||
auto CodeNode = CreateCodeNode();
|
||||
|
||||
auto NewNode = _Dummy();
|
||||
SetCodeNodeBegin(CodeNode, NewNode);
|
||||
|
||||
auto EndBlock = _EndBlock(0);
|
||||
SetCodeNodeLast(CodeNode, EndBlock);
|
||||
|
||||
if (CurrentCodeBlock) {
|
||||
LinkCodeBlocks(CurrentCodeBlock, CodeNode);
|
||||
}
|
||||
|
||||
SetWriteCursor(OldCursor);
|
||||
|
||||
return CodeNode;
|
||||
}
|
||||
|
||||
void IREmitter::SetCurrentCodeBlock(OrderedNode *Node) {
|
||||
CurrentCodeBlock = Node;
|
||||
LogMan::Throw::A(Node->Op(Data.Begin())->Op == OP_CODEBLOCK, "Node wasn't codeblock. It was '%s'", std::string(IR::GetName(Node->Op(Data.Begin())->Op)).c_str());
|
||||
SetWriteCursor(Node->Op(Data.Begin())->CW<IROp_CodeBlock>()->Begin.GetNode(ListData.Begin()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-12
@@ -5,34 +5,34 @@
|
||||
namespace FEXCore::IR {
|
||||
|
||||
void PassManager::AddDefaultPasses() {
|
||||
Passes.emplace_back(std::unique_ptr<FEXCore::IR::Pass>(CreateContextLoadStoreElimination()));
|
||||
Passes.emplace_back(std::unique_ptr<FEXCore::IR::Pass>(CreateConstProp()));
|
||||
////// Passes.emplace_back(std::unique_ptr<FEXCore::IR::Pass>(CreateDeadFlagCalculationEliminination()));
|
||||
Passes.emplace_back(std::unique_ptr<FEXCore::IR::Pass>(CreateSyscallOptimization()));
|
||||
Passes.emplace_back(std::unique_ptr<FEXCore::IR::Pass>(CreatePassDeadCodeElimination()));
|
||||
InsertPass(CreateContextLoadStoreElimination());
|
||||
InsertPass(CreateConstProp());
|
||||
////// InsertPass(CreateDeadFlagCalculationEliminination());
|
||||
InsertPass(CreateSyscallOptimization());
|
||||
InsertPass(CreatePassDeadCodeElimination());
|
||||
|
||||
// If the IR is compacted post-RA then the node indexing gets messed up and the backend isn't able to find the register assigned to a node
|
||||
// Compact before IR, don't worry about RA generating spills/fills
|
||||
Passes.emplace_back(std::unique_ptr<FEXCore::IR::Pass>(CreateIRCompaction()));
|
||||
InsertPass(CreateIRCompaction());
|
||||
}
|
||||
|
||||
void PassManager::AddDefaultValidationPasses() {
|
||||
#ifndef NDEBUG
|
||||
ValidationPasses.emplace_back(std::unique_ptr<FEXCore::IR::Pass>(Validation::CreatePhiValidation()));
|
||||
ValidationPasses.emplace_back(std::unique_ptr<FEXCore::IR::Pass>(Validation::CreateIRValidation()));
|
||||
ValidationPasses.emplace_back(std::unique_ptr<FEXCore::IR::Pass>(Validation::CreateValueDominanceValidation()));
|
||||
InsertValidationPass(Validation::CreatePhiValidation());
|
||||
InsertValidationPass(Validation::CreateIRValidation());
|
||||
InsertValidationPass(Validation::CreateValueDominanceValidation());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool PassManager::Run(OpDispatchBuilder *Disp) {
|
||||
bool PassManager::Run(IREmitter *IREmit) {
|
||||
bool Changed = false;
|
||||
for (auto const &Pass : Passes) {
|
||||
Changed |= Pass->Run(Disp);
|
||||
Changed |= Pass->Run(IREmit);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (auto const &Pass : ValidationPasses) {
|
||||
Changed |= Pass->Run(Disp);
|
||||
Changed |= Pass->Run(IREmit);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+50
-2
@@ -1,32 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/IR/IntrusiveIRList.h>
|
||||
#include <FEXCore/IR/IREmitter.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace FEXCore {
|
||||
class SyscallHandler;
|
||||
}
|
||||
|
||||
namespace FEXCore::IR {
|
||||
class OpDispatchBuilder;
|
||||
class SyscallOptimization;
|
||||
|
||||
using ShouldExitHandler = std::function<void(void)>;
|
||||
|
||||
class Pass {
|
||||
public:
|
||||
virtual ~Pass() = default;
|
||||
virtual bool Run(OpDispatchBuilder *Disp) = 0;
|
||||
virtual bool Run(IREmitter *IREmit) = 0;
|
||||
|
||||
void RegisterPassManager(PassManager *_Manager) {
|
||||
Manager = _Manager;
|
||||
}
|
||||
|
||||
protected:
|
||||
PassManager *Manager;
|
||||
};
|
||||
|
||||
class PassManager final {
|
||||
friend class SyscallOptimization;
|
||||
public:
|
||||
void AddDefaultPasses();
|
||||
void AddDefaultValidationPasses();
|
||||
void InsertPass(Pass *Pass) {
|
||||
Pass->RegisterPassManager(this);
|
||||
Passes.emplace_back(Pass);
|
||||
}
|
||||
bool Run(OpDispatchBuilder *Disp);
|
||||
void InsertRAPass(Pass *Pass) {
|
||||
InsertPass(Pass);
|
||||
}
|
||||
|
||||
bool Run(IREmitter *IREmit);
|
||||
|
||||
void RegisterExitHandler(ShouldExitHandler Handler) {
|
||||
ExitHandler = Handler;
|
||||
}
|
||||
|
||||
bool HasRAPass() const {
|
||||
return RAPass != nullptr;
|
||||
}
|
||||
|
||||
IR::RegisterAllocationPass *GetRAPass() {
|
||||
return reinterpret_cast<IR::RegisterAllocationPass*>(RAPass);
|
||||
}
|
||||
|
||||
void RegisterSyscallHandler(SyscallHandler *Handler) {
|
||||
SyscallHandler = Handler;
|
||||
}
|
||||
|
||||
protected:
|
||||
ShouldExitHandler ExitHandler;
|
||||
SyscallHandler *SyscallHandler;
|
||||
|
||||
private:
|
||||
Pass *RAPass{};
|
||||
|
||||
std::vector<std::unique_ptr<Pass>> Passes;
|
||||
#ifndef NDEBUG
|
||||
std::vector<std::unique_ptr<Pass>> ValidationPasses;
|
||||
void InsertValidationPass(Pass *Pass) {
|
||||
Pass->RegisterPassManager(this);
|
||||
ValidationPasses.emplace_back(Pass);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
+59
-52
@@ -5,12 +5,12 @@ namespace FEXCore::IR {
|
||||
|
||||
class ConstProp final : public FEXCore::IR::Pass {
|
||||
public:
|
||||
bool Run(OpDispatchBuilder *Disp) override;
|
||||
bool Run(IREmitter *IREmit) override;
|
||||
};
|
||||
|
||||
bool ConstProp::Run(OpDispatchBuilder *Disp) {
|
||||
bool ConstProp::Run(IREmitter *IREmit) {
|
||||
bool Changed = false;
|
||||
auto CurrentIR = Disp->ViewIR();
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
uintptr_t ListBegin = CurrentIR.GetListData();
|
||||
uintptr_t DataBegin = CurrentIR.GetData();
|
||||
|
||||
@@ -23,7 +23,7 @@ bool ConstProp::Run(OpDispatchBuilder *Disp) {
|
||||
|
||||
OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin);
|
||||
|
||||
auto OriginalWriteCursor = Disp->GetWriteCursor();
|
||||
auto OriginalWriteCursor = IREmit->GetWriteCursor();
|
||||
|
||||
while (1) {
|
||||
auto BlockIROp = BlockNode->Op(DataBegin)->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
@@ -58,8 +58,8 @@ bool ConstProp::Run(OpDispatchBuilder *Disp) {
|
||||
uint64_t Constant1;
|
||||
uint64_t Constant2;
|
||||
|
||||
if (Disp->IsValueConstant(IROp->Args[0], &Constant1) &&
|
||||
Disp->IsValueConstant(IROp->Args[1], &Constant2)) {
|
||||
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1) &&
|
||||
IREmit->IsValueConstant(IROp->Args[1], &Constant2)) {
|
||||
LogMan::Msg::A("Could const prop op: %s", std::string(IR::GetName(IROp->Op)).c_str());
|
||||
}
|
||||
break;
|
||||
@@ -75,7 +75,7 @@ bool ConstProp::Run(OpDispatchBuilder *Disp) {
|
||||
{
|
||||
uint64_t Constant1;
|
||||
|
||||
if (Disp->IsValueConstant(IROp->Args[0], &Constant1)) {
|
||||
if (IREmit->IsValueConstant(IROp->Args[0], &Constant1)) {
|
||||
LogMan::Msg::A("Could const prop op: %s", std::string(IR::GetName(IROp->Op)).c_str());
|
||||
}
|
||||
break;
|
||||
@@ -86,12 +86,12 @@ bool ConstProp::Run(OpDispatchBuilder *Disp) {
|
||||
uint64_t Constant1;
|
||||
uint64_t Constant2;
|
||||
|
||||
if (Disp->IsValueConstant(Op->Header.Args[0], &Constant1) &&
|
||||
Disp->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) &&
|
||||
IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
uint64_t NewConstant = Constant1 + Constant2;
|
||||
Disp->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = Disp->_Constant(NewConstant);
|
||||
Disp->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = IREmit->_Constant(NewConstant);
|
||||
IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
Changed = true;
|
||||
}
|
||||
break;
|
||||
@@ -101,12 +101,12 @@ bool ConstProp::Run(OpDispatchBuilder *Disp) {
|
||||
uint64_t Constant1;
|
||||
uint64_t Constant2;
|
||||
|
||||
if (Disp->IsValueConstant(Op->Header.Args[0], &Constant1) &&
|
||||
Disp->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) &&
|
||||
IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
uint64_t NewConstant = Constant1 - Constant2;
|
||||
Disp->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = Disp->_Constant(NewConstant);
|
||||
Disp->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = IREmit->_Constant(NewConstant);
|
||||
IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
Changed = true;
|
||||
}
|
||||
break;
|
||||
@@ -116,12 +116,12 @@ bool ConstProp::Run(OpDispatchBuilder *Disp) {
|
||||
uint64_t Constant1;
|
||||
uint64_t Constant2;
|
||||
|
||||
if (Disp->IsValueConstant(Op->Header.Args[0], &Constant1) &&
|
||||
Disp->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) &&
|
||||
IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
uint64_t NewConstant = Constant1 & Constant2;
|
||||
Disp->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = Disp->_Constant(NewConstant);
|
||||
Disp->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = IREmit->_Constant(NewConstant);
|
||||
IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
Changed = true;
|
||||
}
|
||||
break;
|
||||
@@ -131,12 +131,12 @@ bool ConstProp::Run(OpDispatchBuilder *Disp) {
|
||||
uint64_t Constant1;
|
||||
uint64_t Constant2;
|
||||
|
||||
if (Disp->IsValueConstant(Op->Header.Args[0], &Constant1) &&
|
||||
Disp->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) &&
|
||||
IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
uint64_t NewConstant = Constant1 | Constant2;
|
||||
Disp->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = Disp->_Constant(NewConstant);
|
||||
Disp->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = IREmit->_Constant(NewConstant);
|
||||
IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
Changed = true;
|
||||
}
|
||||
break;
|
||||
@@ -146,27 +146,34 @@ bool ConstProp::Run(OpDispatchBuilder *Disp) {
|
||||
uint64_t Constant1;
|
||||
uint64_t Constant2;
|
||||
|
||||
if (Disp->IsValueConstant(Op->Header.Args[0], &Constant1) &&
|
||||
Disp->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) &&
|
||||
IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
uint64_t NewConstant = Constant1 ^ Constant2;
|
||||
Disp->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = Disp->_Constant(NewConstant);
|
||||
Disp->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = IREmit->_Constant(NewConstant);
|
||||
IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
Changed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_LSHL: {
|
||||
auto Op = IROp->C<IR::IROp_Lshl>();
|
||||
auto Op = IROp->CW<IR::IROp_Lshl>();
|
||||
uint64_t Constant1;
|
||||
uint64_t Constant2;
|
||||
|
||||
if (Disp->IsValueConstant(Op->Header.Args[0], &Constant1) &&
|
||||
Disp->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) &&
|
||||
IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
uint64_t NewConstant = Constant1 << Constant2;
|
||||
Disp->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = Disp->_Constant(NewConstant);
|
||||
Disp->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = IREmit->_Constant(NewConstant);
|
||||
IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
Changed = true;
|
||||
}
|
||||
else if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2) &&
|
||||
Constant2 == 0) {
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
OrderedNode *Arg = Op->Header.Args[0].GetNode(ListBegin);
|
||||
IREmit->ReplaceAllUsesWithInclusive(CodeNode, Arg, CodeBegin, CodeLast);
|
||||
Changed = true;
|
||||
}
|
||||
break;
|
||||
@@ -176,16 +183,16 @@ bool ConstProp::Run(OpDispatchBuilder *Disp) {
|
||||
case OP_BFE: {
|
||||
auto Op = IROp->C<IR::IROp_Bfe>();
|
||||
uint64_t Constant;
|
||||
if (IROp->Size <= 8 && Disp->IsValueConstant(Op->Header.Args[0], &Constant)) {
|
||||
if (IROp->Size <= 8 && IREmit->IsValueConstant(Op->Header.Args[0], &Constant)) {
|
||||
uint64_t SourceMask = (1ULL << Op->Width) - 1;
|
||||
if (Op->Width == 64)
|
||||
SourceMask = ~0ULL;
|
||||
SourceMask <<= Op->lsb;
|
||||
|
||||
uint64_t NewConstant = (Constant & SourceMask) >> Op->lsb;
|
||||
Disp->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = Disp->_Constant(NewConstant);
|
||||
Disp->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = IREmit->_Constant(NewConstant);
|
||||
IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
Changed = true;
|
||||
}
|
||||
|
||||
@@ -195,11 +202,11 @@ bool ConstProp::Run(OpDispatchBuilder *Disp) {
|
||||
auto Op = IROp->C<IR::IROp_Zext>();
|
||||
uint64_t Constant;
|
||||
if (Op->SrcSize != 64 &&
|
||||
Disp->IsValueConstant(Op->Header.Args[0], &Constant)) {
|
||||
IREmit->IsValueConstant(Op->Header.Args[0], &Constant)) {
|
||||
uint64_t NewConstant = Constant & ((1ULL << Op->SrcSize) - 1);
|
||||
Disp->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = Disp->_Constant(NewConstant);
|
||||
Disp->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = IREmit->_Constant(NewConstant);
|
||||
IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
Changed = true;
|
||||
}
|
||||
break;
|
||||
@@ -209,12 +216,12 @@ bool ConstProp::Run(OpDispatchBuilder *Disp) {
|
||||
uint64_t Constant1;
|
||||
uint64_t Constant2;
|
||||
|
||||
if (Disp->IsValueConstant(Op->Header.Args[0], &Constant1) &&
|
||||
Disp->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[0], &Constant1) &&
|
||||
IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
uint64_t NewConstant = Constant1 * Constant2;
|
||||
Disp->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = Disp->_Constant(NewConstant);
|
||||
Disp->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
auto ConstantVal = IREmit->_Constant(NewConstant);
|
||||
IREmit->ReplaceAllUsesWithInclusive(CodeNode, ConstantVal, CodeBegin, CodeLast);
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
@@ -235,7 +242,7 @@ bool ConstProp::Run(OpDispatchBuilder *Disp) {
|
||||
}
|
||||
}
|
||||
|
||||
Disp->SetWriteCursor(OriginalWriteCursor);
|
||||
IREmit->SetWriteCursor(OriginalWriteCursor);
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
namespace FEXCore::IR {
|
||||
|
||||
class DeadCodeElimination final : public FEXCore::IR::Pass {
|
||||
bool Run(OpDispatchBuilder *Disp) override;
|
||||
bool Run(IREmitter *IREmit) override;
|
||||
|
||||
private:
|
||||
void markUsed(OrderedNodeWrapper *CodeOp, IROp_Header *IROp);
|
||||
};
|
||||
|
||||
bool DeadCodeElimination::Run(OpDispatchBuilder *Disp) {
|
||||
auto CurrentIR = Disp->ViewIR();
|
||||
bool DeadCodeElimination::Run(IREmitter *IREmit) {
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
|
||||
uintptr_t ListBegin = CurrentIR.GetListData();
|
||||
uintptr_t DataBegin = CurrentIR.GetData();
|
||||
@@ -71,7 +71,7 @@ bool DeadCodeElimination::Run(OpDispatchBuilder *Disp) {
|
||||
default:
|
||||
if (CodeNode->GetUses() == 0) {
|
||||
NumRemoved++;
|
||||
Disp->Remove(CodeNode);
|
||||
IREmit->Remove(CodeNode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
+22
-22
@@ -172,7 +172,7 @@ public:
|
||||
RCLSE() {
|
||||
ClassifyContextStruct(&ClassifiedStruct);
|
||||
}
|
||||
bool Run(FEXCore::IR::OpDispatchBuilder *Disp) override;
|
||||
bool Run(FEXCore::IR::IREmitter *IREmit) override;
|
||||
private:
|
||||
ContextInfo ClassifiedStruct;
|
||||
std::unordered_map<FEXCore::IR::OrderedNodeWrapper::NodeOffsetType, BlockInfo> OffsetToBlockMap;
|
||||
@@ -180,10 +180,10 @@ private:
|
||||
ContextMemberInfo *FindMemberInfo(ContextInfo *ClassifiedInfo, uint32_t Offset, uint8_t Size);
|
||||
ContextMemberInfo *RecordAccess(ContextMemberInfo *Info, FEXCore::IR::RegisterClassType RegClass, uint32_t Offset, uint8_t Size, LastAccessType AccessType, FEXCore::IR::OrderedNode *Node, FEXCore::IR::OrderedNode *Node2 = nullptr);
|
||||
ContextMemberInfo *RecordAccess(ContextInfo *ClassifiedInfo, FEXCore::IR::RegisterClassType RegClass, uint32_t Offset, uint8_t Size, LastAccessType AccessType, FEXCore::IR::OrderedNode *Node, FEXCore::IR::OrderedNode *Node2 = nullptr);
|
||||
void CalculateControlFlowInfo(FEXCore::IR::OpDispatchBuilder *Disp);
|
||||
void CalculateControlFlowInfo(FEXCore::IR::IREmitter *IREmit);
|
||||
|
||||
// Block local Passes
|
||||
bool RedundantStoreLoadElimination(FEXCore::IR::OpDispatchBuilder *Disp);
|
||||
bool RedundantStoreLoadElimination(FEXCore::IR::IREmitter *IREmit);
|
||||
};
|
||||
|
||||
ContextMemberInfo *RCLSE::FindMemberInfo(ContextInfo *ClassifiedInfo, uint32_t Offset, uint8_t Size) {
|
||||
@@ -228,12 +228,12 @@ ContextMemberInfo *RCLSE::RecordAccess(ContextInfo *ClassifiedInfo, FEXCore::IR:
|
||||
return RecordAccess(Info, RegClass, Offset, Size, AccessType, Node, Node2);
|
||||
}
|
||||
|
||||
void RCLSE::CalculateControlFlowInfo(FEXCore::IR::OpDispatchBuilder *Disp) {
|
||||
void RCLSE::CalculateControlFlowInfo(FEXCore::IR::IREmitter *IREmit) {
|
||||
using namespace FEXCore;
|
||||
using namespace FEXCore::IR;
|
||||
|
||||
OffsetToBlockMap.clear();
|
||||
auto CurrentIR = Disp->ViewIR();
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
uintptr_t ListBegin = CurrentIR.GetListData();
|
||||
uintptr_t DataBegin = CurrentIR.GetData();
|
||||
|
||||
@@ -347,15 +347,15 @@ void RCLSE::CalculateControlFlowInfo(FEXCore::IR::OpDispatchBuilder *Disp) {
|
||||
* (%%ssa176) StoreContext %ssa175 i128, 0x10, 0xa0
|
||||
|
||||
*/
|
||||
bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::OpDispatchBuilder *Disp) {
|
||||
bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::IREmitter *IREmit) {
|
||||
using namespace FEXCore;
|
||||
using namespace FEXCore::IR;
|
||||
|
||||
bool Changed = false;
|
||||
auto CurrentIR = Disp->ViewIR();
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
uintptr_t ListBegin = CurrentIR.GetListData();
|
||||
uintptr_t DataBegin = CurrentIR.GetData();
|
||||
auto OriginalWriteCursor = Disp->GetWriteCursor();
|
||||
auto OriginalWriteCursor = IREmit->GetWriteCursor();
|
||||
|
||||
auto Begin = CurrentIR.begin();
|
||||
auto Op = Begin();
|
||||
@@ -402,7 +402,7 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::OpDispatchBuilder *Disp)
|
||||
Info->Accessed == ACCESS_WRITE) {
|
||||
// Remove the last store because this one overwrites it entirely
|
||||
// Happens when we store in to a location then store again
|
||||
Disp->Remove(LastNode2);
|
||||
IREmit->Remove(LastNode2);
|
||||
}
|
||||
}
|
||||
else if (IROp->Op == OP_LOADCONTEXT) {
|
||||
@@ -424,14 +424,14 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::OpDispatchBuilder *Disp)
|
||||
if (Op->Offset >= offsetof(FEXCore::Core::CPUState, xmm[0]) &&
|
||||
Op->Offset <= offsetof(FEXCore::Core::CPUState, xmm[15])) {
|
||||
|
||||
Disp->SetWriteCursor(CodeNode);
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
// XMM needs a bit of special help
|
||||
auto BitCast = Disp->_VBitcast(IROp->Size * 8, 1, LastNode);
|
||||
Disp->ReplaceAllUsesWithInclusive(CodeNode, BitCast, CodeBegin, CodeLast);
|
||||
auto BitCast = IREmit->_VBitcast(IROp->Size * 8, 1, LastNode);
|
||||
IREmit->ReplaceAllUsesWithInclusive(CodeNode, BitCast, CodeBegin, CodeLast);
|
||||
RecordAccess(Info, Op->Class, Op->Offset, IROp->Size, ACCESS_READ, LastNode);
|
||||
}
|
||||
else {
|
||||
Disp->ReplaceAllUsesWithInclusive(CodeNode, LastNode, CodeBegin, CodeLast);
|
||||
IREmit->ReplaceAllUsesWithInclusive(CodeNode, LastNode, CodeBegin, CodeLast);
|
||||
RecordAccess(Info, Op->Class, Op->Offset, IROp->Size, ACCESS_READ, LastNode);
|
||||
}
|
||||
Changed = true;
|
||||
@@ -442,7 +442,7 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::OpDispatchBuilder *Disp)
|
||||
LastSize == IROp->Size &&
|
||||
Info->Accessed == ACCESS_READ) {
|
||||
// Did we read and then read again?
|
||||
Disp->ReplaceAllUsesWithInclusive(CodeNode, LastNode, CodeBegin, CodeLast);
|
||||
IREmit->ReplaceAllUsesWithInclusive(CodeNode, LastNode, CodeBegin, CodeLast);
|
||||
RecordAccess(Info, Op->Class, Op->Offset, IROp->Size, ACCESS_READ, LastNode);
|
||||
Changed = true;
|
||||
}
|
||||
@@ -459,14 +459,14 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::OpDispatchBuilder *Disp)
|
||||
|
||||
if (LastAccess == ACCESS_WRITE) { // 1 byte so always a full write
|
||||
// If the last store matches this load value then we can replace the loaded value with the previous valid one
|
||||
Disp->SetWriteCursor(CodeNode);
|
||||
auto Res = Disp->_Bfe(1, 0, LastNode);
|
||||
Disp->ReplaceAllUsesWithInclusive(CodeNode, Res, CodeBegin, CodeLast);
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
auto Res = IREmit->_Bfe(1, 0, LastNode);
|
||||
IREmit->ReplaceAllUsesWithInclusive(CodeNode, Res, CodeBegin, CodeLast);
|
||||
RecordAccess(Info, FEXCore::IR::GPRClass, offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag, 1, ACCESS_READ, LastNode);
|
||||
Changed = true;
|
||||
}
|
||||
else if (LastAccess == ACCESS_READ) {
|
||||
Disp->ReplaceAllUsesWithInclusive(CodeNode, LastNode, CodeBegin, CodeLast);
|
||||
IREmit->ReplaceAllUsesWithInclusive(CodeNode, LastNode, CodeBegin, CodeLast);
|
||||
RecordAccess(Info, FEXCore::IR::GPRClass, offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag, 1, ACCESS_READ, LastNode);
|
||||
Changed = true;
|
||||
}
|
||||
@@ -493,16 +493,16 @@ bool RCLSE::RedundantStoreLoadElimination(FEXCore::IR::OpDispatchBuilder *Disp)
|
||||
}
|
||||
}
|
||||
|
||||
Disp->SetWriteCursor(OriginalWriteCursor);
|
||||
IREmit->SetWriteCursor(OriginalWriteCursor);
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
||||
bool RCLSE::Run(FEXCore::IR::OpDispatchBuilder *Disp) {
|
||||
bool RCLSE::Run(FEXCore::IR::IREmitter *IREmit) {
|
||||
ResetClassificationAccesses(&ClassifiedStruct);
|
||||
CalculateControlFlowInfo(Disp);
|
||||
CalculateControlFlowInfo(IREmit);
|
||||
bool Changed = false;
|
||||
Changed |= RedundantStoreLoadElimination(Disp);
|
||||
Changed |= RedundantStoreLoadElimination(IREmit);
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace FEXCore::IR {
|
||||
class IRCompaction final : public FEXCore::IR::Pass {
|
||||
public:
|
||||
IRCompaction();
|
||||
bool Run(OpDispatchBuilder *Disp) override;
|
||||
bool Run(IREmitter *IREmit) override;
|
||||
|
||||
private:
|
||||
OpDispatchBuilder LocalBuilder;
|
||||
@@ -20,9 +20,8 @@ IRCompaction::IRCompaction()
|
||||
OldToNewRemap.resize(9000);
|
||||
}
|
||||
|
||||
bool IRCompaction::Run(OpDispatchBuilder *Disp) {
|
||||
LocalBuilder.SetMultiblock(Disp->GetMultiblock());
|
||||
auto CurrentIR = Disp->ViewIR();
|
||||
bool IRCompaction::Run(IREmitter *IREmit) {
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
uint32_t NodeCount = CurrentIR.GetSSACount();
|
||||
|
||||
if (OldToNewRemap.size() < NodeCount) {
|
||||
@@ -216,7 +215,7 @@ bool IRCompaction::Run(OpDispatchBuilder *Disp) {
|
||||
// LogMan::Msg::A("Whoa. Compaction made the IR a different size when it shouldn't have. 0x%lx > 0x%lx or 0x%lx > 0x%lx",NewListSize, OldListSize, NewDataSize, OldDataSize);
|
||||
// }
|
||||
|
||||
Disp->CopyData(LocalBuilder);
|
||||
IREmit->CopyData(LocalBuilder);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18,15 +18,15 @@ namespace FEXCore::IR::Validation {
|
||||
|
||||
class IRValidation final : public FEXCore::IR::Pass {
|
||||
public:
|
||||
bool Run(OpDispatchBuilder *Disp) override;
|
||||
bool Run(IREmitter *IREmit) override;
|
||||
};
|
||||
|
||||
bool IRValidation::Run(OpDispatchBuilder *Disp) {
|
||||
bool IRValidation::Run(IREmitter *IREmit) {
|
||||
bool HadError = false;
|
||||
bool HadWarning = false;
|
||||
|
||||
std::unordered_map<IR::OrderedNodeWrapper::NodeOffsetType, BlockInfo> OffsetToBlockMap;
|
||||
auto CurrentIR = Disp->ViewIR();
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
uintptr_t ListBegin = CurrentIR.GetListData();
|
||||
uintptr_t DataBegin = CurrentIR.GetData();
|
||||
|
||||
@@ -42,8 +42,8 @@ bool IRValidation::Run(OpDispatchBuilder *Disp) {
|
||||
|
||||
OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin);
|
||||
IR::RegisterAllocationPass * RAPass{};
|
||||
if (Disp->CTX->HasRegisterAllocationPass()) {
|
||||
RAPass = Disp->CTX->GetRegisterAllocatorPass();
|
||||
if (Manager->HasRAPass()) {
|
||||
RAPass = Manager->GetRAPass();
|
||||
}
|
||||
|
||||
while (1) {
|
||||
|
||||
@@ -7,12 +7,12 @@ namespace FEXCore::IR::Validation {
|
||||
|
||||
class PhiValidation final : public FEXCore::IR::Pass {
|
||||
public:
|
||||
bool Run(OpDispatchBuilder *Disp) override;
|
||||
bool Run(IREmitter *IREmit) override;
|
||||
};
|
||||
|
||||
bool PhiValidation::Run(OpDispatchBuilder *Disp) {
|
||||
bool PhiValidation::Run(IREmitter *IREmit) {
|
||||
bool HadError = false;
|
||||
auto CurrentIR = Disp->ViewIR();
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
uintptr_t ListBegin = CurrentIR.GetListData();
|
||||
uintptr_t DataBegin = CurrentIR.GetData();
|
||||
|
||||
|
||||
+4
-4
@@ -5,7 +5,7 @@ namespace FEXCore::IR {
|
||||
|
||||
class DeadFlagCalculationEliminination final : public FEXCore::IR::Pass {
|
||||
public:
|
||||
bool Run(OpDispatchBuilder *Disp) override;
|
||||
bool Run(IREmitter *IREmit) override;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -19,11 +19,11 @@ public:
|
||||
* This may be more interesting with full function level recompilation since flags definitely won't be used across function boundaries.
|
||||
*
|
||||
*/
|
||||
bool DeadFlagCalculationEliminination::Run(OpDispatchBuilder *Disp) {
|
||||
bool DeadFlagCalculationEliminination::Run(IREmitter *IREmit) {
|
||||
std::array<OrderedNode*, 32> LastValidFlagStores{};
|
||||
|
||||
bool Changed = false;
|
||||
auto CurrentIR = Disp->ViewIR();
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
uintptr_t ListBegin = CurrentIR.GetListData();
|
||||
uintptr_t DataBegin = CurrentIR.GetData();
|
||||
|
||||
@@ -74,7 +74,7 @@ bool DeadFlagCalculationEliminination::Run(OpDispatchBuilder *Disp) {
|
||||
// If any flags are stored but not loaded by the end of the block, then erase them
|
||||
for (auto &Flag : LastValidFlagStores) {
|
||||
if (Flag != nullptr) {
|
||||
Disp->Remove(Flag);
|
||||
IREmit->Remove(Flag);
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,7 +320,7 @@ namespace FEXCore::IR {
|
||||
public:
|
||||
ConstrainedRAPass();
|
||||
~ConstrainedRAPass();
|
||||
bool Run(OpDispatchBuilder *Disp) override;
|
||||
bool Run(IREmitter *IREmit) override;
|
||||
|
||||
void AllocateRegisterSet(uint32_t RegisterCount, uint32_t ClassCount) override;
|
||||
void AddRegisters(FEXCore::IR::RegisterClassType Class, uint32_t RegisterCount) override;
|
||||
@@ -340,7 +340,7 @@ namespace FEXCore::IR {
|
||||
RegisterGraph *Graph;
|
||||
std::unique_ptr<FEXCore::IR::Pass> LocalCompaction;
|
||||
|
||||
void SpillRegisters(FEXCore::IR::OpDispatchBuilder *Disp);
|
||||
void SpillRegisters(FEXCore::IR::IREmitter *IREmit);
|
||||
|
||||
std::vector<LiveRange> LiveRanges;
|
||||
|
||||
@@ -355,11 +355,11 @@ namespace FEXCore::IR {
|
||||
void CalculateNodeInterference(FEXCore::IR::IRListView<false> *IR);
|
||||
void AllocateVirtualRegisters();
|
||||
|
||||
FEXCore::IR::NodeWrapperIterator FindFirstUse(FEXCore::IR::OpDispatchBuilder *Disp, FEXCore::IR::OrderedNode* Node, FEXCore::IR::NodeWrapperIterator Begin, FEXCore::IR::NodeWrapperIterator End);
|
||||
FEXCore::IR::NodeWrapperIterator FindFirstUse(FEXCore::IR::IREmitter *IREmit, FEXCore::IR::OrderedNode* Node, FEXCore::IR::NodeWrapperIterator Begin, FEXCore::IR::NodeWrapperIterator End);
|
||||
uint32_t FindNodeToSpill(RegisterNode *RegisterNode, uint32_t CurrentLocation, LiveRange const *OpLiveRange);
|
||||
uint32_t FindSpillSlot(uint32_t Node, FEXCore::IR::RegisterClassType RegisterClass);
|
||||
|
||||
bool RunAllocateVirtualRegisters(OpDispatchBuilder *Disp);
|
||||
bool RunAllocateVirtualRegisters(IREmitter *IREmit);
|
||||
};
|
||||
|
||||
ConstrainedRAPass::ConstrainedRAPass() {
|
||||
@@ -725,8 +725,8 @@ namespace FEXCore::IR {
|
||||
}
|
||||
}
|
||||
|
||||
FEXCore::IR::NodeWrapperIterator ConstrainedRAPass::FindFirstUse(FEXCore::IR::OpDispatchBuilder *Disp, FEXCore::IR::OrderedNode* Node, FEXCore::IR::NodeWrapperIterator Begin, FEXCore::IR::NodeWrapperIterator End) {
|
||||
auto CurrentIR = Disp->ViewIR();
|
||||
FEXCore::IR::NodeWrapperIterator ConstrainedRAPass::FindFirstUse(FEXCore::IR::IREmitter *IREmit, FEXCore::IR::OrderedNode* Node, FEXCore::IR::NodeWrapperIterator Begin, FEXCore::IR::NodeWrapperIterator End) {
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
uintptr_t ListBegin = CurrentIR.GetListData();
|
||||
uintptr_t DataBegin = CurrentIR.GetData();
|
||||
|
||||
@@ -856,16 +856,16 @@ namespace FEXCore::IR {
|
||||
return CurrentNode->Head.SpillSlot;
|
||||
}
|
||||
|
||||
void ConstrainedRAPass::SpillRegisters(FEXCore::IR::OpDispatchBuilder *Disp) {
|
||||
void ConstrainedRAPass::SpillRegisters(FEXCore::IR::IREmitter *IREmit) {
|
||||
using namespace FEXCore;
|
||||
|
||||
auto IR = Disp->ViewIR();
|
||||
auto IR = IREmit->ViewIR();
|
||||
uintptr_t ListBegin = IR.GetListData();
|
||||
uintptr_t DataBegin = IR.GetData();
|
||||
|
||||
auto Begin = IR.begin();
|
||||
auto Op = Begin();
|
||||
auto LastCursor = Disp->GetWriteCursor();
|
||||
auto LastCursor = IREmit->GetWriteCursor();
|
||||
|
||||
IR::OrderedNode *RealNode = Op->GetNode(ListBegin);
|
||||
auto HeaderOp = RealNode->Op(DataBegin)->CW<FEXCore::IR::IROp_IRHeader>();
|
||||
@@ -911,15 +911,15 @@ namespace FEXCore::IR {
|
||||
LogMan::Throw::A(ConstantIROp->Header.Op == IR::OP_CONSTANT, "This needs to be const");
|
||||
// First op post Spill
|
||||
auto NextIter = CodeBegin;
|
||||
auto FirstUseLocation = FindFirstUse(Disp, ConstantNode, NextIter, CodeLast);
|
||||
auto FirstUseLocation = FindFirstUse(IREmit, ConstantNode, NextIter, CodeLast);
|
||||
LogMan::Throw::A(FirstUseLocation != IR::NodeWrapperIterator::Invalid(), "At %%ssa%d Spilling Op %%ssa%d but Failure to find op use", CodeOp->ID(), InterferenceNode);
|
||||
if (FirstUseLocation != IR::NodeWrapperIterator::Invalid()) {
|
||||
--FirstUseLocation;
|
||||
IR::OrderedNodeWrapper *FirstUseOp = FirstUseLocation();
|
||||
IR::OrderedNode *FirstUseOrderedNode = FirstUseOp->GetNode(ListBegin);
|
||||
Disp->SetWriteCursor(FirstUseOrderedNode);
|
||||
auto FilledConstant = Disp->_Constant(ConstantIROp->Constant);
|
||||
Disp->ReplaceAllUsesWithInclusive(ConstantNode, FilledConstant, FirstUseLocation, CodeLast);
|
||||
IREmit->SetWriteCursor(FirstUseOrderedNode);
|
||||
auto FilledConstant = IREmit->_Constant(ConstantIROp->Constant);
|
||||
IREmit->ReplaceAllUsesWithInclusive(ConstantNode, FilledConstant, FirstUseLocation, CodeLast);
|
||||
Spilled = true;
|
||||
}
|
||||
break;
|
||||
@@ -946,9 +946,9 @@ namespace FEXCore::IR {
|
||||
auto PrevIter = CodeBegin;
|
||||
--PrevIter;
|
||||
--PrevIter;
|
||||
Disp->SetWriteCursor(PrevIter()->GetNode(ListBegin));
|
||||
IREmit->SetWriteCursor(PrevIter()->GetNode(ListBegin));
|
||||
|
||||
auto SpillOp = Disp->_SpillRegister(InterferenceOrderedNode, SpillSlot, {InterferenceRegClass});
|
||||
auto SpillOp = IREmit->_SpillRegister(InterferenceOrderedNode, SpillSlot, {InterferenceRegClass});
|
||||
SpillOp.first->Header.Size = InterferenceIROp->Size;
|
||||
SpillOp.first->Header.ElementSize = InterferenceIROp->ElementSize;
|
||||
|
||||
@@ -956,7 +956,7 @@ namespace FEXCore::IR {
|
||||
// First op post Spill
|
||||
auto NextIter = CodeBegin;
|
||||
++NextIter;
|
||||
auto FirstUseLocation = FindFirstUse(Disp, InterferenceOrderedNode, NextIter, CodeLast);
|
||||
auto FirstUseLocation = FindFirstUse(IREmit, InterferenceOrderedNode, NextIter, CodeLast);
|
||||
|
||||
LogMan::Throw::A(FirstUseLocation != NodeWrapperIterator::Invalid(), "At %%ssa%d Spilling Op %%ssa%d but Failure to find op use", CodeOp->ID(), InterferenceNode);
|
||||
if (FirstUseLocation != IR::NodeWrapperIterator::Invalid()) {
|
||||
@@ -964,19 +964,19 @@ namespace FEXCore::IR {
|
||||
IR::OrderedNodeWrapper *FirstUseOp = FirstUseLocation();
|
||||
IR::OrderedNode *FirstUseOrderedNode = FirstUseOp->GetNode(ListBegin);
|
||||
|
||||
Disp->SetWriteCursor(FirstUseOrderedNode);
|
||||
IREmit->SetWriteCursor(FirstUseOrderedNode);
|
||||
|
||||
auto FilledInterference = Disp->_FillRegister(SpillSlot, {InterferenceRegClass});
|
||||
auto FilledInterference = IREmit->_FillRegister(SpillSlot, {InterferenceRegClass});
|
||||
FilledInterference.first->Header.Size = InterferenceIROp->Size;
|
||||
FilledInterference.first->Header.ElementSize = InterferenceIROp->ElementSize;
|
||||
Disp->ReplaceAllUsesWithInclusive(InterferenceOrderedNode, FilledInterference, FirstUseLocation, CodeLast);
|
||||
IREmit->ReplaceAllUsesWithInclusive(InterferenceOrderedNode, FilledInterference, FirstUseLocation, CodeLast);
|
||||
Spilled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Disp->SetWriteCursor(LastCursor);
|
||||
IREmit->SetWriteCursor(LastCursor);
|
||||
// We can't spill multiple times in a row. Need to restart
|
||||
if (Spilled) {
|
||||
return;
|
||||
@@ -999,7 +999,7 @@ namespace FEXCore::IR {
|
||||
}
|
||||
}
|
||||
|
||||
bool ConstrainedRAPass::RunAllocateVirtualRegisters(FEXCore::IR::OpDispatchBuilder *Disp) {
|
||||
bool ConstrainedRAPass::RunAllocateVirtualRegisters(FEXCore::IR::IREmitter *IREmit) {
|
||||
using namespace FEXCore;
|
||||
bool Changed = false;
|
||||
|
||||
@@ -1009,8 +1009,8 @@ namespace FEXCore::IR {
|
||||
TopRAPressure.assign(TopRAPressure.size(), 0);
|
||||
|
||||
// We need to rerun compaction every step
|
||||
Changed |= LocalCompaction->Run(Disp);
|
||||
auto IR = Disp->ViewIR();
|
||||
Changed |= LocalCompaction->Run(IREmit);
|
||||
auto IR = IREmit->ViewIR();
|
||||
|
||||
uint32_t SSACount = IR.GetSSACount();
|
||||
|
||||
@@ -1033,7 +1033,7 @@ namespace FEXCore::IR {
|
||||
}
|
||||
|
||||
|
||||
bool ConstrainedRAPass::Run(OpDispatchBuilder *Disp) {
|
||||
bool ConstrainedRAPass::Run(IREmitter *IREmit) {
|
||||
bool Changed = false;
|
||||
|
||||
SpillSlotCount = 0;
|
||||
@@ -1043,7 +1043,7 @@ namespace FEXCore::IR {
|
||||
HadFullRA = true;
|
||||
|
||||
// Virtual allocation pass runs the compaction pass per run
|
||||
Changed |= RunAllocateVirtualRegisters(Disp);
|
||||
Changed |= RunAllocateVirtualRegisters(IREmit);
|
||||
|
||||
for (size_t i = 0; i < PhysicalRegisterCount.size(); ++i) {
|
||||
// Virtual registers fit completely within physical registers
|
||||
@@ -1055,7 +1055,7 @@ namespace FEXCore::IR {
|
||||
break;
|
||||
}
|
||||
|
||||
SpillRegisters(Disp);
|
||||
SpillRegisters(IREmit);
|
||||
Changed = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@ namespace FEXCore::IR {
|
||||
|
||||
class SyscallOptimization final : public FEXCore::IR::Pass {
|
||||
public:
|
||||
bool Run(OpDispatchBuilder *Disp) override;
|
||||
bool Run(IREmitter *IREmit) override;
|
||||
};
|
||||
|
||||
bool SyscallOptimization::Run(OpDispatchBuilder *Disp) {
|
||||
bool SyscallOptimization::Run(IREmitter *IREmit) {
|
||||
bool Changed = false;
|
||||
auto CurrentIR = Disp->ViewIR();
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
uintptr_t ListBegin = CurrentIR.GetListData();
|
||||
uintptr_t DataBegin = CurrentIR.GetData();
|
||||
|
||||
@@ -41,14 +41,14 @@ bool SyscallOptimization::Run(OpDispatchBuilder *Disp) {
|
||||
if (IROp->Op == FEXCore::IR::OP_SYSCALL) {
|
||||
// Is the first argument a constant?
|
||||
uint64_t Constant;
|
||||
if (Disp->IsValueConstant(IROp->Args[0], &Constant)) {
|
||||
auto SyscallDef = Disp->CTX->SyscallHandler->GetDefinition(Constant);
|
||||
if (IREmit->IsValueConstant(IROp->Args[0], &Constant)) {
|
||||
auto SyscallDef = Manager->SyscallHandler->GetDefinition(Constant);
|
||||
// XXX: Once we have the ability to do real function calls then we can call directly in to the syscall handler
|
||||
if (SyscallDef->NumArgs < FEXCore::HLE::SyscallArguments::MAX_ARGS) {
|
||||
// If the number of args are less than what the IR op supports then we can remove arg usage
|
||||
// We need +1 since we are still passing in syscall number here
|
||||
for (uint8_t Arg = (SyscallDef->NumArgs + 1); Arg < FEXCore::HLE::SyscallArguments::MAX_ARGS; ++Arg) {
|
||||
Disp->ReplaceNodeArgument(CodeNode, Arg, Disp->Invalid());
|
||||
IREmit->ReplaceNodeArgument(CodeNode, Arg, IREmit->Invalid());
|
||||
}
|
||||
Changed = true;
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@ namespace {
|
||||
namespace FEXCore::IR::Validation {
|
||||
class ValueDominanceValidation final : public FEXCore::IR::Pass {
|
||||
public:
|
||||
bool Run(OpDispatchBuilder *Disp) override;
|
||||
bool Run(IREmitter *IREmit) override;
|
||||
};
|
||||
|
||||
bool ValueDominanceValidation::Run(OpDispatchBuilder *Disp) {
|
||||
bool ValueDominanceValidation::Run(IREmitter *IREmit) {
|
||||
bool HadError = false;
|
||||
bool HadWarning = false;
|
||||
auto CurrentIR = Disp->ViewIR();
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
uintptr_t ListBegin = CurrentIR.GetListData();
|
||||
uintptr_t DataBegin = CurrentIR.GetData();
|
||||
std::ostringstream Errors;
|
||||
|
||||
+2
-3
@@ -5,8 +5,7 @@
|
||||
|
||||
namespace FEXCore {
|
||||
namespace IR {
|
||||
template<bool>
|
||||
class IRListView;
|
||||
class IREmitter;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,7 +79,7 @@ public:
|
||||
|
||||
virtual void GetAuxv(uint64_t& addr, uint64_t& size) {}
|
||||
|
||||
using IRHandler = std::function<void(uint64_t Addr, FEXCore::IR::IRListView<true> *IR)>;
|
||||
using IRHandler = std::function<void(uint64_t Addr, FEXCore::IR::IREmitter *IR)>;
|
||||
virtual void AddIR(IRHandler Handler) {}
|
||||
};
|
||||
|
||||
|
||||
+525
@@ -0,0 +1,525 @@
|
||||
#pragma once
|
||||
#include <FEXCore/IR/IntrusiveIRList.h>
|
||||
#include <FEXCore/IR/IR.h>
|
||||
|
||||
#include "LogManager.h"
|
||||
|
||||
namespace FEXCore::IR {
|
||||
class Pass;
|
||||
class PassManager;
|
||||
|
||||
class IREmitter {
|
||||
friend class FEXCore::IR::Pass;
|
||||
friend class FEXCore::IR::PassManager;
|
||||
|
||||
public:
|
||||
IREmitter()
|
||||
: Data {8 * 1024 * 1024}
|
||||
, ListData {8 * 1024 * 1024} {
|
||||
ResetWorkingList();
|
||||
}
|
||||
|
||||
IRListView<false> ViewIR() { return IRListView<false>(&Data, &ListData); }
|
||||
IRListView<true> *CreateIRCopy() { return new IRListView<true>(&Data, &ListData); }
|
||||
void ResetWorkingList();
|
||||
|
||||
/**
|
||||
* @name IR allocation routines
|
||||
*
|
||||
* @{ */
|
||||
|
||||
// These handlers add cost to the constructor and destructor
|
||||
// If it becomes an issue then blow them away
|
||||
// GCC also generates some pretty atrocious code around these
|
||||
// Use Clang!
|
||||
#define IROP_ALLOCATE_HELPERS
|
||||
#define IROP_DISPATCH_HELPERS
|
||||
#include <FEXCore/IR/IRDefines.inc>
|
||||
|
||||
IRPair<IROp_Constant> _Constant(uint8_t Size, uint64_t Constant) {
|
||||
auto Op = AllocateOp<IROp_Constant, IROps::OP_CONSTANT>();
|
||||
Op.first->Constant = Constant;
|
||||
Op.first->Header.Size = Size / 8;
|
||||
Op.first->Header.ElementSize = Size / 8;
|
||||
Op.first->Header.NumArgs = 0;
|
||||
Op.first->Header.HasDest = true;
|
||||
return Op;
|
||||
}
|
||||
IRPair<IROp_VBitcast> _VBitcast(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
auto Op = AllocateOp<IROp_VBitcast, IROps::OP_VBITCAST>();
|
||||
Op.first->Header.Size = RegisterSize / 8;
|
||||
Op.first->Header.ElementSize = ElementSize;
|
||||
Op.first->Header.NumArgs = 1;
|
||||
Op.first->Header.HasDest = true;
|
||||
Op.first->Header.Args[0] = ssa0->Wrapped(ListData.Begin());
|
||||
ssa0->AddUse();
|
||||
return Op;
|
||||
}
|
||||
IRPair<IROp_LoadContext> _LoadContext(uint8_t Size, uint32_t Offset, RegisterClassType Class) {
|
||||
return _LoadContext(Offset, Class, Size);
|
||||
}
|
||||
IRPair<IROp_StoreContext> _StoreContext(RegisterClassType Class, uint8_t Size, uint32_t Offset, OrderedNode *ssa0) {
|
||||
return _StoreContext(ssa0, Offset, Class, Size);
|
||||
}
|
||||
IRPair<IROp_Bfe> _Bfe(uint8_t Width, uint8_t lsb, OrderedNode *ssa0) {
|
||||
return _Bfe(ssa0, Width, lsb);
|
||||
}
|
||||
IRPair<IROp_Bfi> _Bfi(uint8_t Width, uint8_t lsb, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _Bfi(ssa0, ssa1, Width, lsb);
|
||||
}
|
||||
IRPair<IROp_StoreMem> _StoreMem(FEXCore::IR::RegisterClassType Class, uint8_t Size, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t Align = 1) {
|
||||
return _StoreMem(ssa0, ssa1, Size, Align, Class);
|
||||
}
|
||||
IRPair<IROp_LoadMem> _LoadMem(FEXCore::IR::RegisterClassType Class, uint8_t Size, OrderedNode *ssa0, uint8_t Align = 1) {
|
||||
return _LoadMem(ssa0, Size, Align, Class);
|
||||
}
|
||||
IRPair<IROp_Select> _Select(uint8_t Cond, OrderedNode *ssa0, OrderedNode *ssa1, OrderedNode *ssa2, OrderedNode *ssa3) {
|
||||
return _Select(ssa0, ssa1, ssa2, ssa3, {Cond});
|
||||
}
|
||||
IRPair<IROp_Sext> _Sext(uint8_t SrcSize, OrderedNode *ssa0) {
|
||||
return _Sext(ssa0, SrcSize);
|
||||
}
|
||||
IRPair<IROp_Zext> _Zext(uint8_t SrcSize, OrderedNode *ssa0) {
|
||||
return _Zext(ssa0, SrcSize);
|
||||
}
|
||||
IRPair<IROp_VInsElement> _VInsElement(uint8_t RegisterSize, uint8_t ElementSize, uint8_t DestIdx, uint8_t SrcIdx, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VInsElement(ssa0, ssa1, DestIdx, SrcIdx, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VInsScalarElement> _VInsScalarElement(uint8_t RegisterSize, uint8_t ElementSize, uint8_t DestIdx, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VInsScalarElement(ssa0, ssa1, DestIdx, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VExtractElement> _VExtractElement(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t Index) {
|
||||
return _VExtractElement(ssa0, Index, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VAnd> _VAnd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VAnd(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VAdd> _VAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VAdd(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSub> _VSub(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSub(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUQAdd> _VUQAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUQAdd(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUQSub> _VUQSub(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUQSub(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSQAdd> _VSQAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSQAdd(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSQSub> _VSQSub(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSQSub(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VAddP> _VAddP(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VAddP(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUMin> _VUMin(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUMin(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSMin> _VSMin(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSMin(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUMax> _VUMax(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUMax(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSMax> _VSMax(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSMax(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VZip> _VZip(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VZip(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VZip2> _VZip2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VZip2(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VCMPEQ> _VCMPEQ(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VCMPEQ(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VCMPGT> _VCMPGT(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VCMPGT(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFAdd> _VFAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFAdd(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFAddP> _VFAddP(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFAddP(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFCMPEQ> _VFCMPEQ(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFCMPEQ(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFCMPNEQ> _VFCMPNEQ(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFCMPNEQ(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFCMPLT> _VFCMPLT(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFCMPLT(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFCMPGT> _VFCMPGT(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFCMPGT(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFCMPLE> _VFCMPLE(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFCMPLE(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFCMPUNO> _VFCMPUNO(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFCMPUNO(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFCMPORD> _VFCMPORD(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFCMPORD(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUShl> _VUShl(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUShl(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUShlS> _VUShlS(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUShlS(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUShrS> _VUShrS(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUShrS(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSShrS> _VSShrS(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSShrS(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUShr> _VUShr(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUShr(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSShr> _VSShr(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSShr(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VExtr> _VExtr(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t Index) {
|
||||
return _VExtr(ssa0, ssa1, Index, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSLI> _VSLI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t ByteShift) {
|
||||
return _VSLI(ssa0, ByteShift, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSRI> _VSRI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t ByteShift) {
|
||||
return _VSRI(ssa0, ByteShift, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUShrI> _VUShrI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) {
|
||||
return _VUShrI(ssa0, BitShift, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSShrI> _VSShrI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) {
|
||||
return _VSShrI(ssa0, BitShift, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUShrNI> _VUShrNI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) {
|
||||
return _VUShrNI(ssa0, BitShift, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUShrNI2> _VUShrNI2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t BitShift) {
|
||||
return _VUShrNI2(ssa0, ssa1, BitShift, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VShlI> _VShlI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) {
|
||||
return _VShlI(ssa0, BitShift, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFSqrt> _VFSqrt(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VFSqrt(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFRSqrt> _VFRSqrt(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VFRSqrt(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VNeg> _VNeg(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VNeg(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFNeg> _VFNeg(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VFNeg(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VNot> _VNot(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VNot(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSQXTN> _VSQXTN(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VSQXTN(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSQXTN2> _VSQXTN2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSQXTN2(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSQXTUN> _VSQXTUN(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VSQXTUN(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSQXTUN2> _VSQXTUN2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSQXTUN2(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VCastFromGPR> _VCastFromGPR(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VCastFromGPR(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VExtractToGPR> _VExtractToGPR(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t Index) {
|
||||
return _VExtractToGPR(ssa0, Index, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VInsGPR> _VInsGPR(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t Index) {
|
||||
return _VInsGPR(ssa0, ssa1, Index, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_Vector_FToF> _Vector_FToF(uint8_t RegisterSize, uint8_t DstElementSize, uint8_t SrcElementSize, OrderedNode *ssa0) {
|
||||
return _Vector_FToF(ssa0, SrcElementSize, RegisterSize, DstElementSize);
|
||||
}
|
||||
IRPair<IROp_Float_FToF> _Float_FToF(uint8_t DstElementSize, uint8_t SrcElementSize, OrderedNode *ssa0) {
|
||||
return _Float_FToF(ssa0, SrcElementSize, DstElementSize);
|
||||
}
|
||||
IRPair<IROp_VUMul> _VUMul(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUMul(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSMul> _VSMul(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSMul(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUMull> _VUMull(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUMull(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSMull> _VSMull(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSMull(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUMull2> _VUMull2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUMull2(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSMull2> _VSMull2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSMull2(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSXTL> _VSXTL(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VSXTL(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSXTL2> _VSXTL2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VSXTL2(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUXTL> _VUXTL(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VUXTL(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUXTL2> _VUXTL2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VUXTL2(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_Jump> _Jump() {
|
||||
return _Jump(InvalidNode);
|
||||
}
|
||||
IRPair<IROp_CondJump> _CondJump(OrderedNode *ssa0) {
|
||||
return _CondJump(ssa0, InvalidNode, InvalidNode);
|
||||
}
|
||||
|
||||
IRPair<IROp_Phi> _Phi() {
|
||||
return _Phi(InvalidNode, InvalidNode, 0);
|
||||
}
|
||||
|
||||
IRPair<IROp_PhiValue> _PhiValue(OrderedNode *Value, OrderedNode *Block) {
|
||||
return _PhiValue(Value, Block, InvalidNode);
|
||||
}
|
||||
|
||||
OrderedNode *Invalid() {
|
||||
return InvalidNode;
|
||||
}
|
||||
|
||||
void AddPhiValue(IR::IROp_Phi *Phi, OrderedNode *Value) {
|
||||
// Got to do some bookkeeping first
|
||||
Value->AddUse();
|
||||
auto ValueIROp = Value->Op(Data.Begin())->C<IR::IROp_PhiValue>()->Value.GetNode(ListData.Begin())->Op(Data.Begin());
|
||||
Phi->Header.Size = ValueIROp->Size;
|
||||
Phi->Header.ElementSize = ValueIROp->ElementSize;
|
||||
|
||||
if (!Phi->PhiBegin.ID()) {
|
||||
Phi->PhiBegin = Phi->PhiEnd = Value->Wrapped(ListData.Begin());
|
||||
return;
|
||||
}
|
||||
auto PhiValueEndNode = Phi->PhiEnd.GetNode(ListData.Begin());
|
||||
auto PhiValueEndOp = PhiValueEndNode->Op(Data.Begin())->CW<IR::IROp_PhiValue>();
|
||||
PhiValueEndOp->Next = Value->Wrapped(ListData.Begin());
|
||||
}
|
||||
|
||||
void SetJumpTarget(IR::IROp_Jump *Op, OrderedNode *Target) {
|
||||
LogMan::Throw::A(Target->Op(Data.Begin())->Op == OP_CODEBLOCK,
|
||||
"Tried setting Jump target to %%ssa%d %s",
|
||||
Target->Wrapped(ListData.Begin()).ID(),
|
||||
std::string(IR::GetName(Target->Op(Data.Begin())->Op)).c_str());
|
||||
|
||||
Op->Header.Args[0].NodeOffset = Target->Wrapped(ListData.Begin()).NodeOffset;
|
||||
}
|
||||
void SetTrueJumpTarget(IR::IROp_CondJump *Op, OrderedNode *Target) {
|
||||
LogMan::Throw::A(Target->Op(Data.Begin())->Op == OP_CODEBLOCK,
|
||||
"Tried setting CondJump target to %%ssa%d %s",
|
||||
Target->Wrapped(ListData.Begin()).ID(),
|
||||
std::string(IR::GetName(Target->Op(Data.Begin())->Op)).c_str());
|
||||
|
||||
Op->Header.Args[1].NodeOffset = Target->Wrapped(ListData.Begin()).NodeOffset;
|
||||
}
|
||||
void SetFalseJumpTarget(IR::IROp_CondJump *Op, OrderedNode *Target) {
|
||||
LogMan::Throw::A(Target->Op(Data.Begin())->Op == OP_CODEBLOCK,
|
||||
"Tried setting CondJump target to %%ssa%d %s",
|
||||
Target->Wrapped(ListData.Begin()).ID(),
|
||||
std::string(IR::GetName(Target->Op(Data.Begin())->Op)).c_str());
|
||||
|
||||
Op->Header.Args[2].NodeOffset = Target->Wrapped(ListData.Begin()).NodeOffset;
|
||||
}
|
||||
|
||||
void SetJumpTarget(IRPair<IROp_Jump> Op, OrderedNode *Target) {
|
||||
LogMan::Throw::A(Target->Op(Data.Begin())->Op == OP_CODEBLOCK,
|
||||
"Tried setting Jump target to %%ssa%d %s",
|
||||
Target->Wrapped(ListData.Begin()).ID(),
|
||||
std::string(IR::GetName(Target->Op(Data.Begin())->Op)).c_str());
|
||||
|
||||
Op.first->Header.Args[0].NodeOffset = Target->Wrapped(ListData.Begin()).NodeOffset;
|
||||
}
|
||||
void SetTrueJumpTarget(IRPair<IROp_CondJump> Op, OrderedNode *Target) {
|
||||
LogMan::Throw::A(Target->Op(Data.Begin())->Op == OP_CODEBLOCK,
|
||||
"Tried setting CondJump target to %%ssa%d %s",
|
||||
Target->Wrapped(ListData.Begin()).ID(),
|
||||
std::string(IR::GetName(Target->Op(Data.Begin())->Op)).c_str());
|
||||
Op.first->Header.Args[1].NodeOffset = Target->Wrapped(ListData.Begin()).NodeOffset;
|
||||
}
|
||||
void SetFalseJumpTarget(IRPair<IROp_CondJump> Op, OrderedNode *Target) {
|
||||
LogMan::Throw::A(Target->Op(Data.Begin())->Op == OP_CODEBLOCK,
|
||||
"Tried setting CondJump target to %%ssa%d %s",
|
||||
Target->Wrapped(ListData.Begin()).ID(),
|
||||
std::string(IR::GetName(Target->Op(Data.Begin())->Op)).c_str());
|
||||
Op.first->Header.Args[2].NodeOffset = Target->Wrapped(ListData.Begin()).NodeOffset;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
bool IsValueConstant(OrderedNodeWrapper ssa, uint64_t *Constant) {
|
||||
OrderedNode *RealNode = ssa.GetNode(ListData.Begin());
|
||||
FEXCore::IR::IROp_Header *IROp = RealNode->Op(Data.Begin());
|
||||
if (IROp->Op == OP_CONSTANT) {
|
||||
auto Op = IROp->C<IR::IROp_Constant>();
|
||||
*Constant = Op->Constant;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is fairly special in how it operates
|
||||
// Since the node is decoupled from the backing op then we can swap out the backing op without much overhead
|
||||
// This can potentially cause problems where multiple nodes are pointing to the same IROp
|
||||
OrderedNode *ReplaceAllUsesWith(OrderedNode *Node, IROp_Header *Op) {
|
||||
RemoveArgUses(Node);
|
||||
Node->Header.Value.SetOffset(Data.Begin(), reinterpret_cast<uintptr_t>(Op));
|
||||
return Node;
|
||||
}
|
||||
|
||||
// This is similar to the previous op except that we pass in a node
|
||||
// This takes the op backing in the new node and replaces the node in the other node
|
||||
// Again can cause problems where things are pointing to NewNode and haven't been decoupled
|
||||
OrderedNode *ReplaceAllUsesWith(OrderedNode *Node, OrderedNode *NewNode) {
|
||||
RemoveArgUses(Node);
|
||||
Node->Header.Value.NodeOffset = NewNode->Header.Value.NodeOffset;
|
||||
return Node;
|
||||
}
|
||||
|
||||
void ReplaceAllUsesWithInclusive(OrderedNode *Node, OrderedNode *NewNode, IR::NodeWrapperIterator After, IR::NodeWrapperIterator End);
|
||||
void ReplaceNodeArgument(OrderedNode *Node, uint8_t Arg, OrderedNode *NewArg);
|
||||
|
||||
void Remove(OrderedNode *Node);
|
||||
|
||||
void SetPackedRFLAG(bool Lower8, OrderedNode *Src);
|
||||
OrderedNode *GetPackedRFLAG(bool Lower8);
|
||||
|
||||
void CopyData(IREmitter const &rhs) {
|
||||
LogMan::Throw::A(rhs.Data.BackingSize() <= Data.BackingSize(), "Trying to take ownership of data that is too large");
|
||||
LogMan::Throw::A(rhs.ListData.BackingSize() <= ListData.BackingSize(), "Trying to take ownership of data that is too large");
|
||||
Data.CopyData(rhs.Data);
|
||||
ListData.CopyData(rhs.ListData);
|
||||
InvalidNode = rhs.InvalidNode;
|
||||
CurrentWriteCursor = rhs.CurrentWriteCursor;
|
||||
CodeBlocks = rhs.CodeBlocks;
|
||||
}
|
||||
|
||||
void SetWriteCursor(OrderedNode *Node) {
|
||||
CurrentWriteCursor = Node;
|
||||
}
|
||||
|
||||
OrderedNode *GetWriteCursor() {
|
||||
return CurrentWriteCursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This creates an orphaned code node
|
||||
* The IROp backing is in the correct list but the OrderedNode lives outside of the list
|
||||
*
|
||||
* XXX: This is because we don't want code blocks to interleave with current instruction IR ops currently
|
||||
* We can change this behaviour once we remove the old BeginBlock/EndBlock types
|
||||
*
|
||||
* @return OrderedNode
|
||||
*/
|
||||
IRPair<IROp_CodeBlock> CreateCodeNode() {
|
||||
auto CodeNode = _CodeBlock(InvalidNode, InvalidNode, InvalidNode);
|
||||
CodeBlocks.emplace_back(CodeNode);
|
||||
return CodeNode;
|
||||
}
|
||||
|
||||
void SetCodeNodeBegin(OrderedNode *CodeNode, OrderedNode *Begin) {
|
||||
FEXCore::IR::IROp_CodeBlock *IROp = CodeNode->Op(Data.Begin())->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
LogMan::Throw::A(IROp->Header.Op == IROps::OP_CODEBLOCK, "Invalid");
|
||||
IROp->Begin = Begin->Wrapped(ListData.Begin());
|
||||
}
|
||||
|
||||
void SetCodeNodeLast(OrderedNode *CodeNode, OrderedNode *Last) {
|
||||
FEXCore::IR::IROp_CodeBlock *IROp = CodeNode->Op(Data.Begin())->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
LogMan::Throw::A(IROp->Header.Op == IROps::OP_CODEBLOCK, "Invalid");
|
||||
IROp->Last = Last->Wrapped(ListData.Begin());
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Links codeblocks together
|
||||
* Codeblocks are singly linked so we need to walk the list forward if the linked block isn't isn't the last
|
||||
*
|
||||
* eq.
|
||||
* CodeNode->Next -> Next
|
||||
* to
|
||||
* CodeNode->Next -> New -> Next
|
||||
*
|
||||
* @{ */
|
||||
/** @} */
|
||||
void LinkCodeBlocks(OrderedNode *CodeNode, OrderedNode *Next) {
|
||||
FEXCore::IR::IROp_CodeBlock *CurrentIROp = CodeNode->Op(Data.Begin())->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
LogMan::Throw::A(CurrentIROp->Header.Op == IROps::OP_CODEBLOCK, "Invalid");
|
||||
|
||||
OrderedNodeWrapper OldNext = CurrentIROp->Next;
|
||||
// First thing is to assign CodeNode->Next to the incoming node
|
||||
{
|
||||
CurrentIROp->Next = Next->Wrapped(ListData.Begin());
|
||||
}
|
||||
|
||||
// Second thing is to assign the incoming node's Next to what was in CodeNode->Next
|
||||
{
|
||||
FEXCore::IR::IROp_CodeBlock *NextIROp = Next->Op(Data.Begin())->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
auto NewOldNext = NextIROp->Next;
|
||||
NextIROp->Next = OldNext;
|
||||
OldNext = NewOldNext;
|
||||
}
|
||||
}
|
||||
|
||||
IRPair<IROp_CodeBlock> CreateNewCodeBlock();
|
||||
void SetCurrentCodeBlock(OrderedNode *Node);
|
||||
|
||||
protected:
|
||||
void RemoveArgUses(OrderedNode *Node);
|
||||
|
||||
OrderedNode *CreateNode(IROp_Header *Op) {
|
||||
uintptr_t ListBegin = ListData.Begin();
|
||||
size_t Size = sizeof(OrderedNode);
|
||||
void *Ptr = ListData.Allocate(Size);
|
||||
OrderedNode *Node = new (Ptr) OrderedNode();
|
||||
Node->Header.Value.SetOffset(Data.Begin(), reinterpret_cast<uintptr_t>(Op));
|
||||
|
||||
if (CurrentWriteCursor) {
|
||||
CurrentWriteCursor->append(ListBegin, Node);
|
||||
}
|
||||
CurrentWriteCursor = Node;
|
||||
return Node;
|
||||
}
|
||||
|
||||
OrderedNode *GetNode(uint32_t SSANode) {
|
||||
uintptr_t ListBegin = ListData.Begin();
|
||||
OrderedNode *Node = reinterpret_cast<OrderedNode *>(ListBegin + SSANode * sizeof(OrderedNode));
|
||||
return Node;
|
||||
}
|
||||
|
||||
OrderedNode *EmplaceOrphanedNode(OrderedNode *OldNode) {
|
||||
size_t Size = sizeof(OrderedNode);
|
||||
OrderedNode *Ptr = reinterpret_cast<OrderedNode*>(ListData.Allocate(Size));
|
||||
memcpy(Ptr, OldNode, Size);
|
||||
return Ptr;
|
||||
}
|
||||
|
||||
OrderedNode *CurrentWriteCursor = nullptr;
|
||||
|
||||
// These could be combined with a little bit of work to be more efficient with memory usage. Isn't a big deal
|
||||
IntrusiveAllocator Data;
|
||||
IntrusiveAllocator ListData;
|
||||
|
||||
OrderedNode *InvalidNode;
|
||||
OrderedNode *CurrentCodeBlock{};
|
||||
std::vector<OrderedNode*> CodeBlocks;
|
||||
uint64_t Entry;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
from enum import Flag
|
||||
import json
|
||||
import struct
|
||||
import sys
|
||||
from json_config_parse import parse_json
|
||||
|
||||
if (len(sys.argv) < 3):
|
||||
sys.exit()
|
||||
|
||||
output_file = sys.argv[2]
|
||||
asm_file = open(sys.argv[1], "r")
|
||||
asm_text = asm_file.read()
|
||||
asm_file.close()
|
||||
|
||||
json_text = asm_text.split("%ifdef CONFIG")
|
||||
if (len(json_text) > 1):
|
||||
json_text = json_text[1].split("%endif")
|
||||
if (len(json_text) > 1):
|
||||
json_text = json_text[0].strip()
|
||||
|
||||
parse_json(json_text, output_file)
|
||||
@@ -1,247 +0,0 @@
|
||||
from enum import Flag
|
||||
import json
|
||||
import struct
|
||||
import sys
|
||||
|
||||
class Regs(Flag):
|
||||
REG_NONE = 0
|
||||
REG_RIP = (1 << 0)
|
||||
REG_RAX = (1 << 1)
|
||||
REG_RBX = (1 << 2)
|
||||
REG_RCX = (1 << 3)
|
||||
REG_RDX = (1 << 4)
|
||||
REG_RSI = (1 << 5)
|
||||
REG_RDI = (1 << 6)
|
||||
REG_RBP = (1 << 7)
|
||||
REG_RSP = (1 << 8)
|
||||
REG_R8 = (1 << 9)
|
||||
REG_R9 = (1 << 10)
|
||||
REG_R10 = (1 << 11)
|
||||
REG_R11 = (1 << 12)
|
||||
REG_R12 = (1 << 13)
|
||||
REG_R13 = (1 << 14)
|
||||
REG_R14 = (1 << 15)
|
||||
REG_R15 = (1 << 16)
|
||||
REG_XMM0 = (1 << 17)
|
||||
REG_XMM1 = (1 << 18)
|
||||
REG_XMM2 = (1 << 19)
|
||||
REG_XMM3 = (1 << 20)
|
||||
REG_XMM4 = (1 << 21)
|
||||
REG_XMM5 = (1 << 22)
|
||||
REG_XMM6 = (1 << 23)
|
||||
REG_XMM7 = (1 << 24)
|
||||
REG_XMM8 = (1 << 25)
|
||||
REG_XMM9 = (1 << 26)
|
||||
REG_XMM10 = (1 << 27)
|
||||
REG_XMM11 = (1 << 28)
|
||||
REG_XMM12 = (1 << 29)
|
||||
REG_XMM13 = (1 << 30)
|
||||
REG_XMM14 = (1 << 31)
|
||||
REG_XMM15 = (1 << 32)
|
||||
REG_GS = (1 << 33)
|
||||
REG_FS = (1 << 34)
|
||||
REG_FLAGS = (1 << 35)
|
||||
REG_MM0 = (1 << 36)
|
||||
REG_MM1 = (1 << 37)
|
||||
REG_MM2 = (1 << 38)
|
||||
REG_MM3 = (1 << 39)
|
||||
REG_MM4 = (1 << 40)
|
||||
REG_MM5 = (1 << 41)
|
||||
REG_MM6 = (1 << 42)
|
||||
REG_MM7 = (1 << 43)
|
||||
REG_MM8 = (1 << 44)
|
||||
REG_ALL = (1 << 45) - 1
|
||||
REG_INVALID = (1 << 45)
|
||||
|
||||
class ABI(Flag) :
|
||||
ABI_SYSTEMV = 0
|
||||
ABI_WIN64 = 1
|
||||
ABI_NONE = 2
|
||||
|
||||
RegStringLookup = {
|
||||
"NONE": Regs.REG_NONE,
|
||||
"RAX": Regs.REG_RAX,
|
||||
"RIP": Regs.REG_RIP,
|
||||
"RBX": Regs.REG_RBX,
|
||||
"RCX": Regs.REG_RCX,
|
||||
"RDX": Regs.REG_RDX,
|
||||
"RSI": Regs.REG_RSI,
|
||||
"RDI": Regs.REG_RDI,
|
||||
"RBP": Regs.REG_RBP,
|
||||
"RSP": Regs.REG_RSP,
|
||||
"R8": Regs.REG_R8,
|
||||
"R9": Regs.REG_R9,
|
||||
"R10": Regs.REG_R10,
|
||||
"R11": Regs.REG_R11,
|
||||
"R12": Regs.REG_R12,
|
||||
"R13": Regs.REG_R13,
|
||||
"R14": Regs.REG_R14,
|
||||
"R15": Regs.REG_R15,
|
||||
"XMM0": Regs.REG_XMM0,
|
||||
"XMM1": Regs.REG_XMM1,
|
||||
"XMM2": Regs.REG_XMM2,
|
||||
"XMM3": Regs.REG_XMM3,
|
||||
"XMM4": Regs.REG_XMM4,
|
||||
"XMM5": Regs.REG_XMM5,
|
||||
"XMM6": Regs.REG_XMM6,
|
||||
"XMM7": Regs.REG_XMM7,
|
||||
"XMM8": Regs.REG_XMM8,
|
||||
"XMM9": Regs.REG_XMM9,
|
||||
"XMM10": Regs.REG_XMM10,
|
||||
"XMM11": Regs.REG_XMM11,
|
||||
"XMM12": Regs.REG_XMM12,
|
||||
"XMM13": Regs.REG_XMM13,
|
||||
"XMM14": Regs.REG_XMM14,
|
||||
"XMM15": Regs.REG_XMM15,
|
||||
"GS": Regs.REG_GS,
|
||||
"FS": Regs.REG_FS,
|
||||
"FLAGS": Regs.REG_FLAGS,
|
||||
"ALL": Regs.REG_ALL,
|
||||
"MM0": Regs.REG_MM0,
|
||||
"MM1": Regs.REG_MM1,
|
||||
"MM2": Regs.REG_MM2,
|
||||
"MM3": Regs.REG_MM3,
|
||||
"MM4": Regs.REG_MM4,
|
||||
"MM5": Regs.REG_MM5,
|
||||
"MM6": Regs.REG_MM6,
|
||||
"MM7": Regs.REG_MM7,
|
||||
"MM8": Regs.REG_MM8,
|
||||
}
|
||||
|
||||
ABIStringLookup = {
|
||||
"SYSTEMV": ABI.ABI_SYSTEMV,
|
||||
"WIN64": ABI.ABI_WIN64,
|
||||
"NONE": ABI.ABI_NONE,
|
||||
}
|
||||
|
||||
if (len(sys.argv) < 3):
|
||||
sys.exit()
|
||||
|
||||
output_file = sys.argv[2]
|
||||
asm_file = open(sys.argv[1], "r")
|
||||
asm_text = asm_file.read()
|
||||
asm_file.close()
|
||||
|
||||
# Default options
|
||||
OptionMatch = Regs.REG_INVALID
|
||||
OptionIgnore = Regs.REG_NONE
|
||||
OptionABI = ABI.ABI_SYSTEMV
|
||||
OptionStackSize = 4096
|
||||
OptionEntryPoint = 1
|
||||
OptionRegData = {}
|
||||
OptionMemoryRegions = {}
|
||||
|
||||
json_text = asm_text.split("%ifdef CONFIG")
|
||||
if (len(json_text) > 1):
|
||||
json_text = json_text[1].split("%endif")
|
||||
if (len(json_text) > 1):
|
||||
json_text = json_text[0].strip()
|
||||
|
||||
json_object = json.loads(json_text)
|
||||
json_object = {k.upper(): v for k, v in json_object.items()}
|
||||
|
||||
# Begin parsing the JSON
|
||||
if ("MATCH" in json_object):
|
||||
data = json_object["MATCH"]
|
||||
if (type(data) is str):
|
||||
data = [data]
|
||||
|
||||
for data_val in data:
|
||||
data_val = data_val.upper()
|
||||
if not (data_val in RegStringLookup):
|
||||
sys.exit("Invalid Match register option")
|
||||
if (OptionMatch == Regs.REG_INVALID):
|
||||
OptionMatch = Regs.REG_NONE
|
||||
RegOption = RegStringLookup[data_val]
|
||||
OptionMatch = OptionMatch | RegOption
|
||||
|
||||
if ("IGNORE" in json_object):
|
||||
data = json_object["IGNORE"]
|
||||
if (type(data) is str):
|
||||
data = [data]
|
||||
|
||||
for data_val in data:
|
||||
data_val = data_val.upper()
|
||||
if not (data_val in RegStringLookup):
|
||||
sys.exit("Invalid Ignore register option")
|
||||
if (OptionMatch == Regs.REG_INVALID):
|
||||
OptionMatch = Regs.REG_NONE
|
||||
RegOption = RegStringLookup[data_val]
|
||||
OptionIgnore = OptionIgnore | RegOption
|
||||
|
||||
if ("ABI" in json_object):
|
||||
data = json_object["ABI"]
|
||||
data = data.upper()
|
||||
if not (data in ABIStringLookup):
|
||||
sys.exit("Invalid ABI")
|
||||
OptionABI = ABIStringLookup[data]
|
||||
|
||||
if ("STACKSIZE" in json_object):
|
||||
data = json_object["STACKSIZE"]
|
||||
OptionStackSize = int(data, 0)
|
||||
|
||||
if ("ENTRYPOINT" in json_object):
|
||||
data = json_object["ENTRYPOINT"]
|
||||
data = int(data, 0)
|
||||
if (data == 0):
|
||||
sys.exit("Invalid entrypoint of 0")
|
||||
OptionEntryPoint = data
|
||||
|
||||
if ("MEMORYREGIONS" in json_object):
|
||||
data = json_object["MEMORYREGIONS"]
|
||||
if not (type(data) is dict):
|
||||
sys.exit("RegData value must be list of key:value pairs")
|
||||
for data_key, data_val in data.items():
|
||||
OptionMemoryRegions[int(data_key, 0)] = int(data_val, 0);
|
||||
|
||||
if ("REGDATA" in json_object):
|
||||
data = json_object["REGDATA"]
|
||||
if not (type(data) is dict):
|
||||
sys.exit("RegData value must be list of key:value pairs")
|
||||
for data_key, data_val in data.items():
|
||||
data_key = data_key.upper()
|
||||
if not (data_key in RegStringLookup):
|
||||
sys.exit("Invalid RegData register option")
|
||||
|
||||
data_key_index = RegStringLookup[data_key]
|
||||
data_key_values = []
|
||||
|
||||
# Create a list of values for this register as an integer
|
||||
if (type(data_val) is list):
|
||||
for data_key_value in data_val:
|
||||
data_key_values.append(int(data_key_value, 0))
|
||||
else:
|
||||
data_key_values.append(int(data_val, 0))
|
||||
OptionRegData[data_key_index] = data_key_values
|
||||
|
||||
# If Match option wasn't touched then set it to the default
|
||||
if (OptionMatch == Regs.REG_INVALID):
|
||||
OptionMatch = Regs.REG_NONE
|
||||
|
||||
config_file = open(output_file, "wb")
|
||||
config_file.write(struct.pack('Q', OptionMatch.value))
|
||||
config_file.write(struct.pack('Q', OptionIgnore.value))
|
||||
config_file.write(struct.pack('Q', OptionStackSize))
|
||||
config_file.write(struct.pack('Q', OptionEntryPoint))
|
||||
config_file.write(struct.pack('I', OptionABI.value))
|
||||
|
||||
# Number of memory regions
|
||||
config_file.write(struct.pack('I', len(OptionMemoryRegions)))
|
||||
|
||||
# Number of register values
|
||||
config_file.write(struct.pack('I', len(OptionRegData)))
|
||||
|
||||
# Print number of memory regions
|
||||
for reg_key, reg_val in OptionMemoryRegions.items():
|
||||
config_file.write(struct.pack('Q', reg_key))
|
||||
config_file.write(struct.pack('Q', reg_val))
|
||||
|
||||
# Print Register values
|
||||
for reg_key, reg_val in OptionRegData.items():
|
||||
config_file.write(struct.pack('I', len(reg_val)))
|
||||
config_file.write(struct.pack('Q', reg_key.value))
|
||||
for reg_vals in reg_val:
|
||||
config_file.write(struct.pack('Q', reg_vals))
|
||||
|
||||
config_file.close()
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
from enum import Flag
|
||||
import json
|
||||
import struct
|
||||
import sys
|
||||
|
||||
class Regs(Flag):
|
||||
REG_NONE = 0
|
||||
REG_RIP = (1 << 0)
|
||||
REG_RAX = (1 << 1)
|
||||
REG_RBX = (1 << 2)
|
||||
REG_RCX = (1 << 3)
|
||||
REG_RDX = (1 << 4)
|
||||
REG_RSI = (1 << 5)
|
||||
REG_RDI = (1 << 6)
|
||||
REG_RBP = (1 << 7)
|
||||
REG_RSP = (1 << 8)
|
||||
REG_R8 = (1 << 9)
|
||||
REG_R9 = (1 << 10)
|
||||
REG_R10 = (1 << 11)
|
||||
REG_R11 = (1 << 12)
|
||||
REG_R12 = (1 << 13)
|
||||
REG_R13 = (1 << 14)
|
||||
REG_R14 = (1 << 15)
|
||||
REG_R15 = (1 << 16)
|
||||
REG_XMM0 = (1 << 17)
|
||||
REG_XMM1 = (1 << 18)
|
||||
REG_XMM2 = (1 << 19)
|
||||
REG_XMM3 = (1 << 20)
|
||||
REG_XMM4 = (1 << 21)
|
||||
REG_XMM5 = (1 << 22)
|
||||
REG_XMM6 = (1 << 23)
|
||||
REG_XMM7 = (1 << 24)
|
||||
REG_XMM8 = (1 << 25)
|
||||
REG_XMM9 = (1 << 26)
|
||||
REG_XMM10 = (1 << 27)
|
||||
REG_XMM11 = (1 << 28)
|
||||
REG_XMM12 = (1 << 29)
|
||||
REG_XMM13 = (1 << 30)
|
||||
REG_XMM14 = (1 << 31)
|
||||
REG_XMM15 = (1 << 32)
|
||||
REG_GS = (1 << 33)
|
||||
REG_FS = (1 << 34)
|
||||
REG_FLAGS = (1 << 35)
|
||||
REG_MM0 = (1 << 36)
|
||||
REG_MM1 = (1 << 37)
|
||||
REG_MM2 = (1 << 38)
|
||||
REG_MM3 = (1 << 39)
|
||||
REG_MM4 = (1 << 40)
|
||||
REG_MM5 = (1 << 41)
|
||||
REG_MM6 = (1 << 42)
|
||||
REG_MM7 = (1 << 43)
|
||||
REG_MM8 = (1 << 44)
|
||||
REG_ALL = (1 << 45) - 1
|
||||
REG_INVALID = (1 << 45)
|
||||
|
||||
class ABI(Flag) :
|
||||
ABI_SYSTEMV = 0
|
||||
ABI_WIN64 = 1
|
||||
ABI_NONE = 2
|
||||
|
||||
RegStringLookup = {
|
||||
"NONE": Regs.REG_NONE,
|
||||
"RAX": Regs.REG_RAX,
|
||||
"RIP": Regs.REG_RIP,
|
||||
"RBX": Regs.REG_RBX,
|
||||
"RCX": Regs.REG_RCX,
|
||||
"RDX": Regs.REG_RDX,
|
||||
"RSI": Regs.REG_RSI,
|
||||
"RDI": Regs.REG_RDI,
|
||||
"RBP": Regs.REG_RBP,
|
||||
"RSP": Regs.REG_RSP,
|
||||
"R8": Regs.REG_R8,
|
||||
"R9": Regs.REG_R9,
|
||||
"R10": Regs.REG_R10,
|
||||
"R11": Regs.REG_R11,
|
||||
"R12": Regs.REG_R12,
|
||||
"R13": Regs.REG_R13,
|
||||
"R14": Regs.REG_R14,
|
||||
"R15": Regs.REG_R15,
|
||||
"XMM0": Regs.REG_XMM0,
|
||||
"XMM1": Regs.REG_XMM1,
|
||||
"XMM2": Regs.REG_XMM2,
|
||||
"XMM3": Regs.REG_XMM3,
|
||||
"XMM4": Regs.REG_XMM4,
|
||||
"XMM5": Regs.REG_XMM5,
|
||||
"XMM6": Regs.REG_XMM6,
|
||||
"XMM7": Regs.REG_XMM7,
|
||||
"XMM8": Regs.REG_XMM8,
|
||||
"XMM9": Regs.REG_XMM9,
|
||||
"XMM10": Regs.REG_XMM10,
|
||||
"XMM11": Regs.REG_XMM11,
|
||||
"XMM12": Regs.REG_XMM12,
|
||||
"XMM13": Regs.REG_XMM13,
|
||||
"XMM14": Regs.REG_XMM14,
|
||||
"XMM15": Regs.REG_XMM15,
|
||||
"GS": Regs.REG_GS,
|
||||
"FS": Regs.REG_FS,
|
||||
"FLAGS": Regs.REG_FLAGS,
|
||||
"ALL": Regs.REG_ALL,
|
||||
"MM0": Regs.REG_MM0,
|
||||
"MM1": Regs.REG_MM1,
|
||||
"MM2": Regs.REG_MM2,
|
||||
"MM3": Regs.REG_MM3,
|
||||
"MM4": Regs.REG_MM4,
|
||||
"MM5": Regs.REG_MM5,
|
||||
"MM6": Regs.REG_MM6,
|
||||
"MM7": Regs.REG_MM7,
|
||||
"MM8": Regs.REG_MM8,
|
||||
}
|
||||
|
||||
ABIStringLookup = {
|
||||
"SYSTEMV": ABI.ABI_SYSTEMV,
|
||||
"WIN64": ABI.ABI_WIN64,
|
||||
"NONE": ABI.ABI_NONE,
|
||||
}
|
||||
|
||||
def parse_json(json_text, output_file):
|
||||
# Default options
|
||||
OptionMatch = Regs.REG_INVALID
|
||||
OptionIgnore = Regs.REG_NONE
|
||||
OptionABI = ABI.ABI_SYSTEMV
|
||||
OptionStackSize = 4096
|
||||
OptionEntryPoint = 1
|
||||
OptionRegData = {}
|
||||
OptionMemoryRegions = {}
|
||||
|
||||
|
||||
json_object = json.loads(json_text)
|
||||
json_object = {k.upper(): v for k, v in json_object.items()}
|
||||
|
||||
# Begin parsing the JSON
|
||||
if ("MATCH" in json_object):
|
||||
data = json_object["MATCH"]
|
||||
if (type(data) is str):
|
||||
data = [data]
|
||||
|
||||
for data_val in data:
|
||||
data_val = data_val.upper()
|
||||
if not (data_val in RegStringLookup):
|
||||
sys.exit("Invalid Match register option")
|
||||
if (OptionMatch == Regs.REG_INVALID):
|
||||
OptionMatch = Regs.REG_NONE
|
||||
RegOption = RegStringLookup[data_val]
|
||||
OptionMatch = OptionMatch | RegOption
|
||||
|
||||
if ("IGNORE" in json_object):
|
||||
data = json_object["IGNORE"]
|
||||
if (type(data) is str):
|
||||
data = [data]
|
||||
|
||||
for data_val in data:
|
||||
data_val = data_val.upper()
|
||||
if not (data_val in RegStringLookup):
|
||||
sys.exit("Invalid Ignore register option")
|
||||
if (OptionMatch == Regs.REG_INVALID):
|
||||
OptionMatch = Regs.REG_NONE
|
||||
RegOption = RegStringLookup[data_val]
|
||||
OptionIgnore = OptionIgnore | RegOption
|
||||
|
||||
if ("ABI" in json_object):
|
||||
data = json_object["ABI"]
|
||||
data = data.upper()
|
||||
if not (data in ABIStringLookup):
|
||||
sys.exit("Invalid ABI")
|
||||
OptionABI = ABIStringLookup[data]
|
||||
|
||||
if ("STACKSIZE" in json_object):
|
||||
data = json_object["STACKSIZE"]
|
||||
OptionStackSize = int(data, 0)
|
||||
|
||||
if ("ENTRYPOINT" in json_object):
|
||||
data = json_object["ENTRYPOINT"]
|
||||
data = int(data, 0)
|
||||
if (data == 0):
|
||||
sys.exit("Invalid entrypoint of 0")
|
||||
OptionEntryPoint = data
|
||||
|
||||
if ("MEMORYREGIONS" in json_object):
|
||||
data = json_object["MEMORYREGIONS"]
|
||||
if not (type(data) is dict):
|
||||
sys.exit("RegData value must be list of key:value pairs")
|
||||
for data_key, data_val in data.items():
|
||||
OptionMemoryRegions[int(data_key, 0)] = int(data_val, 0);
|
||||
|
||||
if ("REGDATA" in json_object):
|
||||
data = json_object["REGDATA"]
|
||||
if not (type(data) is dict):
|
||||
sys.exit("RegData value must be list of key:value pairs")
|
||||
for data_key, data_val in data.items():
|
||||
data_key = data_key.upper()
|
||||
if not (data_key in RegStringLookup):
|
||||
sys.exit("Invalid RegData register option")
|
||||
|
||||
data_key_index = RegStringLookup[data_key]
|
||||
data_key_values = []
|
||||
|
||||
# Create a list of values for this register as an integer
|
||||
if (type(data_val) is list):
|
||||
for data_key_value in data_val:
|
||||
data_key_values.append(int(data_key_value, 0))
|
||||
else:
|
||||
data_key_values.append(int(data_val, 0))
|
||||
OptionRegData[data_key_index] = data_key_values
|
||||
|
||||
# If Match option wasn't touched then set it to the default
|
||||
if (OptionMatch == Regs.REG_INVALID):
|
||||
OptionMatch = Regs.REG_NONE
|
||||
|
||||
config_file = open(output_file, "wb")
|
||||
config_file.write(struct.pack('Q', OptionMatch.value))
|
||||
config_file.write(struct.pack('Q', OptionIgnore.value))
|
||||
config_file.write(struct.pack('Q', OptionStackSize))
|
||||
config_file.write(struct.pack('Q', OptionEntryPoint))
|
||||
config_file.write(struct.pack('I', OptionABI.value))
|
||||
|
||||
# Number of memory regions
|
||||
config_file.write(struct.pack('I', len(OptionMemoryRegions)))
|
||||
|
||||
# Number of register values
|
||||
config_file.write(struct.pack('I', len(OptionRegData)))
|
||||
|
||||
# Print number of memory regions
|
||||
for reg_key, reg_val in OptionMemoryRegions.items():
|
||||
config_file.write(struct.pack('Q', reg_key))
|
||||
config_file.write(struct.pack('Q', reg_val))
|
||||
|
||||
# Print Register values
|
||||
for reg_key, reg_val in OptionRegData.items():
|
||||
config_file.write(struct.pack('I', len(reg_val)))
|
||||
config_file.write(struct.pack('Q', reg_key.value))
|
||||
for reg_vals in reg_val:
|
||||
config_file.write(struct.pack('Q', reg_vals))
|
||||
|
||||
config_file.close()
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
from enum import Flag
|
||||
import json
|
||||
import struct
|
||||
import sys
|
||||
from json_config_parse import parse_json
|
||||
|
||||
if (len(sys.argv) < 3):
|
||||
sys.exit()
|
||||
|
||||
output_file = sys.argv[2]
|
||||
asm_file = open(sys.argv[1], "r")
|
||||
asm_text = asm_file.read()
|
||||
asm_file.close()
|
||||
|
||||
json_text = asm_text.split(";%ifdef CONFIG")
|
||||
if (len(json_text) > 1):
|
||||
json_text = json_text[1].split(";%endif")
|
||||
if (len(json_text) > 1):
|
||||
json_text = json_text[0].strip()
|
||||
# We need to walk each line of text and remove the comment line
|
||||
json_text = json_text.splitlines(False)
|
||||
parsed_lines = ""
|
||||
for line in json_text:
|
||||
line = line.strip()
|
||||
if (line[0] != ';'):
|
||||
sys.exit("Config line needs to start with a comment character ;")
|
||||
line = line.lstrip(";")
|
||||
|
||||
parsed_lines = parsed_lines + line + '\n'
|
||||
|
||||
parsed_lines = parsed_lines.strip()
|
||||
parse_json(parsed_lines, output_file)
|
||||
@@ -81,7 +81,7 @@ class IRCodeLoader final : public FEXCore::CodeLoader {
|
||||
uint64_t GetFinalRIP() override { return 0; }
|
||||
|
||||
virtual void AddIR(IRHandler Handler) override {
|
||||
Handler(IR->GetEntryRIP(), IR->CreateIRCopy());
|
||||
Handler(IR->GetEntryRIP(), IR);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -108,7 +108,7 @@ int main(int argc, char **argv, char **const envp) {
|
||||
auto Args = FEX::ArgLoader::Get();
|
||||
auto ParsedArgs = FEX::ArgLoader::GetParsedArgs();
|
||||
|
||||
LogMan::Throw::A(!Args.empty(), "Not enough arguments");
|
||||
LogMan::Throw::A(Args.size() > 1, "Not enough arguments");
|
||||
|
||||
FEXCore::Context::InitializeStaticTables();
|
||||
auto SHM = FEXCore::SHM::AllocateSHMRegion(1ULL << 36);
|
||||
@@ -127,7 +127,7 @@ int main(int argc, char **argv, char **const envp) {
|
||||
FEXCore::Context::AddGuestMemoryRegion(CTX, SHM);
|
||||
|
||||
FEX::IRLoader::InitializeStaticTables();
|
||||
FEX::IRLoader::Loader Loader(Args[0]);
|
||||
FEX::IRLoader::Loader Loader(Args[0], Args[1]);
|
||||
|
||||
int Return{};
|
||||
|
||||
@@ -149,12 +149,20 @@ int main(int argc, char **argv, char **const envp) {
|
||||
FEXCore::Context::RunUntilExit(CTX);
|
||||
|
||||
LogMan::Msg::D("Reason we left VM: %d", ShutdownReason);
|
||||
Return = 0;
|
||||
}
|
||||
else {
|
||||
|
||||
// Just re-use compare state. It also checks against the expected values in config.
|
||||
FEXCore::Core::CPUState State;
|
||||
FEXCore::Context::GetCPUState(CTX, &State);
|
||||
bool Passed = Loader.CompareStates(&State);
|
||||
|
||||
LogMan::Msg::I("Passed? %s\n", Passed ? "Yes" : "No");
|
||||
|
||||
Return = Passed ? 0 : -1;
|
||||
}
|
||||
else {
|
||||
LogMan::Msg::E("Couldn't load IR");
|
||||
Return = -1;
|
||||
}
|
||||
}
|
||||
|
||||
FEXCore::Context::DestroyContext(CTX);
|
||||
FEXCore::SHM::DestroyRegion(SHM);
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace FEX::IRLoader {
|
||||
|
||||
template<>
|
||||
std::pair<DecodeFailure, FEXCore::IR::TypeDefinition> Loader::DecodeValue(std::string &Arg) {
|
||||
uint8_t Size{}, Elements{};
|
||||
uint8_t Size{}, Elements{1};
|
||||
int NumArgs = sscanf(Arg.c_str(), "i%hhdv%hhd", &Size, &Elements);
|
||||
|
||||
if (NumArgs != 1 && NumArgs != 2) {
|
||||
@@ -159,9 +159,8 @@ namespace FEX::IRLoader {
|
||||
return {DecodeFailure::DECODE_OKAY, Op->second};
|
||||
}
|
||||
|
||||
Loader::Loader(std::string const &Filename)
|
||||
: Data {8 * 1024 * 1024}
|
||||
, ListData {8 * 1024 * 1024} {
|
||||
Loader::Loader(std::string const &Filename, std::string const &ConfigFilename) {
|
||||
Config.Init(ConfigFilename);
|
||||
std::fstream fp(Filename, std::fstream::binary | std::fstream::in);
|
||||
|
||||
if (!fp.is_open()) {
|
||||
@@ -212,6 +211,12 @@ namespace FEX::IRLoader {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Line[0] == ';') {
|
||||
// This is a comment line
|
||||
// Skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t CurrentPos{};
|
||||
// Let's see if this node is assigning something first
|
||||
if (Line[0] == '%') {
|
||||
@@ -476,18 +481,6 @@ namespace FEX::IRLoader {
|
||||
return true;
|
||||
}
|
||||
|
||||
// IR dispatcher requirements
|
||||
void Loader::ResetWorkingList() {
|
||||
Data.Reset();
|
||||
ListData.Reset();
|
||||
CodeBlocks.clear();
|
||||
CurrentWriteCursor = nullptr;
|
||||
// This is necessary since we do "null" pointer checks
|
||||
InvalidNode = reinterpret_cast<OrderedNode*>(ListData.Allocate(sizeof(OrderedNode)));
|
||||
CurrentCodeBlock = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void InitializeStaticTables() {
|
||||
for (FEXCore::IR::IROps Op = FEXCore::IR::IROps::OP_DUMMY;
|
||||
Op <= FEXCore::IR::IROps::OP_LAST;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
#include <FEXCore/IR/IR.h>
|
||||
#include <FEXCore/IR/IntrusiveIRList.h>
|
||||
#include <FEXCore/IR/IREmitter.h>
|
||||
|
||||
#include "HarnessHelpers.h"
|
||||
|
||||
#include "LogManager.h"
|
||||
|
||||
@@ -25,19 +28,18 @@ enum class DecodeFailure {
|
||||
}
|
||||
|
||||
namespace FEX::IRLoader {
|
||||
class Loader final {
|
||||
class Loader final : public FEXCore::IR::IREmitter {
|
||||
public:
|
||||
Loader(std::string const &Filename);
|
||||
|
||||
IRListView<false> ViewIR() { return IRListView<false>(&Data, &ListData); }
|
||||
IRListView<true> *CreateIRCopy() { return new IRListView<true>(&Data, &ListData); }
|
||||
Loader(std::string const &Filename, std::string const &ConfigFilename);
|
||||
|
||||
bool IsValid() const { return Loaded; }
|
||||
uint64_t GetEntryRIP() const { return EntryRIP; }
|
||||
|
||||
#define IROP_ALLOCATE_HELPERS
|
||||
bool CompareStates(FEXCore::Core::CPUState const* State) {
|
||||
return Config.CompareStates(State, nullptr);
|
||||
}
|
||||
|
||||
#define IROP_PARSER_ALLOCATE_HELPERS
|
||||
#define IROP_DISPATCH_HELPERS
|
||||
#include <FEXCore/IR/IRDefines.inc>
|
||||
private:
|
||||
bool Parse();
|
||||
@@ -46,45 +48,6 @@ namespace FEX::IRLoader {
|
||||
std::vector<std::string> Lines;
|
||||
bool Loaded{};
|
||||
|
||||
// IR definition requirements
|
||||
void ResetWorkingList();
|
||||
|
||||
OrderedNode *CreateNode(IROp_Header *Op) {
|
||||
uintptr_t ListBegin = ListData.Begin();
|
||||
size_t Size = sizeof(OrderedNode);
|
||||
void *Ptr = ListData.Allocate(Size);
|
||||
OrderedNode *Node = new (Ptr) OrderedNode();
|
||||
Node->Header.Value.SetOffset(Data.Begin(), reinterpret_cast<uintptr_t>(Op));
|
||||
|
||||
if (CurrentWriteCursor) {
|
||||
CurrentWriteCursor->append(ListBegin, Node);
|
||||
}
|
||||
CurrentWriteCursor = Node;
|
||||
return Node;
|
||||
}
|
||||
|
||||
OrderedNode *GetNode(uint32_t SSANode) {
|
||||
uintptr_t ListBegin = ListData.Begin();
|
||||
OrderedNode *Node = reinterpret_cast<OrderedNode *>(ListBegin + SSANode * sizeof(OrderedNode));
|
||||
return Node;
|
||||
}
|
||||
|
||||
void SetWriteCursor(OrderedNode *Node) {
|
||||
CurrentWriteCursor = Node;
|
||||
}
|
||||
|
||||
OrderedNode *GetWriteCursor() {
|
||||
return CurrentWriteCursor;
|
||||
}
|
||||
|
||||
IntrusiveAllocator Data;
|
||||
IntrusiveAllocator ListData;
|
||||
|
||||
OrderedNode *InvalidNode;
|
||||
OrderedNode *CurrentCodeBlock{};
|
||||
OrderedNode *CurrentWriteCursor = nullptr;
|
||||
|
||||
std::vector<OrderedNode*> CodeBlocks;
|
||||
std::unordered_map<std::string, OrderedNode*> SSANameMapper;
|
||||
|
||||
template<typename Type>
|
||||
@@ -138,6 +101,7 @@ namespace FEX::IRLoader {
|
||||
|
||||
std::vector<LineDefinition> Defs;
|
||||
LineDefinition *CurrentDef{};
|
||||
FEX::HarnessHelper::ConfigLoader Config;
|
||||
};
|
||||
|
||||
void InitializeStaticTables();
|
||||
|
||||
@@ -25,7 +25,7 @@ foreach(ASM_SRC ${ASM_SOURCES})
|
||||
|
||||
add_custom_command(OUTPUT ${OUTPUT_CONFIG_NAME}
|
||||
DEPENDS "${ASM_SRC}"
|
||||
COMMAND "python3" ARGS "${CMAKE_SOURCE_DIR}/Scripts/json_asm_parse.py" "${ASM_SRC}" "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_CONFIG_NAME}")
|
||||
COMMAND "python3" ARGS "${CMAKE_SOURCE_DIR}/Scripts/json_asm_config_parse.py" "${ASM_SRC}" "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_CONFIG_NAME}")
|
||||
|
||||
list(APPEND ASM_DEPENDS "${OUTPUT_NAME};${OUTPUT_CONFIG_NAME}")
|
||||
|
||||
@@ -70,3 +70,12 @@ endforeach()
|
||||
|
||||
add_custom_target(asm_files ALL
|
||||
DEPENDS "${ASM_DEPENDS}")
|
||||
|
||||
execute_process(COMMAND "nproc" OUTPUT_VARIABLE CORES)
|
||||
string(STRIP ${CORES} CORES)
|
||||
|
||||
add_custom_target(
|
||||
asm_tests
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
USES_TERMINAL
|
||||
COMMAND "ctest" "-j${CORES}" "-R" "\.*.asm")
|
||||
@@ -1 +1,2 @@
|
||||
add_subdirectory(ASM/)
|
||||
add_subdirectory(IR/)
|
||||
@@ -0,0 +1,15 @@
|
||||
;%ifdef CONFIG
|
||||
;{
|
||||
; "RegData": {
|
||||
; "RAX": "0x4142434445464748"
|
||||
; }
|
||||
;}
|
||||
;%endif
|
||||
|
||||
(%ssa1) IRHeader #0x1000, %ssa2, #0
|
||||
(%ssa2) CodeBlock %ssa6, %ssa8, %ssa3
|
||||
(%ssa6 i0) Dummy
|
||||
%Value i64 = Constant #0x4142434445464748
|
||||
(%Store i64) StoreContext %Value i64, #0x8, GPR
|
||||
(%ssa7 i0) Break #4, #4
|
||||
(%ssa8 i0) EndBlock #0x0
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
# Careful. Globbing can't see changes to the contents of files
|
||||
# Need to do a fresh clean to see changes
|
||||
file(GLOB_RECURSE IR_SOURCES CONFIGURE_DEPENDS *.ir)
|
||||
|
||||
set(IR_DEPENDS "")
|
||||
foreach(IR_SRC ${IR_SOURCES})
|
||||
get_filename_component(IR_NAME ${IR_SRC} NAME)
|
||||
|
||||
# Generate a temporary file
|
||||
set(TMP_FILE "${CMAKE_CURRENT_BINARY_DIR}/${IR_TMP}")
|
||||
|
||||
set(OUTPUT_CONFIG_NAME "${IR_NAME}.config.bin")
|
||||
|
||||
add_custom_command(OUTPUT ${OUTPUT_CONFIG_NAME}
|
||||
DEPENDS "${IR_SRC}"
|
||||
COMMAND "python3" ARGS "${CMAKE_SOURCE_DIR}/Scripts/json_ir_config_parse.py" "${IR_SRC}" "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_CONFIG_NAME}")
|
||||
|
||||
list(APPEND IR_DEPENDS "${OUTPUT_CONFIG_NAME}")
|
||||
|
||||
set(TEST_ARGS
|
||||
"-c irint -n 1" "ir_int_1"
|
||||
"-c irint -n 500" "ir_int_500"
|
||||
"-c irint -n 500 -m" "ir_int_500_m"
|
||||
"-c irjit -n 1" "ir_jit_1"
|
||||
"-c irjit -n 500" "ir_jit_500"
|
||||
"-c irjit -n 500 -m" "ir_jit_500_m"
|
||||
"-c llvm -n 1" "ir_llvm_1"
|
||||
"-c llvm -n 500" "ir_llvm_500"
|
||||
"-c llvm -n 500 -m" "ir_llvm_500_m"
|
||||
)
|
||||
|
||||
list(LENGTH TEST_ARGS ARG_COUNT)
|
||||
math(EXPR ARG_COUNT "${ARG_COUNT}-1")
|
||||
foreach(Index RANGE 0 ${ARG_COUNT} 2)
|
||||
math(EXPR TEST_NAME_INDEX "${Index}+1")
|
||||
|
||||
list(GET TEST_ARGS ${Index} ARGS)
|
||||
list(GET TEST_ARGS ${TEST_NAME_INDEX} TEST_DESC)
|
||||
|
||||
set(TEST_NAME "${TEST_DESC}/Test_${IR_NAME}")
|
||||
string(REPLACE " " ";" ARGS_LIST ${ARGS})
|
||||
|
||||
set(TEST_NAME "${TEST_DESC}/Test_${IR_NAME}")
|
||||
string(REPLACE " " ";" ARGS_LIST ${ARGS})
|
||||
add_test(NAME ${TEST_NAME}
|
||||
COMMAND "python3" "${CMAKE_SOURCE_DIR}/Scripts/testharness_runner.py"
|
||||
"${CMAKE_SOURCE_DIR}/unittests/IR/Known_Failures"
|
||||
"Test_${IR_NAME}"
|
||||
"${CMAKE_BINARY_DIR}/Bin/IRLoader"
|
||||
${ARGS_LIST} "${IR_SRC}" "${OUTPUT_CONFIG_NAME}")
|
||||
# This will cause the IR tests to fail if it can't find the TestHarness or IRN files
|
||||
# Prety crap way to work around the fact that tests can't have a build dependency in a different directory
|
||||
# Just make sure to independently run `make all` then `make test`
|
||||
set_property(TEST ${TEST_NAME} APPEND PROPERTY DEPENDS "${CMAKE_BINARY_DIR}/Bin/IRLoader")
|
||||
set_property(TEST ${TEST_NAME} APPEND PROPERTY DEPENDS "${IRC_SRC}")
|
||||
set_property(TEST ${TEST_NAME} APPEND PROPERTY DEPENDS "${OUTPUT_CONFIG_NAME}")
|
||||
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
add_custom_target(ir_files ALL
|
||||
DEPENDS "${IR_DEPENDS}")
|
||||
|
||||
execute_process(COMMAND "nproc" OUTPUT_VARIABLE CORES)
|
||||
string(STRIP ${CORES} CORES)
|
||||
|
||||
add_custom_target(
|
||||
ir_tests
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
USES_TERMINAL
|
||||
COMMAND "ctest" "-j${CORES}" "-R" "\.*.ir")
|
||||
Whitespace-only changes.
Reference in new issue
Block a user