IR: drop RegisterAllocationData

This sideband is now unused, registers are encoded directly in the IR. So we can
garbage collect all this code for quite some savings.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
This commit is contained in:
Alyssa Rosenzweig committed 2025-05-19 13:42:59 -04:00
1 parent 49f8332c5b
commit 65ee1fafa8
18 files changed
+23 -154

No files matched your search

+1 -1
View File
@@ -575,7 +575,7 @@ def print_ir_arg_printer():
if arg.IsSSA:
# SSA value
output_file.write("\tPrintArg(out, IR, Op->Header.Args[{}], RAData);\n".format(SSAArgNum))
output_file.write("\tPrintArg(out, IR, Op->Header.Args[{}]);\n".format(SSAArgNum))
SSAArgNum = SSAArgNum + 1
else:
# User defined op that is stored
@@ -50,7 +50,6 @@ namespace HLE {
} // namespace FEXCore
namespace FEXCore::IR {
class RegisterAllocationData;
struct IRListCopy;
class IRListView;
namespace Validation {
@@ -271,7 +270,6 @@ public:
struct GenerateIRResult {
std::optional<IR::IRListView> IRView;
IR::RegisterAllocationData* RAData;
uint64_t TotalInstructions;
uint64_t TotalInstructionsLength;
uint64_t StartAddr;
+1 -2
View File
@@ -18,7 +18,6 @@ namespace FEXCore {
namespace IR {
class IRListView;
class RegisterAllocationData;
} // namespace IR
namespace Core {
@@ -119,7 +118,7 @@ namespace CPU {
*/
[[nodiscard]]
virtual CompiledCode CompileCode(uint64_t Entry, uint64_t Size, bool SingleInst, const FEXCore::IR::IRListView* IR,
FEXCore::Core::DebugData* DebugData, const FEXCore::IR::RegisterAllocationData* RAData, bool CheckTF) = 0;
FEXCore::Core::DebugData* DebugData, bool CheckTF) = 0;
/**
* @brief Relocates a block of code from the JIT code object cache
+7 -11
View File
@@ -509,11 +509,11 @@ void ContextImpl::ClearCodeCache(FEXCore::Core::InternalThreadState* Thread) {
Thread->CPUBackend->ClearCache();
}
static void IRDumper(FEXCore::Core::InternalThreadState* Thread, IR::IREmitter* IREmitter, uint64_t GuestRIP, IR::RegisterAllocationData* RA) {
static void IRDumper(FEXCore::Core::InternalThreadState* Thread, IR::IREmitter* IREmitter, uint64_t GuestRIP) {
FEXCore::File::File FD = FEXCore::File::File::GetStdERR();
fextl::stringstream out;
auto NewIR = IREmitter->ViewIR();
FEXCore::IR::Dump(&out, &NewIR, RA);
FEXCore::IR::Dump(&out, &NewIR);
fextl::fmt::print(FD, "IR-ShouldDump-{} 0x{:x}:\n{}\n@@@@@\n", NewIR.PostRA() ? "post" : "pre", GuestRIP, out.str());
};
@@ -686,7 +686,7 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue
if (HadDispatchError && TotalInstructions == 0) {
// Couldn't handle any instruction in op dispatcher
Thread->OpDispatcher->ResetWorkingList();
return {{}, nullptr, 0, 0, 0, 0};
return {{}, 0, 0, 0, 0};
}
if (NeedsBlockEnd) {
@@ -712,22 +712,19 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue
auto ShouldDump = Thread->OpDispatcher->ShouldDumpIR();
// Debug
if (ShouldDump) {
IRDumper(Thread, IREmitter, GuestRIP, nullptr);
IRDumper(Thread, IREmitter, GuestRIP);
}
// Run the passmanager over the IR from the dispatcher
Thread->PassManager->Run(IREmitter);
auto RAData = Thread->PassManager->HasPass("RA") ? Thread->PassManager->GetPass<IR::RegisterAllocationPass>("RA")->GetAllocationData() : nullptr;
// Debug
if (ShouldDump) {
IRDumper(Thread, IREmitter, GuestRIP, RAData);
IRDumper(Thread, IREmitter, GuestRIP);
}
return {
.IRView = IREmitter->ViewIR(),
.RAData = RAData,
.TotalInstructions = TotalInstructions,
.TotalInstructionsLength = TotalInstructionsLength,
.StartAddr = Thread->FrontendDecoder->DecodedMinAddress,
@@ -760,8 +757,7 @@ ContextImpl::CompileCodeResult ContextImpl::CompileCode(FEXCore::Core::InternalT
}
// Generate IR + Meta Info
auto [IRView, RAData, TotalInstructions, TotalInstructionsLength, StartAddr, Length] =
GenerateIR(Thread, GuestRIP, Config.GDBSymbols(), MaxInst);
auto [IRView, TotalInstructions, TotalInstructionsLength, StartAddr, Length] = GenerateIR(Thread, GuestRIP, Config.GDBSymbols(), MaxInst);
if (!IRView) {
return {nullptr, nullptr, 0, 0};
}
@@ -772,7 +768,7 @@ ContextImpl::CompileCodeResult ContextImpl::CompileCode(FEXCore::Core::InternalT
// Attempt to get the CPU backend to compile this code
auto CompiledCode = Thread->CPUBackend->CompileCode(GuestRIP, Length, TotalInstructions == 1, &*IRView, DebugData.get(), RAData, TFSet);
auto CompiledCode = Thread->CPUBackend->CompileCode(GuestRIP, Length, TotalInstructions == 1, &*IRView, DebugData.get(), TFSet);
// Release the IR
Thread->OpDispatcher->DelayedDisownBuffer();
+1 -3
View File
@@ -681,15 +681,13 @@ void Arm64JITCore::EmitInterruptChecks(bool CheckTF) {
}
CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size, bool SingleInst, const FEXCore::IR::IRListView* IR,
FEXCore::Core::DebugData* DebugData, const FEXCore::IR::RegisterAllocationData* RAData,
bool CheckTF) {
FEXCore::Core::DebugData* DebugData, bool CheckTF) {
FEXCORE_PROFILE_SCOPED("Arm64::CompileCode");
JumpTargets.clear();
uint32_t SSACount = IR->GetSSACount();
this->Entry = Entry;
this->RAData = RAData;
this->DebugData = DebugData;
this->IR = IR;
+2 -4
View File
@@ -38,9 +38,8 @@ public:
~Arm64JITCore() override;
[[nodiscard]]
CPUBackend::CompiledCode
CompileCode(uint64_t Entry, uint64_t Size, bool SingleInst, const FEXCore::IR::IRListView* IR, FEXCore::Core::DebugData* DebugData,
const FEXCore::IR::RegisterAllocationData* RAData, bool CheckTF) override;
CPUBackend::CompiledCode CompileCode(uint64_t Entry, uint64_t Size, bool SingleInst, const FEXCore::IR::IRListView* IR,
FEXCore::Core::DebugData* DebugData, bool CheckTF) override;
void ClearCache() override;
@@ -292,7 +291,6 @@ private:
// This is purely a debugging aid for developers to see if they are in JIT code space when inspecting raw memory
void EmitDetectionString();
IR::RegisterAllocationPass* RAPass {};
const IR::RegisterAllocationData* RAData {};
FEXCore::Core::DebugData* DebugData {};
void ResetStack();
+4 -16
View File
@@ -47,19 +47,12 @@ AOTIRInlineEntry* AOTIRInlineIndex::Find(uint64_t GuestStart) {
return nullptr;
}
IR::RegisterAllocationData* AOTIRInlineEntry::GetRAData() {
return (IR::RegisterAllocationData*)InlineData;
}
IR::IRListView* AOTIRInlineEntry::GetIRData() {
auto RAData = GetRAData();
auto Offset = RAData->Size(RAData->MapCount);
return (IR::IRListView*)&InlineData[Offset];
return (IR::IRListView*)InlineData;
}
void AOTIRCaptureCacheEntry::AppendAOTIRCaptureCache(uint64_t GuestRIP, uint64_t Start, uint64_t Length, uint64_t Hash,
const FEXCore::IR::IRListView& IRList, const FEXCore::IR::RegisterAllocationData* RAData) {
const FEXCore::IR::IRListView& IRList) {
auto Inserted = Index.emplace(GuestRIP, Stream->Offset());
if (Inserted.second) {
@@ -69,8 +62,6 @@ void AOTIRCaptureCacheEntry::AppendAOTIRCaptureCache(uint64_t GuestRIP, uint64_t
};
Stream->Write((const char*)&entry, sizeof(entry));
RAData->Serialize(*Stream);
// IRData (inline)
IRList.Serialize(*Stream);
}
@@ -265,9 +256,6 @@ class IRInlineStorage : public IRStorageBase {
public:
IRInlineStorage(AOTIRInlineEntry& entry)
: entry(entry) {}
const RegisterAllocationData* RAData() override {
return entry.GetRAData();
}
IRListView GetIRView() override {
return entry.GetIRData();
}
@@ -332,7 +320,7 @@ bool AOTIRCaptureCache::PostCompileCode(FEXCore::Core::InternalThreadState* Thre
}
// Add to AOT cache if aot generation is enabled
if (GeneratedIR && IR->RAData() && (CTX->Config.AOTIRCapture() || CTX->Config.AOTIRGenerate())) {
if (GeneratedIR && (CTX->Config.AOTIRCapture() || CTX->Config.AOTIRGenerate())) {
auto hash = XXH3_64bits((void*)StartAddr, Length);
@@ -356,7 +344,7 @@ bool AOTIRCaptureCache::PostCompileCode(FEXCore::Core::InternalThreadState* Thre
uint64_t tag = FEXCore::IR::AOTIR_COOKIE;
AotFile->Stream->Write(&tag, sizeof(tag));
}
AotFile->AppendAOTIRCaptureCache(LocalRIP, LocalStartAddr, Length, hash, IR->GetIRView(), IR->RAData());
AotFile->AppendAOTIRCaptureCache(LocalRIP, LocalStartAddr, Length, hash, IR->GetIRView());
});
if (CTX->Config.AOTIRGenerate()) {
+2 -5
View File
@@ -50,7 +50,6 @@ class ContextImpl;
}
namespace FEXCore::IR {
class RegisterAllocationData;
class IRListView;
constexpr auto COOKIE_VERSION = [](const char CookieText[4], uint32_t Version) {
@@ -75,10 +74,9 @@ struct AOTIRInlineEntry {
uint64_t GuestHash;
uint64_t GuestLength;
/* RAData followed by IRData */
/* IRData */
uint8_t InlineData[0];
IR::RegisterAllocationData* GetRAData();
IR::IRListView* GetIRData();
};
@@ -100,8 +98,7 @@ struct AOTIRCaptureCacheEntry {
fextl::unique_ptr<FEXCore::Context::AOTIRWriter> Stream;
fextl::map<uint64_t, uint64_t> Index;
void AppendAOTIRCaptureCache(uint64_t GuestRIP, uint64_t Start, uint64_t Length, uint64_t Hash, const FEXCore::IR::IRListView& IRList,
const FEXCore::IR::RegisterAllocationData* RAData);
void AppendAOTIRCaptureCache(uint64_t GuestRIP, uint64_t Start, uint64_t Length, uint64_t Hash, const FEXCore::IR::IRListView& IRList);
};
struct AOTIRCacheEntry {
+1 -2
View File
@@ -11,7 +11,6 @@ namespace FEXCore::IR {
class OrderedNode;
class RegisterAllocationPass;
class RegisterAllocationData;
/**
* @brief The IROp_Header is an dynamically sized array
@@ -766,7 +765,7 @@ inline NodeID NodeWrapperBase<Type>::ID() const {
bool IsFragmentExit(FEXCore::IR::IROps Op);
bool IsBlockExit(FEXCore::IR::IROps Op);
void Dump(fextl::stringstream* out, const IRListView* IR, const IR::RegisterAllocationData* RAData);
void Dump(fextl::stringstream* out, const IRListView* IR);
} // namespace FEXCore::IR
template<>
+2 -2
View File
@@ -82,7 +82,7 @@ static void PrintArg(fextl::stringstream* out, [[maybe_unused]] const IRListView
}
}
static void PrintArg(fextl::stringstream* out, const IRListView* IR, OrderedNodeWrapper Arg, const IR::RegisterAllocationData* RAData) {
static void PrintArg(fextl::stringstream* out, const IRListView* IR, OrderedNodeWrapper Arg) {
if (Arg.IsImmediate()) {
auto PhyReg = PhysicalRegister(Arg);
@@ -272,7 +272,7 @@ static void PrintArg(fextl::stringstream* out, [[maybe_unused]] const IRListView
}
}
void Dump(fextl::stringstream* out, const IRListView* IR, const IR::RegisterAllocationData* RAData) {
void Dump(fextl::stringstream* out, const IRListView* IR) {
auto HeaderOp = IR->GetHeader();
int8_t CurrentIndent = 0;
@@ -419,9 +419,6 @@ class IRStorageBase {
public:
virtual ~IRStorageBase() = default;
// Optional RA data. Returns nullptr if none present
virtual const RegisterAllocationData* RAData() = 0;
virtual IRListView GetIRView() = 0;
};
-1
View File
@@ -15,7 +15,6 @@ class IntrusivePooledAllocator;
namespace FEXCore::IR {
class Pass;
class RegisterAllocationPass;
class RegisterAllocationData;
fextl::unique_ptr<FEXCore::IR::Pass> CreateConstProp(bool SupportsTSOImm9, const FEXCore::CPUIDEmu* CPUID);
fextl::unique_ptr<FEXCore::IR::Pass> CreateDeadFlagCalculationEliminination();
@@ -38,12 +38,6 @@ IRDumper::IRDumper() {
}
void 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();
@@ -64,7 +58,7 @@ void IRDumper::Run(IREmitter* IREmit) {
if (FD.IsValid() || DumpToLog) {
fextl::stringstream out;
FEXCore::IR::Dump(&out, &IR, RA);
FEXCore::IR::Dump(&out, &IR);
if (FD.IsValid()) {
fextl::fmt::print(FD, "IR-{} 0x{:x}:\n{}\n@@@@@\n", IR.PostRA() ? "post" : "pre", HeaderOp->OriginalRIP, out.str());
} else {
@@ -58,11 +58,6 @@ void IRValidation::Run(IREmitter* IREmit) {
LOGMAN_THROW_A_FMT(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader");
#endif
IR::RegisterAllocationData* RAData {};
if (Manager->HasPass("RA")) {
RAData = Manager->GetPass<IR::RegisterAllocationPass>("RA")->GetAllocationData();
}
for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) {
auto BlockIROp = BlockHeader->CW<FEXCore::IR::IROp_CodeBlock>();
LOGMAN_THROW_A_FMT(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block");
@@ -257,7 +252,7 @@ void IRValidation::Run(IREmitter* IREmit) {
HadWarning = false;
if (HadError || HadWarning) {
fextl::stringstream Out;
FEXCore::IR::Dump(&Out, &CurrentIR, RAData);
FEXCore::IR::Dump(&Out, &CurrentIR);
if (HadError) {
Out << "Errors:" << std::endl << Errors.str() << std::endl;
@@ -58,11 +58,7 @@ public:
void Run(IREmitter* IREmit) override;
void AddRegisters(IR::RegisterClassType Class, uint32_t RegisterCount) override;
RegisterAllocationData* GetAllocationData() override;
RegisterAllocationData::UniquePtr PullAllocationData() override;
private:
IR::RegisterAllocationData::UniquePtr AllocData;
RegisterClass Classes[INVALID_CLASS];
IREmitter* IREmit;
@@ -445,14 +441,6 @@ void ConstrainedRAPass::AddRegisters(IR::RegisterClassType Class, uint32_t Regis
Classes[Class].Count = RegisterCount;
}
RegisterAllocationData* ConstrainedRAPass::GetAllocationData() {
return AllocData.get();
}
RegisterAllocationData::UniquePtr ConstrainedRAPass::PullAllocationData() {
return std::move(AllocData);
}
void ConstrainedRAPass::Run(IREmitter* IREmit_) {
FEXCORE_PROFILE_SCOPED("PassManager::RA");
@@ -673,13 +661,6 @@ void ConstrainedRAPass::Run(IREmitter* IREmit_) {
}
}
/* Now that we're done growing things, we can finalize our results.
*
* TODO: Rework RegisterAllocationData to remove this memcpy, it's pointless.
*/
AllocData = RegisterAllocationData::Create(SSAToReg.size());
memcpy(AllocData->Map, SSAToReg.data(), sizeof(PhysicalRegister) * SSAToReg.size());
PreferredReg.clear();
SSAToNewSSA.clear();
NewSSAToSSA.clear();
@@ -12,8 +12,6 @@ $end_info$
#include <stdint.h>
namespace FEXCore::IR {
class RegisterAllocationData;
struct RegisterAllocationDataDeleter;
struct RegisterClassType;
class RegisterAllocationPass : public FEXCore::IR::Pass {
@@ -22,16 +20,6 @@ public:
// Number of GPRs usable for pairs at start of GPR set. Must be even.
uint32_t PairRegs;
/**
* @brief Returns the register and class map array
*/
virtual RegisterAllocationData* GetAllocationData() = 0;
/**
* @brief Returns and transfers ownership of the register and class map array
*/
virtual std::unique_ptr<RegisterAllocationData, RegisterAllocationDataDeleter> PullAllocationData() = 0;
};
} // namespace FEXCore::IR
@@ -41,61 +41,4 @@ union PhysicalRegister {
static_assert(sizeof(PhysicalRegister) == 1);
struct RegisterAllocationDataDeleter;
// This class is serialized, can't have any holes in the structure
// otherwise ASAN complains about reading uninitialized memory
class FEX_PACKED RegisterAllocationData {
public:
uint32_t SpillSlotCount {};
uint32_t MapCount {};
PhysicalRegister Map[0];
PhysicalRegister GetNodeRegister(NodeID Node) const {
return Map[Node.Value];
}
uint32_t SpillSlots() const {
return SpillSlotCount;
}
static size_t Size(uint32_t NodeCount) {
return sizeof(RegisterAllocationData) + NodeCount * sizeof(Map[0]);
}
using UniquePtr = std::unique_ptr<FEXCore::IR::RegisterAllocationData, RegisterAllocationDataDeleter>;
static UniquePtr Create(uint32_t NodeCount);
UniquePtr CreateCopy() const;
void Serialize(FEXCore::Context::AOTIRWriter& stream) const {
stream.Write((const char*)&SpillSlotCount, sizeof(SpillSlotCount));
stream.Write((const char*)&MapCount, sizeof(MapCount));
// RAData (inline)
stream.Write((const char*)&Map[0], sizeof(Map[0]) * MapCount);
}
};
struct RegisterAllocationDataDeleter {
void operator()(RegisterAllocationData* r) const {
FEXCore::Allocator::free(r);
}
};
inline auto RegisterAllocationData::Create(uint32_t NodeCount) -> UniquePtr {
auto Ret = (RegisterAllocationData*)FEXCore::Allocator::malloc(Size(NodeCount));
memset(&Ret->Map[0], PhysicalRegister::Invalid().Raw, NodeCount);
Ret->SpillSlotCount = 0;
Ret->MapCount = NodeCount;
return UniquePtr {Ret};
}
inline auto RegisterAllocationData::CreateCopy() const -> UniquePtr {
auto copy = (RegisterAllocationData*)FEXCore::Allocator::malloc(Size(MapCount));
memcpy((void*)&copy->Map[0], (void*)&Map[0], MapCount * sizeof(Map[0]));
copy->SpillSlotCount = SpillSlotCount;
copy->MapCount = MapCount;
return UniquePtr {copy};
}
} // namespace FEXCore::IR
-1
View File
@@ -17,7 +17,6 @@ namespace FEXCore::IR {
class OrderedNode;
class RegisterAllocationPass;
class RegisterAllocationData;
enum class SyscallFlags : uint8_t {
DEFAULT = 0,