Merge pull request #182 from Sonicadvance1/IRLoader

IR Loader unit tests and surrounding support!
This commit is contained in:
Stefanos Kornilios Mitsis Poiitidis authored and GitHub committed 2020-05-12 19:51:17 +03:00
commit 4e19bea7ae
34 files changed
+1329 -1059

No files matched your search

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