mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-14 11:01:01 +02:00
IR: Adds Option to run the IRDumper with more configurations
This is incredibly useful and I find myself hacking this feature in every time I am optimizing IR. Adds a new configuration option which allows dumping IR at various times. Before any optimization passes has happened After all optimizations passes have happened Before and After each IRPass to see what is breaking something. Needs #2864 merged first
This commit is contained in:
1 parent
53ac8abce9
commit
35e97ec9bc
13 files changed
+183
-34
No files matched your search
+1
@@ -133,6 +133,7 @@ set (SRCS
|
||||
Interface/IR/Passes/DeadCodeElimination.cpp
|
||||
Interface/IR/Passes/DeadContextStoreElimination.cpp
|
||||
Interface/IR/Passes/IRCompaction.cpp
|
||||
Interface/IR/Passes/IRDumperPass.cpp
|
||||
Interface/IR/Passes/IRValidation.cpp
|
||||
Interface/IR/Passes/RAValidation.cpp
|
||||
Interface/IR/Passes/LongDivideRemovalPass.cpp
|
||||
|
||||
@@ -198,6 +198,24 @@
|
||||
"[no, stdout, stderr, <Folder>]"
|
||||
]
|
||||
},
|
||||
"PassManagerDumpIR": {
|
||||
"Type": "strenum",
|
||||
"Default": "FEXCore::Config::PassManagerDumpIR::OFF",
|
||||
"Enums": {
|
||||
"BEFOREOPT": "beforeopt",
|
||||
"AFTEROPT": "afteropt",
|
||||
"BEFORE": "beforepass",
|
||||
"AFTER": "afterpass"
|
||||
},
|
||||
"Desc": [
|
||||
"Allows controlling when FEX dumps its IR.",
|
||||
"\toff: IR dumping will be disabled",
|
||||
"\tbeforeopt: Dump IR before any optimizations",
|
||||
"\tafteropt: Dump IR after all optimizations",
|
||||
"\tbeforepass: Dump IR before every optimization pass",
|
||||
"\tafterpass: Dump IR after every optimization pass"
|
||||
]
|
||||
},
|
||||
"DumpGPRs": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
|
||||
@@ -230,7 +230,6 @@ namespace FEXCore::Context {
|
||||
FEX_CONFIG_OPT(ThunkHostLibsPath, THUNKHOSTLIBS);
|
||||
FEX_CONFIG_OPT(ThunkHostLibsPath32, THUNKHOSTLIBS32);
|
||||
FEX_CONFIG_OPT(ThunkConfigFile, THUNKCONFIG);
|
||||
FEX_CONFIG_OPT(DumpIR, DUMPIR);
|
||||
FEX_CONFIG_OPT(StaticRegisterAllocation, SRA);
|
||||
FEX_CONFIG_OPT(GlobalJITNaming, GLOBALJITNAMING);
|
||||
FEX_CONFIG_OPT(LibraryJITNaming, LIBRARYJITNAMING);
|
||||
|
||||
+8
-25
@@ -603,6 +603,8 @@ namespace FEXCore::Context {
|
||||
ERROR_AND_DIE_FMT("Unknown core configuration");
|
||||
break;
|
||||
}
|
||||
|
||||
Thread->PassManager->Finalize();
|
||||
}
|
||||
|
||||
FEXCore::Core::InternalThreadState* ContextImpl::CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID) {
|
||||
@@ -729,30 +731,11 @@ namespace FEXCore::Context {
|
||||
}
|
||||
|
||||
static void IRDumper(FEXCore::Core::InternalThreadState *Thread, IR::IREmitter *IREmitter, uint64_t GuestRIP, IR::RegisterAllocationData* RA) {
|
||||
FEXCore::File::File FD;
|
||||
const auto DumpIRStr = static_cast<ContextImpl*>(Thread->CTX)->Config.DumpIR();
|
||||
|
||||
// DumpIRStr might be no if not dumping but ShouldDump is set in OpDisp
|
||||
if (DumpIRStr =="stderr" || DumpIRStr =="no") {
|
||||
FD = FEXCore::File::File::GetStdERR();
|
||||
}
|
||||
else if (DumpIRStr =="stdout") {
|
||||
FD = FEXCore::File::File::GetStdOUT();
|
||||
}
|
||||
else {
|
||||
const auto fileName = fextl::fmt::format("{}/{:x}{}", DumpIRStr, GuestRIP, RA ? "-post.ir" : "-pre.ir");
|
||||
FD = FEXCore::File::File(fileName.c_str(),
|
||||
FEXCore::File::FileModes::WRITE |
|
||||
FEXCore::File::FileModes::CREATE |
|
||||
FEXCore::File::FileModes::TRUNCATE);
|
||||
}
|
||||
|
||||
if (FD.IsValid()) {
|
||||
fextl::stringstream out;
|
||||
auto NewIR = IREmitter->ViewIR();
|
||||
FEXCore::IR::Dump(&out, &NewIR, RA);
|
||||
fextl::fmt::print(FD, "IR-{} 0x{:x}:\n{}\n@@@@@\n", RA ? "post" : "pre", GuestRIP, out.str());
|
||||
}
|
||||
FEXCore::File::File FD = FEXCore::File::File::GetStdERR();
|
||||
fextl::stringstream out;
|
||||
auto NewIR = IREmitter->ViewIR();
|
||||
FEXCore::IR::Dump(&out, &NewIR, RA);
|
||||
fextl::fmt::print(FD, "IR-ShouldDump-{} 0x{:x}:\n{}\n@@@@@\n", RA ? "post" : "pre", GuestRIP, out.str());
|
||||
};
|
||||
|
||||
static void ValidateIR(ContextImpl *ctx, IR::IREmitter *IREmitter) {
|
||||
@@ -920,7 +903,7 @@ namespace FEXCore::Context {
|
||||
|
||||
IR::IREmitter *IREmitter = Thread->OpDispatcher.get();
|
||||
|
||||
auto ShouldDump = static_cast<ContextImpl*>(Thread->CTX)->Config.DumpIR() != "no" || Thread->OpDispatcher->ShouldDumpIR();
|
||||
auto ShouldDump = Thread->OpDispatcher->ShouldDumpIR();
|
||||
// Debug
|
||||
{
|
||||
if (ShouldDump) {
|
||||
|
||||
@@ -4734,7 +4734,7 @@ void OpDispatchBuilder::CreateJumpBlocks(fextl::vector<FEXCore::Frontend::Decode
|
||||
|
||||
void OpDispatchBuilder::BeginFunction(uint64_t RIP, fextl::vector<FEXCore::Frontend::Decoder::DecodedBlocks> const *Blocks) {
|
||||
Entry = RIP;
|
||||
auto IRHeader = _IRHeader(InvalidNode, 0);
|
||||
auto IRHeader = _IRHeader(InvalidNode, RIP, 0);
|
||||
CreateJumpBlocks(Blocks);
|
||||
|
||||
auto Block = GetNewJumpBlock(RIP);
|
||||
|
||||
+1
-1
@@ -208,7 +208,7 @@ namespace FEXCore {
|
||||
auto Result = Thread->CTX->AddCustomIREntrypoint(
|
||||
args->original_callee,
|
||||
[CTX, GuestThunkEntrypoint = args->target_addr](uintptr_t Entrypoint, FEXCore::IR::IREmitter *emit) {
|
||||
auto IRHeader = emit->_IRHeader(emit->Invalid(), 0);
|
||||
auto IRHeader = emit->_IRHeader(emit->Invalid(), Entrypoint, 0);
|
||||
auto Block = emit->CreateCodeNode();
|
||||
IRHeader.first->Blocks = emit->WrapNode(Block);
|
||||
emit->SetCurrentCodeBlock(Block);
|
||||
|
||||
+1
-1
@@ -160,7 +160,7 @@
|
||||
"HasSideEffects": true,
|
||||
"SwitchGen": false
|
||||
},
|
||||
"IRHeader SSA:$Blocks, u32:$BlockCount": {
|
||||
"IRHeader SSA:$Blocks, u64:$OriginalRIP, u32:$BlockCount": {
|
||||
"SwitchGen": false
|
||||
},
|
||||
"CodeBlock SSA:$Begin, SSA:$Last": {
|
||||
|
||||
+5
-1
@@ -533,11 +533,15 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto OriginalRIP = DecodeValue<uint64_t>(Def.Args[1]);
|
||||
|
||||
if (!CheckPrintError(Def, OriginalRIP.first)) return false;
|
||||
|
||||
auto CodeBlockCount = DecodeValue<uint64_t>(Def.Args[1]);
|
||||
|
||||
if (!CheckPrintError(Def, CodeBlockCount.first)) return false;
|
||||
|
||||
IRHeader = _IRHeader(InvalidNode, CodeBlockCount.second);
|
||||
IRHeader = _IRHeader(InvalidNode, OriginalRIP.second, CodeBlockCount.second);
|
||||
}
|
||||
|
||||
SetWriteCursor(nullptr); // isolate the header from everything following
|
||||
|
||||
@@ -17,6 +17,54 @@ $end_info$
|
||||
namespace FEXCore::IR {
|
||||
class IREmitter;
|
||||
|
||||
void PassManager::Finalize() {
|
||||
if (!PassManagerDumpIR()) {
|
||||
// Not configured to dump any IR, just return.
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = Passes.begin();
|
||||
// Walk the passes and add them where asked.
|
||||
if (PassManagerDumpIR() & FEXCore::Config::PassManagerDumpIR::BEFORE) {
|
||||
// Insert at the start.
|
||||
it = InsertAt(it, Debug::CreateIRDumper());
|
||||
++it; // Skip what we inserted.
|
||||
}
|
||||
|
||||
if ((PassManagerDumpIR() & FEXCore::Config::PassManagerDumpIR::BEFOREOPT) ||
|
||||
(PassManagerDumpIR() & FEXCore::Config::PassManagerDumpIR::AFTEROPT)) {
|
||||
|
||||
bool SkipFirstBefore = PassManagerDumpIR() & FEXCore::Config::PassManagerDumpIR::BEFORE;
|
||||
for (; it != Passes.end();) {
|
||||
if (PassManagerDumpIR() & FEXCore::Config::PassManagerDumpIR::BEFOREOPT) {
|
||||
if (SkipFirstBefore) {
|
||||
// If we need to skip the first one, then continue.
|
||||
SkipFirstBefore = false;
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Insert before
|
||||
it = InsertAt(it, Debug::CreateIRDumper());
|
||||
++it; // Skip what we inserted.
|
||||
}
|
||||
|
||||
++it; // Skip current pass.
|
||||
if (PassManagerDumpIR() & FEXCore::Config::PassManagerDumpIR::AFTEROPT) {
|
||||
// Insert after
|
||||
it = InsertAt(it, Debug::CreateIRDumper());
|
||||
++it; // Skip what we inserted.
|
||||
}
|
||||
}
|
||||
}
|
||||
if (PassManagerDumpIR() & FEXCore::Config::PassManagerDumpIR::AFTER) {
|
||||
if (!(PassManagerDumpIR() & FEXCore::Config::PassManagerDumpIR::AFTEROPT)) {
|
||||
// Insert final IRDumper.
|
||||
it = InsertAt(it, Debug::CreateIRDumper());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PassManager::AddDefaultPasses(FEXCore::Context::ContextImpl *ctx, bool InlineConstants, bool StaticRegisterAllocation) {
|
||||
FEX_CONFIG_OPT(DisablePasses, O0);
|
||||
|
||||
|
||||
+10
-3
@@ -49,8 +49,7 @@ public:
|
||||
void AddDefaultPasses(FEXCore::Context::ContextImpl *ctx, bool InlineConstants, bool StaticRegisterAllocation);
|
||||
void AddDefaultValidationPasses();
|
||||
Pass* InsertPass(fextl::unique_ptr<Pass> Pass, fextl::string Name = "") {
|
||||
Pass->RegisterPassManager(this);
|
||||
auto PassPtr = Passes.emplace_back(std::move(Pass)).get();
|
||||
auto PassPtr = InsertAt(Passes.end(), std::move(Pass))->get();
|
||||
|
||||
if (!Name.empty()) {
|
||||
NameToPassMaping[Name] = PassPtr;
|
||||
@@ -83,12 +82,19 @@ public:
|
||||
SyscallHandler = Handler;
|
||||
}
|
||||
|
||||
void Finalize();
|
||||
|
||||
protected:
|
||||
ShouldExitHandler ExitHandler;
|
||||
FEXCore::HLE::SyscallHandler *SyscallHandler;
|
||||
|
||||
private:
|
||||
fextl::vector<fextl::unique_ptr<Pass>> Passes;
|
||||
using PassArrayType = fextl::vector<fextl::unique_ptr<Pass>>;
|
||||
PassArrayType::iterator InsertAt(PassArrayType::iterator pos, fextl::unique_ptr<Pass> Pass) {
|
||||
Pass->RegisterPassManager(this);
|
||||
return Passes.insert(pos, std::move(Pass));
|
||||
}
|
||||
PassArrayType Passes;
|
||||
fextl::unordered_map<fextl::string, Pass*> NameToPassMaping;
|
||||
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
@@ -104,6 +110,7 @@ private:
|
||||
#endif
|
||||
|
||||
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
|
||||
FEX_CONFIG_OPT(PassManagerDumpIR, PASSMANAGERDUMPIR);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,5 +29,9 @@ fextl::unique_ptr<FEXCore::IR::Pass> CreateRAValidation();
|
||||
fextl::unique_ptr<FEXCore::IR::Pass> CreatePhiValidation();
|
||||
fextl::unique_ptr<FEXCore::IR::Pass> CreateValueDominanceValidation();
|
||||
}
|
||||
|
||||
namespace Debug {
|
||||
fextl::unique_ptr<FEXCore::IR::Pass> CreateIRDumper();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,8 @@ bool IRCompaction::Run(IREmitter *IREmit) {
|
||||
|
||||
// Zero is always zero(invalid)
|
||||
OldToNewRemap[0].NodeID.Invalidate();
|
||||
auto LocalHeaderOp = LocalBuilder._IRHeader(OrderedNodeWrapper::WrapOffset(0).GetNode(ListBegin), HeaderOp->BlockCount);
|
||||
auto LocalHeaderOp = LocalBuilder._IRHeader(OrderedNodeWrapper::WrapOffset(0).GetNode(ListBegin), HeaderOp->OriginalRIP, HeaderOp->BlockCount);
|
||||
|
||||
OldToNewRemap[CurrentIR.GetID(HeaderNode).Value].NodeID = LocalIR.GetID(LocalHeaderOp.Node);
|
||||
|
||||
{
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
$info$
|
||||
tags: ir|debug
|
||||
desc: Prints IR
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/IR/PassManager.h"
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
#include "Interface/Core/OpcodeDispatcher.h"
|
||||
|
||||
#include <FEXCore/IR/IR.h>
|
||||
|
||||
namespace FEXCore::IR::Debug {
|
||||
class IRDumper final : public FEXCore::IR::Pass {
|
||||
public:
|
||||
IRDumper();
|
||||
bool Run(IREmitter *IREmit) override;
|
||||
|
||||
private:
|
||||
FEX_CONFIG_OPT(DumpIR, DUMPIR);
|
||||
bool DumpToFile{};
|
||||
bool DumpToLog{};
|
||||
};
|
||||
|
||||
IRDumper::IRDumper() {
|
||||
const auto DumpIRStr = DumpIR();
|
||||
if (DumpIRStr == "stderr" || DumpIRStr == "stdout" || DumpIRStr == "no") {
|
||||
// Intentionally do nothing
|
||||
}
|
||||
else if (DumpIRStr == "server") {
|
||||
DumpToLog = true;
|
||||
}
|
||||
else {
|
||||
DumpToFile = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool IRDumper::Run(IREmitter *IREmit) {
|
||||
auto RAPass = Manager->GetPass<IR::RegisterAllocationPass>("RA");
|
||||
IR::RegisterAllocationData* RA{};
|
||||
if (RAPass) {
|
||||
RA = RAPass->GetAllocationData();
|
||||
}
|
||||
|
||||
FEXCore::File::File FD{};
|
||||
if (DumpIR() == "stderr") {
|
||||
FD = FEXCore::File::File::GetStdERR();
|
||||
}
|
||||
else if (DumpIR() == "stdout") {
|
||||
FD = FEXCore::File::File::GetStdOUT();
|
||||
}
|
||||
|
||||
auto IR = IREmit->ViewIR();
|
||||
auto HeaderOp = IR.GetHeader();
|
||||
LOGMAN_THROW_AA_FMT(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader");
|
||||
|
||||
// DumpIRStr might be no if not dumping but ShouldDump is set in OpDisp
|
||||
if (DumpToFile) {
|
||||
const auto fileName = fextl::fmt::format("{}/{:x}{}", DumpIR(), HeaderOp->OriginalRIP, RA ? "-post.ir" : "-pre.ir");
|
||||
FD = FEXCore::File::File(fileName.c_str(),
|
||||
FEXCore::File::FileModes::WRITE |
|
||||
FEXCore::File::FileModes::CREATE |
|
||||
FEXCore::File::FileModes::TRUNCATE);
|
||||
}
|
||||
|
||||
if (FD.IsValid() || DumpToLog) {
|
||||
fextl::stringstream out;
|
||||
FEXCore::IR::Dump(&out, &IR, RA);
|
||||
if (FD.IsValid()) {
|
||||
fextl::fmt::print(FD, "IR-{} 0x{:x}:\n{}\n@@@@@\n", RA ? "post" : "pre", HeaderOp->OriginalRIP, out.str());
|
||||
}
|
||||
else {
|
||||
LogMan::Msg::IFmt("IR-{} 0x{:x}:\n{}\n@@@@@\n", RA ? "post" : "pre", HeaderOp->OriginalRIP, out.str());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fextl::unique_ptr<FEXCore::IR::Pass> CreateIRDumper() {
|
||||
return fextl::make_unique<IRDumper>();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user