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:
Ryan Houdek committed 2023-08-09 05:58:20 -07:00
1 parent 53ac8abce9
commit 35e97ec9bc
13 files changed
+183 -34

No files matched your search

+1
View File
@@ -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
+18
View File
@@ -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",
-1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+48
View File
@@ -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
View File
@@ -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);
};
}
+4
View File
@@ -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>();
}
}