mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-14 13:00:41 +02:00
Merge pull request #2868 from Sonicadvance1/irdumper_passmanager
IR: Adds Option to run the IRDumper with more configurations
This commit is contained in:
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