mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-12 22:00:41 +02:00
I recommend viewing the new source file as the diff is quite messy. --- The old RA commits every "how not to write an RA" sin in the book. Chaitin spill-one loop? Check. Potential spilling caused by alignment issues since there's no live range splitting? Check. Panic spilling? Check. Generating an interference graph with linear live ranges, so you get the code quality of linear scan with the cost of graph colouring? Check. ... It is wholly unsuitable to any application, and specifically unsuitable for FEX. --- The new RA exploits a key IR invariant unique to FEX: no values are live across block boundaries. This is validated. Because of this invariant, all RA is block local. This lets us use a dead simple 2 pass RA that generates ~optimal code in linear time. The first pass walks the IR backwards, analyzing the IR. This is a souped up analogue to liveness analysis. The second pass walks the IR forward, blasting out registers. If necessary, it will insert spill and/or shuffle code on the fly. Spilling uses the well-known furthest-first heuristic, which has excellent results for straight line code. That's it :-) Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
124 lines
3.8 KiB
C++
124 lines
3.8 KiB
C++
// SPDX-License-Identifier: MIT
|
|
/*
|
|
$info$
|
|
meta: ir|opts ~ IR to IR Optimization
|
|
tags: ir|opts
|
|
desc: Defines which passes are run, and runs them
|
|
$end_info$
|
|
*/
|
|
|
|
#include "Interface/Context/Context.h"
|
|
#include "Interface/IR/PassManager.h"
|
|
#include "Interface/IR/Passes.h"
|
|
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
|
|
|
#include <FEXCore/Config/Config.h>
|
|
#include <FEXCore/Utils/Profiler.h>
|
|
|
|
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::BEFOREOPT) {
|
|
// Insert at the start.
|
|
it = InsertAt(it, Debug::CreateIRDumper());
|
|
++it; // Skip what we inserted.
|
|
}
|
|
|
|
if ((PassManagerDumpIR() & FEXCore::Config::PassManagerDumpIR::BEFOREPASS) ||
|
|
(PassManagerDumpIR() & FEXCore::Config::PassManagerDumpIR::AFTERPASS)) {
|
|
|
|
bool SkipFirstBefore = PassManagerDumpIR() & FEXCore::Config::PassManagerDumpIR::BEFOREOPT;
|
|
for (; it != Passes.end();) {
|
|
if (PassManagerDumpIR() & FEXCore::Config::PassManagerDumpIR::BEFOREPASS) {
|
|
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::AFTERPASS) {
|
|
// Insert after
|
|
it = InsertAt(it, Debug::CreateIRDumper());
|
|
++it; // Skip what we inserted.
|
|
}
|
|
}
|
|
}
|
|
if (PassManagerDumpIR() & FEXCore::Config::PassManagerDumpIR::AFTEROPT) {
|
|
if (!(PassManagerDumpIR() & FEXCore::Config::PassManagerDumpIR::AFTERPASS)) {
|
|
// Insert final IRDumper.
|
|
InsertAt(Passes.end(), Debug::CreateIRDumper());
|
|
}
|
|
}
|
|
}
|
|
|
|
void PassManager::AddDefaultPasses(FEXCore::Context::ContextImpl* ctx, bool InlineConstants) {
|
|
FEX_CONFIG_OPT(DisablePasses, O0);
|
|
|
|
if (!DisablePasses()) {
|
|
InsertPass(CreateContextLoadStoreElimination(ctx->HostFeatures.SupportsAVX));
|
|
|
|
if (Is64BitMode()) {
|
|
// This needs to run after RCLSE
|
|
// This only matters for 64-bit code since these instructions don't exist in 32-bit
|
|
InsertPass(CreateLongDivideEliminationPass());
|
|
}
|
|
|
|
InsertPass(CreateDeadStoreElimination());
|
|
InsertPass(CreatePassDeadCodeElimination());
|
|
InsertPass(CreateConstProp(InlineConstants, ctx->HostFeatures.SupportsTSOImm9, Is64BitMode()));
|
|
|
|
InsertPass(CreateDeadFlagCalculationEliminination());
|
|
|
|
InsertPass(CreateInlineCallOptimization(&ctx->CPUID));
|
|
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
|
|
InsertPass(CreateIRCompaction(ctx->OpDispatcherAllocator), "Compaction");
|
|
}
|
|
|
|
void PassManager::AddDefaultValidationPasses() {
|
|
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
|
InsertValidationPass(Validation::CreateIRValidation(), "IRValidation");
|
|
InsertValidationPass(Validation::CreateRAValidation());
|
|
#endif
|
|
}
|
|
|
|
void PassManager::InsertRegisterAllocationPass() {
|
|
InsertPass(IR::CreateRegisterAllocationPass(), "RA");
|
|
}
|
|
|
|
bool PassManager::Run(IREmitter* IREmit) {
|
|
FEXCORE_PROFILE_SCOPED("PassManager::Run");
|
|
|
|
bool Changed = false;
|
|
for (const auto& Pass : Passes) {
|
|
Changed |= Pass->Run(IREmit);
|
|
}
|
|
|
|
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
|
for (const auto& Pass : ValidationPasses) {
|
|
Changed |= Pass->Run(IREmit);
|
|
}
|
|
#endif
|
|
|
|
return Changed;
|
|
}
|
|
} // namespace FEXCore::IR
|