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 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/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/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; 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; }; 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", 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/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/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; +}; + +} 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/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/Source/Tests/IRLoader.cpp b/Source/Tests/IRLoader.cpp index a1b829f61..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: @@ -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..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) { @@ -159,9 +159,8 @@ namespace FEX::IRLoader { return {DecodeFailure::DECODE_OKAY, Op->second}; } - Loader::Loader(std::string const &Filename) - : Data {8 * 1024 * 1024} - , ListData {8 * 1024 * 1024} { + Loader::Loader(std::string const &Filename, std::string const &ConfigFilename) { + Config.Init(ConfigFilename); std::fstream fp(Filename, std::fstream::binary | std::fstream::in); if (!fp.is_open()) { @@ -212,6 +211,12 @@ namespace FEX::IRLoader { continue; } + if (Line[0] == ';') { + // This is a comment line + // Skip it + continue; + } + size_t CurrentPos{}; // Let's see if this node is assigning something first if (Line[0] == '%') { @@ -476,18 +481,6 @@ namespace FEX::IRLoader { return true; } - // IR dispatcher requirements - void Loader::ResetWorkingList() { - Data.Reset(); - ListData.Reset(); - CodeBlocks.clear(); - CurrentWriteCursor = nullptr; - // This is necessary since we do "null" pointer checks - InvalidNode = reinterpret_cast(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(); diff --git a/unittests/ASM/CMakeLists.txt b/unittests/ASM/CMakeLists.txt index 59befec63..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}") @@ -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") 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