From bb7e58b3f11c1ebbe9a5d2b0291d4c75d48c4ec6 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 8 May 2020 18:40:21 -0700 Subject: [PATCH 01/12] Adds more IR documentation --- External/FEXCore/Source/Interface/IR/IR.json | 66 +++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/External/FEXCore/Source/Interface/IR/IR.json b/External/FEXCore/Source/Interface/IR/IR.json index 153855b9a..c3006d44c 100644 --- a/External/FEXCore/Source/Interface/IR/IR.json +++ b/External/FEXCore/Source/Interface/IR/IR.json @@ -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", From 1b720479c4841c51457b97a777f214280bd85624 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 8 May 2020 18:59:54 -0700 Subject: [PATCH 02/12] Adds a self contained IREmitter class This class does IR emitting and some basic handling of IR specific tasks. This is going to be used to split the x86 OpDispatcher from the IR emission so the IR emitter can be passed around without caring about x86 dependencies. --- External/FEXCore/Source/CMakeLists.txt | 1 + .../FEXCore/Source/Interface/IR/IREmitter.cpp | 95 ++++ .../FEXCore/include/FEXCore/IR/IREmitter.h | 525 ++++++++++++++++++ 3 files changed, 621 insertions(+) create mode 100644 External/FEXCore/Source/Interface/IR/IREmitter.cpp create mode 100644 External/FEXCore/include/FEXCore/IR/IREmitter.h diff --git a/External/FEXCore/Source/CMakeLists.txt b/External/FEXCore/Source/CMakeLists.txt index b5056191f..b2a59e063 100644 --- a/External/FEXCore/Source/CMakeLists.txt +++ b/External/FEXCore/Source/CMakeLists.txt @@ -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 diff --git a/External/FEXCore/Source/Interface/IR/IREmitter.cpp b/External/FEXCore/Source/Interface/IR/IREmitter.cpp new file mode 100644 index 000000000..995b347ff --- /dev/null +++ b/External/FEXCore/Source/Interface/IR/IREmitter.cpp @@ -0,0 +1,95 @@ +#include + +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(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 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()->Begin.GetNode(ListData.Begin())); +} + +} + diff --git a/External/FEXCore/include/FEXCore/IR/IREmitter.h b/External/FEXCore/include/FEXCore/IR/IREmitter.h new file mode 100644 index 000000000..cb69febe0 --- /dev/null +++ b/External/FEXCore/include/FEXCore/IR/IREmitter.h @@ -0,0 +1,525 @@ +#pragma once +#include +#include + +#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 ViewIR() { return IRListView(&Data, &ListData); } + IRListView *CreateIRCopy() { return new IRListView(&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 + + IRPair _Constant(uint8_t Size, uint64_t Constant) { + auto Op = AllocateOp(); + 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 _VBitcast(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { + auto Op = AllocateOp(); + 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 _LoadContext(uint8_t Size, uint32_t Offset, RegisterClassType Class) { + return _LoadContext(Offset, Class, Size); + } + IRPair _StoreContext(RegisterClassType Class, uint8_t Size, uint32_t Offset, OrderedNode *ssa0) { + return _StoreContext(ssa0, Offset, Class, Size); + } + IRPair _Bfe(uint8_t Width, uint8_t lsb, OrderedNode *ssa0) { + return _Bfe(ssa0, Width, lsb); + } + IRPair _Bfi(uint8_t Width, uint8_t lsb, OrderedNode *ssa0, OrderedNode *ssa1) { + return _Bfi(ssa0, ssa1, Width, lsb); + } + IRPair _StoreMem(FEXCore::IR::RegisterClassType Class, uint8_t Size, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t Align = 1) { + return _StoreMem(ssa0, ssa1, Size, Align, Class); + } + IRPair _LoadMem(FEXCore::IR::RegisterClassType Class, uint8_t Size, OrderedNode *ssa0, uint8_t Align = 1) { + return _LoadMem(ssa0, Size, Align, Class); + } + IRPair _Select(uint8_t Cond, OrderedNode *ssa0, OrderedNode *ssa1, OrderedNode *ssa2, OrderedNode *ssa3) { + return _Select(ssa0, ssa1, ssa2, ssa3, {Cond}); + } + IRPair _Sext(uint8_t SrcSize, OrderedNode *ssa0) { + return _Sext(ssa0, SrcSize); + } + IRPair _Zext(uint8_t SrcSize, OrderedNode *ssa0) { + return _Zext(ssa0, SrcSize); + } + IRPair _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 _VInsScalarElement(uint8_t RegisterSize, uint8_t ElementSize, uint8_t DestIdx, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VInsScalarElement(ssa0, ssa1, DestIdx, RegisterSize, ElementSize); + } + IRPair _VExtractElement(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t Index) { + return _VExtractElement(ssa0, Index, RegisterSize, ElementSize); + } + IRPair _VAnd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VAnd(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VAdd(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VSub(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VSub(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VUQAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VUQAdd(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VUQSub(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VUQSub(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VSQAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VSQAdd(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VSQSub(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VSQSub(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VAddP(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VAddP(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VUMin(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VUMin(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VSMin(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VSMin(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VUMax(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VUMax(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VSMax(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VSMax(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VZip(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VZip(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VZip2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VZip2(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VCMPEQ(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VCMPEQ(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VCMPGT(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VCMPGT(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VFAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VFAdd(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VFAddP(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VFAddP(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VFCMPEQ(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VFCMPEQ(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VFCMPNEQ(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VFCMPNEQ(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VFCMPLT(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VFCMPLT(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VFCMPGT(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VFCMPGT(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VFCMPLE(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VFCMPLE(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VFCMPUNO(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VFCMPUNO(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VFCMPORD(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VFCMPORD(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VUShl(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VUShl(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VUShlS(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VUShlS(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VUShrS(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VUShrS(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VSShrS(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VSShrS(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VUShr(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VUShr(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VSShr(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VSShr(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VExtr(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t Index) { + return _VExtr(ssa0, ssa1, Index, RegisterSize, ElementSize); + } + IRPair _VSLI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t ByteShift) { + return _VSLI(ssa0, ByteShift, RegisterSize, ElementSize); + } + IRPair _VSRI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t ByteShift) { + return _VSRI(ssa0, ByteShift, RegisterSize, ElementSize); + } + IRPair _VUShrI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) { + return _VUShrI(ssa0, BitShift, RegisterSize, ElementSize); + } + IRPair _VSShrI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) { + return _VSShrI(ssa0, BitShift, RegisterSize, ElementSize); + } + IRPair _VUShrNI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) { + return _VUShrNI(ssa0, BitShift, RegisterSize, ElementSize); + } + IRPair _VUShrNI2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t BitShift) { + return _VUShrNI2(ssa0, ssa1, BitShift, RegisterSize, ElementSize); + } + IRPair _VShlI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) { + return _VShlI(ssa0, BitShift, RegisterSize, ElementSize); + } + IRPair _VFSqrt(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { + return _VFSqrt(ssa0, RegisterSize, ElementSize); + } + IRPair _VFRSqrt(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { + return _VFRSqrt(ssa0, RegisterSize, ElementSize); + } + IRPair _VNeg(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { + return _VNeg(ssa0, RegisterSize, ElementSize); + } + IRPair _VFNeg(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { + return _VFNeg(ssa0, RegisterSize, ElementSize); + } + IRPair _VNot(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { + return _VNot(ssa0, RegisterSize, ElementSize); + } + IRPair _VSQXTN(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { + return _VSQXTN(ssa0, RegisterSize, ElementSize); + } + IRPair _VSQXTN2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VSQXTN2(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VSQXTUN(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { + return _VSQXTUN(ssa0, RegisterSize, ElementSize); + } + IRPair _VSQXTUN2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VSQXTUN2(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VCastFromGPR(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { + return _VCastFromGPR(ssa0, RegisterSize, ElementSize); + } + IRPair _VExtractToGPR(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t Index) { + return _VExtractToGPR(ssa0, Index, RegisterSize, ElementSize); + } + IRPair _VInsGPR(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t Index) { + return _VInsGPR(ssa0, ssa1, Index, RegisterSize, ElementSize); + } + IRPair _Vector_FToF(uint8_t RegisterSize, uint8_t DstElementSize, uint8_t SrcElementSize, OrderedNode *ssa0) { + return _Vector_FToF(ssa0, SrcElementSize, RegisterSize, DstElementSize); + } + IRPair _Float_FToF(uint8_t DstElementSize, uint8_t SrcElementSize, OrderedNode *ssa0) { + return _Float_FToF(ssa0, SrcElementSize, DstElementSize); + } + IRPair _VUMul(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VUMul(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VSMul(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VSMul(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VUMull(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VUMull(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VSMull(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VSMull(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VUMull2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VUMull2(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VSMull2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { + return _VSMull2(ssa0, ssa1, RegisterSize, ElementSize); + } + IRPair _VSXTL(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { + return _VSXTL(ssa0, RegisterSize, ElementSize); + } + IRPair _VSXTL2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { + return _VSXTL2(ssa0, RegisterSize, ElementSize); + } + IRPair _VUXTL(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { + return _VUXTL(ssa0, RegisterSize, ElementSize); + } + IRPair _VUXTL2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { + return _VUXTL2(ssa0, RegisterSize, ElementSize); + } + IRPair _Jump() { + return _Jump(InvalidNode); + } + IRPair _CondJump(OrderedNode *ssa0) { + return _CondJump(ssa0, InvalidNode, InvalidNode); + } + + IRPair _Phi() { + return _Phi(InvalidNode, InvalidNode, 0); + } + + IRPair _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()->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(); + 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 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 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 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(); + *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(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 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(); + 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(); + 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(); + 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(); + auto NewOldNext = NextIROp->Next; + NextIROp->Next = OldNext; + OldNext = NewOldNext; + } + } + + IRPair 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(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(ListBegin + SSANode * sizeof(OrderedNode)); + return Node; + } + + OrderedNode *EmplaceOrphanedNode(OrderedNode *OldNode) { + size_t Size = sizeof(OrderedNode); + OrderedNode *Ptr = reinterpret_cast(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 CodeBlocks; + uint64_t Entry; +}; + +} From dc87134af3f42f446f598ed80b89c90a55875577 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 8 May 2020 19:05:36 -0700 Subject: [PATCH 03/12] Switches the OpcodeDispatcher over to using the new IREmitter class --- .../Interface/Core/OpcodeDispatcher.cpp | 93 +--- .../Source/Interface/Core/OpcodeDispatcher.h | 497 +----------------- 2 files changed, 4 insertions(+), 586 deletions(-) diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index c74e44c54..9863c80a8 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -3314,33 +3314,6 @@ void OpDispatchBuilder::CMPXCHGPairOp(OpcodeArgs) { SetRFLAG(ZFResult); } -OpDispatchBuilder::IRPair 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()->Begin.GetNode(ListData.Begin())); -} - void OpDispatchBuilder::CreateJumpBlocks(std::vector 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(ListData.Allocate(sizeof(OrderedNode))); DecodeFailure = false; ShouldDump = false; - CurrentCodeBlock = nullptr; } template @@ -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> BaseOpTable = { // Instructions diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h index 7a78f2be1..2b4d0c6db 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h @@ -6,6 +6,7 @@ #include #include #include +#include #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 ViewIR() { return IRListView(&Data, &ListData); } - IRListView *CreateIRCopy() { return new IRListView(&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 - - IRPair _Constant(uint8_t Size, uint64_t Constant) { - auto Op = AllocateOp(); - 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 _VBitcast(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { - auto Op = AllocateOp(); - 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 _LoadContext(uint8_t Size, uint32_t Offset, RegisterClassType Class) { - return _LoadContext(Offset, Class, Size); - } - IRPair _StoreContext(RegisterClassType Class, uint8_t Size, uint32_t Offset, OrderedNode *ssa0) { - return _StoreContext(ssa0, Offset, Class, Size); - } - IRPair _Bfe(uint8_t Width, uint8_t lsb, OrderedNode *ssa0) { - return _Bfe(ssa0, Width, lsb); - } - IRPair _Bfi(uint8_t Width, uint8_t lsb, OrderedNode *ssa0, OrderedNode *ssa1) { - return _Bfi(ssa0, ssa1, Width, lsb); - } - IRPair _StoreMem(FEXCore::IR::RegisterClassType Class, uint8_t Size, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t Align = 1) { - return _StoreMem(ssa0, ssa1, Size, Align, Class); - } - IRPair _LoadMem(FEXCore::IR::RegisterClassType Class, uint8_t Size, OrderedNode *ssa0, uint8_t Align = 1) { - return _LoadMem(ssa0, Size, Align, Class); - } - IRPair _Select(uint8_t Cond, OrderedNode *ssa0, OrderedNode *ssa1, OrderedNode *ssa2, OrderedNode *ssa3) { - return _Select(ssa0, ssa1, ssa2, ssa3, {Cond}); - } - IRPair _Sext(uint8_t SrcSize, OrderedNode *ssa0) { - return _Sext(ssa0, SrcSize); - } - IRPair _Zext(uint8_t SrcSize, OrderedNode *ssa0) { - return _Zext(ssa0, SrcSize); - } - IRPair _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 _VInsScalarElement(uint8_t RegisterSize, uint8_t ElementSize, uint8_t DestIdx, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VInsScalarElement(ssa0, ssa1, DestIdx, RegisterSize, ElementSize); - } - IRPair _VExtractElement(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t Index) { - return _VExtractElement(ssa0, Index, RegisterSize, ElementSize); - } - IRPair _VAnd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VAnd(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VAdd(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VSub(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VSub(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VUQAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VUQAdd(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VUQSub(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VUQSub(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VSQAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VSQAdd(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VSQSub(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VSQSub(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VAddP(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VAddP(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VUMin(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VUMin(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VSMin(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VSMin(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VUMax(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VUMax(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VSMax(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VSMax(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VZip(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VZip(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VZip2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VZip2(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VCMPEQ(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VCMPEQ(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VCMPGT(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VCMPGT(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VFAdd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VFAdd(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VFAddP(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VFAddP(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VFCMPEQ(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VFCMPEQ(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VFCMPNEQ(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VFCMPNEQ(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VFCMPLT(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VFCMPLT(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VFCMPGT(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VFCMPGT(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VFCMPLE(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VFCMPLE(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VFCMPUNO(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VFCMPUNO(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VFCMPORD(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VFCMPORD(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VUShl(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VUShl(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VUShlS(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VUShlS(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VUShrS(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VUShrS(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VSShrS(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VSShrS(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VUShr(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VUShr(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VSShr(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VSShr(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VExtr(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t Index) { - return _VExtr(ssa0, ssa1, Index, RegisterSize, ElementSize); - } - IRPair _VSLI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t ByteShift) { - return _VSLI(ssa0, ByteShift, RegisterSize, ElementSize); - } - IRPair _VSRI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t ByteShift) { - return _VSRI(ssa0, ByteShift, RegisterSize, ElementSize); - } - IRPair _VUShrI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) { - return _VUShrI(ssa0, BitShift, RegisterSize, ElementSize); - } - IRPair _VSShrI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) { - return _VSShrI(ssa0, BitShift, RegisterSize, ElementSize); - } - IRPair _VUShrNI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) { - return _VUShrNI(ssa0, BitShift, RegisterSize, ElementSize); - } - IRPair _VUShrNI2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t BitShift) { - return _VUShrNI2(ssa0, ssa1, BitShift, RegisterSize, ElementSize); - } - IRPair _VShlI(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t BitShift) { - return _VShlI(ssa0, BitShift, RegisterSize, ElementSize); - } - IRPair _VFSqrt(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { - return _VFSqrt(ssa0, RegisterSize, ElementSize); - } - IRPair _VFRSqrt(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { - return _VFRSqrt(ssa0, RegisterSize, ElementSize); - } - IRPair _VNeg(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { - return _VNeg(ssa0, RegisterSize, ElementSize); - } - IRPair _VFNeg(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { - return _VFNeg(ssa0, RegisterSize, ElementSize); - } - IRPair _VNot(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { - return _VNot(ssa0, RegisterSize, ElementSize); - } - IRPair _VSQXTN(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { - return _VSQXTN(ssa0, RegisterSize, ElementSize); - } - IRPair _VSQXTN2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VSQXTN2(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VSQXTUN(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { - return _VSQXTUN(ssa0, RegisterSize, ElementSize); - } - IRPair _VSQXTUN2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VSQXTUN2(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VCastFromGPR(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { - return _VCastFromGPR(ssa0, RegisterSize, ElementSize); - } - IRPair _VExtractToGPR(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t Index) { - return _VExtractToGPR(ssa0, Index, RegisterSize, ElementSize); - } - IRPair _VInsGPR(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1, uint8_t Index) { - return _VInsGPR(ssa0, ssa1, Index, RegisterSize, ElementSize); - } - IRPair _Vector_FToF(uint8_t RegisterSize, uint8_t DstElementSize, uint8_t SrcElementSize, OrderedNode *ssa0) { - return _Vector_FToF(ssa0, SrcElementSize, RegisterSize, DstElementSize); - } - IRPair _Float_FToF(uint8_t DstElementSize, uint8_t SrcElementSize, OrderedNode *ssa0) { - return _Float_FToF(ssa0, SrcElementSize, DstElementSize); - } - IRPair _VUMul(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VUMul(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VSMul(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VSMul(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VUMull(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VUMull(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VSMull(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VSMull(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VUMull2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VUMull2(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VSMull2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) { - return _VSMull2(ssa0, ssa1, RegisterSize, ElementSize); - } - IRPair _VSXTL(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { - return _VSXTL(ssa0, RegisterSize, ElementSize); - } - IRPair _VSXTL2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { - return _VSXTL2(ssa0, RegisterSize, ElementSize); - } - IRPair _VUXTL(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { - return _VUXTL(ssa0, RegisterSize, ElementSize); - } - IRPair _VUXTL2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) { - return _VUXTL2(ssa0, RegisterSize, ElementSize); - } - IRPair _Jump() { - return _Jump(InvalidNode); - } - IRPair _CondJump(OrderedNode *ssa0) { - return _CondJump(ssa0, InvalidNode, InvalidNode); - } - - IRPair _Phi() { - return _Phi(InvalidNode, InvalidNode, 0); - } - - IRPair _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()->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(); - 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 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 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 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(); - *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(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 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(); - 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(); - 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(); - 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(); - auto NewOldNext = NextIROp->Next; - NextIROp->Next = OldNext; - OldNext = NewOldNext; - } - } - - IRPair 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(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(ListBegin + SSANode * sizeof(OrderedNode)); - return Node; - } - - OrderedNode *EmplaceOrphanedNode(OrderedNode *OldNode) { - size_t Size = sizeof(OrderedNode); - OrderedNode *Ptr = reinterpret_cast(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 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 CodeBlocks; bool Multiblock{}; uint64_t Entry; }; From dff0a8fb69fc6a62a58cc29c51ce39df0a4b2422 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 8 May 2020 19:11:31 -0700 Subject: [PATCH 04/12] Switch the IRLoader over to using the IREmitter class Reduces some code duplication --- Source/Tests/IRLoader.cpp | 20 ++++++++---- Source/Tests/IRLoader/Loader.cpp | 17 ++-------- Source/Tests/IRLoader/Loader.h | 56 ++++++-------------------------- 3 files changed, 26 insertions(+), 67 deletions(-) diff --git a/Source/Tests/IRLoader.cpp b/Source/Tests/IRLoader.cpp index a1b829f61..8a6c960ac 100644 --- a/Source/Tests/IRLoader.cpp +++ b/Source/Tests/IRLoader.cpp @@ -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); diff --git a/Source/Tests/IRLoader/Loader.cpp b/Source/Tests/IRLoader/Loader.cpp index c1bd51d0c..f3cbdc4db 100644 --- a/Source/Tests/IRLoader/Loader.cpp +++ b/Source/Tests/IRLoader/Loader.cpp @@ -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()) { @@ -476,18 +475,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(ListData.Allocate(sizeof(OrderedNode))); - CurrentCodeBlock = nullptr; - } - - void InitializeStaticTables() { for (FEXCore::IR::IROps Op = FEXCore::IR::IROps::OP_DUMMY; Op <= FEXCore::IR::IROps::OP_LAST; diff --git a/Source/Tests/IRLoader/Loader.h b/Source/Tests/IRLoader/Loader.h index 1c6429a1d..d69977890 100644 --- a/Source/Tests/IRLoader/Loader.h +++ b/Source/Tests/IRLoader/Loader.h @@ -1,6 +1,9 @@ #pragma once #include #include +#include + +#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 ViewIR() { return IRListView(&Data, &ListData); } - IRListView *CreateIRCopy() { return new IRListView(&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 private: bool Parse(); @@ -46,45 +48,6 @@ namespace FEX::IRLoader { std::vector 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(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(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 CodeBlocks; std::unordered_map SSANameMapper; template @@ -138,6 +101,7 @@ namespace FEX::IRLoader { std::vector Defs; LineDefinition *CurrentDef{}; + FEX::HarnessHelper::ConfigLoader Config; }; void InitializeStaticTables(); From 718b3e6b4cc577cda8710b8c1f7ac5b59563814c Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 8 May 2020 19:19:45 -0700 Subject: [PATCH 05/12] Refactors PassManager to ingest IREmitter class instead of OpDispatcher The Passmanager passes need very little or zero x86 knowledge to do their work. They don't need the x86 OpDispatcher handling code at all. Convert this entirely over to using the IREmitter class so it can actually optimize IR code from the IRLoader frontend --- .../FEXCore/Source/Interface/Core/Core.cpp | 12 +- .../Source/Interface/IR/PassManager.cpp | 24 ++-- .../FEXCore/Source/Interface/IR/PassManager.h | 52 +++++++- .../Source/Interface/IR/Passes/ConstProp.cpp | 111 ++++++++++-------- .../IR/Passes/DeadCodeElimination.cpp | 8 +- .../IR/Passes/DeadContextStoreElimination.cpp | 44 +++---- .../Interface/IR/Passes/IRCompaction.cpp | 9 +- .../Interface/IR/Passes/IRValidation.cpp | 10 +- .../Interface/IR/Passes/PhiValidation.cpp | 6 +- .../RedundantFlagCalculationElimination.cpp | 8 +- .../IR/Passes/RegisterAllocationPass.cpp | 52 ++++---- .../IR/Passes/SyscallOptimization.cpp | 12 +- .../IR/Passes/ValueDominanceValidation.cpp | 6 +- .../FEXCore/include/FEXCore/Core/CodeLoader.h | 5 +- Source/Tests/IRLoader.cpp | 2 +- 15 files changed, 210 insertions(+), 151 deletions(-) diff --git a/External/FEXCore/Source/Interface/Core/Core.cpp b/External/FEXCore/Source/Interface/Core/Core.cpp index 23c98593e..bb0723795 100644 --- a/External/FEXCore/Source/Interface/Core/Core.cpp +++ b/External/FEXCore/Source/Interface/Core/Core.cpp @@ -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 *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; diff --git a/External/FEXCore/Source/Interface/IR/PassManager.cpp b/External/FEXCore/Source/Interface/IR/PassManager.cpp index 1b0a8cb67..93e525d03 100644 --- a/External/FEXCore/Source/Interface/IR/PassManager.cpp +++ b/External/FEXCore/Source/Interface/IR/PassManager.cpp @@ -5,34 +5,34 @@ namespace FEXCore::IR { void PassManager::AddDefaultPasses() { - Passes.emplace_back(std::unique_ptr(CreateContextLoadStoreElimination())); - Passes.emplace_back(std::unique_ptr(CreateConstProp())); - ////// Passes.emplace_back(std::unique_ptr(CreateDeadFlagCalculationEliminination())); - Passes.emplace_back(std::unique_ptr(CreateSyscallOptimization())); - Passes.emplace_back(std::unique_ptr(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(CreateIRCompaction())); + InsertPass(CreateIRCompaction()); } void PassManager::AddDefaultValidationPasses() { #ifndef NDEBUG - ValidationPasses.emplace_back(std::unique_ptr(Validation::CreatePhiValidation())); - ValidationPasses.emplace_back(std::unique_ptr(Validation::CreateIRValidation())); - ValidationPasses.emplace_back(std::unique_ptr(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 diff --git a/External/FEXCore/Source/Interface/IR/PassManager.h b/External/FEXCore/Source/Interface/IR/PassManager.h index 9a5c202ba..35b2d84af 100644 --- a/External/FEXCore/Source/Interface/IR/PassManager.h +++ b/External/FEXCore/Source/Interface/IR/PassManager.h @@ -1,32 +1,80 @@ #pragma once #include +#include +#include #include #include +namespace FEXCore { +class SyscallHandler; +} + namespace FEXCore::IR { class OpDispatchBuilder; +class SyscallOptimization; + +using ShouldExitHandler = std::function; 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(RAPass); + } + + void RegisterSyscallHandler(SyscallHandler *Handler) { + SyscallHandler = Handler; + } + +protected: + ShouldExitHandler ExitHandler; + SyscallHandler *SyscallHandler; private: + Pass *RAPass{}; + std::vector> Passes; #ifndef NDEBUG std::vector> ValidationPasses; + void InsertValidationPass(Pass *Pass) { + Pass->RegisterPassManager(this); + ValidationPasses.emplace_back(Pass); + } #endif }; } diff --git a/External/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp b/External/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp index 9cc8e0edd..9e106999b 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/ConstProp.cpp @@ -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(); @@ -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(); + auto Op = IROp->CW(); 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(); 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(); 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; } diff --git a/External/FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp b/External/FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp index 2e44a2360..7a83b7fb4 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/DeadCodeElimination.cpp @@ -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; } diff --git a/External/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp b/External/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp index 056e828cb..7a85bec1a 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/DeadContextStoreElimination.cpp @@ -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 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; } diff --git a/External/FEXCore/Source/Interface/IR/Passes/IRCompaction.cpp b/External/FEXCore/Source/Interface/IR/Passes/IRCompaction.cpp index 0b95bdc01..f1d701874 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/IRCompaction.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/IRCompaction.cpp @@ -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; } diff --git a/External/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp b/External/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp index 698074056..9891d7884 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/IRValidation.cpp @@ -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 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) { diff --git a/External/FEXCore/Source/Interface/IR/Passes/PhiValidation.cpp b/External/FEXCore/Source/Interface/IR/Passes/PhiValidation.cpp index a304e0a7d..ccce4abed 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/PhiValidation.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/PhiValidation.cpp @@ -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(); diff --git a/External/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp b/External/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp index af633304a..26883d5e2 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp @@ -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 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; } } diff --git a/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp b/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp index b757567b4..dbee50588 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp @@ -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 LocalCompaction; - void SpillRegisters(FEXCore::IR::OpDispatchBuilder *Disp); + void SpillRegisters(FEXCore::IR::IREmitter *IREmit); std::vector LiveRanges; @@ -355,11 +355,11 @@ namespace FEXCore::IR { void CalculateNodeInterference(FEXCore::IR::IRListView *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(); @@ -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; } diff --git a/External/FEXCore/Source/Interface/IR/Passes/SyscallOptimization.cpp b/External/FEXCore/Source/Interface/IR/Passes/SyscallOptimization.cpp index 3e25da55c..1bb3f7c5a 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/SyscallOptimization.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/SyscallOptimization.cpp @@ -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; } diff --git a/External/FEXCore/Source/Interface/IR/Passes/ValueDominanceValidation.cpp b/External/FEXCore/Source/Interface/IR/Passes/ValueDominanceValidation.cpp index 52cf2b448..c4ace11e0 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/ValueDominanceValidation.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/ValueDominanceValidation.cpp @@ -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; diff --git a/External/FEXCore/include/FEXCore/Core/CodeLoader.h b/External/FEXCore/include/FEXCore/Core/CodeLoader.h index 7f71ab19e..a84590f93 100644 --- a/External/FEXCore/include/FEXCore/Core/CodeLoader.h +++ b/External/FEXCore/include/FEXCore/Core/CodeLoader.h @@ -5,8 +5,7 @@ namespace FEXCore { namespace IR { -template -class IRListView; +class IREmitter; } /** @@ -80,7 +79,7 @@ public: virtual void GetAuxv(uint64_t& addr, uint64_t& size) {} - using IRHandler = std::function *IR)>; + using IRHandler = std::function; virtual void AddIR(IRHandler Handler) {} }; diff --git a/Source/Tests/IRLoader.cpp b/Source/Tests/IRLoader.cpp index 8a6c960ac..405032663 100644 --- a/Source/Tests/IRLoader.cpp +++ b/Source/Tests/IRLoader.cpp @@ -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: From cda734cea29fd6a9b70978bb4a7328da18b718c5 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 8 May 2020 19:24:52 -0700 Subject: [PATCH 06/12] Adds support for comment lines in the IR parser Comments start with ';' on the line. You can also technically have comments after IR ops but that isn't explicitly supported, just an artifact of how things are parsed. --- Source/Tests/IRLoader/Loader.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Tests/IRLoader/Loader.cpp b/Source/Tests/IRLoader/Loader.cpp index f3cbdc4db..25c672766 100644 --- a/Source/Tests/IRLoader/Loader.cpp +++ b/Source/Tests/IRLoader/Loader.cpp @@ -211,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] == '%') { From b70176c99a61d4b5c7591c456f1c519525a2b0af Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 8 May 2020 19:25:45 -0700 Subject: [PATCH 07/12] Fixes bug in TypeDefinition elements Elements of 0 isn't a real thing. Would cause problems in the CPU backends. Ensure we have at least 1 element (Which technically means a vector 1 element wide, but that just defines as a scalar) --- Source/Tests/IRLoader/Loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tests/IRLoader/Loader.cpp b/Source/Tests/IRLoader/Loader.cpp index 25c672766..865080589 100644 --- a/Source/Tests/IRLoader/Loader.cpp +++ b/Source/Tests/IRLoader/Loader.cpp @@ -100,7 +100,7 @@ namespace FEX::IRLoader { template<> std::pair 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) { From 9664e24961ad81fb100013867be815d84e601d21 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 8 May 2020 19:28:59 -0700 Subject: [PATCH 08/12] Adds a cmake target for running ASM tests --- unittests/ASM/CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/unittests/ASM/CMakeLists.txt b/unittests/ASM/CMakeLists.txt index 59befec63..1968dc882 100644 --- a/unittests/ASM/CMakeLists.txt +++ b/unittests/ASM/CMakeLists.txt @@ -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") From 31afb62a2a9d3006fa9930c713aa5421f04872cc Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 8 May 2020 19:30:28 -0700 Subject: [PATCH 09/12] Split python unit test ASM config parser code out to an independent file This will be needed once we have multiple file type configs getting parsed --- Scripts/json_asm_config_parse.py | 21 +++ Scripts/json_asm_parse.py | 247 ------------------------------- Scripts/json_config_parse.py | 235 +++++++++++++++++++++++++++++ unittests/ASM/CMakeLists.txt | 2 +- 4 files changed, 257 insertions(+), 248 deletions(-) create mode 100644 Scripts/json_asm_config_parse.py delete mode 100644 Scripts/json_asm_parse.py create mode 100644 Scripts/json_config_parse.py diff --git a/Scripts/json_asm_config_parse.py b/Scripts/json_asm_config_parse.py new file mode 100644 index 000000000..c3613a8e4 --- /dev/null +++ b/Scripts/json_asm_config_parse.py @@ -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) diff --git a/Scripts/json_asm_parse.py b/Scripts/json_asm_parse.py deleted file mode 100644 index 3f1021e19..000000000 --- a/Scripts/json_asm_parse.py +++ /dev/null @@ -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() - diff --git a/Scripts/json_config_parse.py b/Scripts/json_config_parse.py new file mode 100644 index 000000000..95216a54c --- /dev/null +++ b/Scripts/json_config_parse.py @@ -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() + diff --git a/unittests/ASM/CMakeLists.txt b/unittests/ASM/CMakeLists.txt index 1968dc882..24967f6ab 100644 --- a/unittests/ASM/CMakeLists.txt +++ b/unittests/ASM/CMakeLists.txt @@ -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}") From 6cdd3916c0e5b444784dc7b5d7696762ecc7656e Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 8 May 2020 19:35:07 -0700 Subject: [PATCH 10/12] Adds IR based unit test support. Now that everything is punched through that is necessary, we can now have IR based unit tests that will be used to ensure the IR is working correctly. Specifically this is necessary for quick iterating on RA testing. --- Scripts/json_ir_config_parse.py | 32 +++++++++++++++ unittests/CMakeLists.txt | 1 + unittests/IR/Basic/SetGPR.ir | 15 +++++++ unittests/IR/CMakeLists.txt | 72 +++++++++++++++++++++++++++++++++ unittests/IR/Known_Failures | 0 5 files changed, 120 insertions(+) create mode 100644 Scripts/json_ir_config_parse.py create mode 100644 unittests/IR/Basic/SetGPR.ir create mode 100644 unittests/IR/CMakeLists.txt create mode 100644 unittests/IR/Known_Failures diff --git a/Scripts/json_ir_config_parse.py b/Scripts/json_ir_config_parse.py new file mode 100644 index 000000000..ebe95b63f --- /dev/null +++ b/Scripts/json_ir_config_parse.py @@ -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) diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index c0815455f..fc5e2b74b 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(ASM/) +add_subdirectory(IR/) diff --git a/unittests/IR/Basic/SetGPR.ir b/unittests/IR/Basic/SetGPR.ir new file mode 100644 index 000000000..6218d42ea --- /dev/null +++ b/unittests/IR/Basic/SetGPR.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 diff --git a/unittests/IR/CMakeLists.txt b/unittests/IR/CMakeLists.txt new file mode 100644 index 000000000..c6923fe23 --- /dev/null +++ b/unittests/IR/CMakeLists.txt @@ -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") diff --git a/unittests/IR/Known_Failures b/unittests/IR/Known_Failures new file mode 100644 index 000000000..e69de29bb From c6616a08664c24ff3177fe5a4a1304a895d33ce9 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 8 May 2020 19:38:12 -0700 Subject: [PATCH 11/12] Switches over the unit test running in the github yml file to target specific unit test categories Allows us to split out the ASM unit tests and the IR unit tests to their own independent steps in the yml file --- .github/workflows/ccpp.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 337601544..af16a69ed 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -45,9 +45,14 @@ jobs: # Execute the build. You can specify a specific target with "--target " 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 From e6e4634651ac6dcc3ed50416fdab9d3fdd18f5ac Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sat, 9 May 2020 12:21:54 -0700 Subject: [PATCH 12/12] Ensures LLVM JIT doesn't crash if DebugInfo isn't provided IR doesn't have DebugInfo, all the other backends already handle this --- External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp b/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp index cc4b0b02f..1d3c26276 100644 --- a/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp +++ b/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp @@ -4065,7 +4065,9 @@ void* FEXCore::CPU::LLVMJITCore::CompileCode(FEXCore::IR::IRListView const JITState.Functions.emplace_back(Engine); - DebugData->HostCodeSize = JITState.MemManager->GetLastCodeAllocation(); + if (DebugData) { + DebugData->HostCodeSize = JITState.MemManager->GetLastCodeAllocation(); + } void *FunctionPtr = reinterpret_cast(Engine->getFunctionAddress(FunctionName.str())); return FunctionPtr;