From 2cd844bbcb798a8aa779cef433bba284ca9fd71b Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Fri, 10 Sep 2021 07:22:57 +1200 Subject: [PATCH] RAValidation: Validate Spill slots This can prove that the ssa in the Spill slots are consistant based on control flow. It's one major blind spot is that it can't prove the spill slot actually contains the correct ssa, if it has the same wrong ssa on every cfg path. --- .../Interface/IR/Passes/RAValidation.cpp | 79 +++++++++++++++++-- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/External/FEXCore/Source/Interface/IR/Passes/RAValidation.cpp b/External/FEXCore/Source/Interface/IR/Passes/RAValidation.cpp index 3cc9711b4..a669382b0 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/RAValidation.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/RAValidation.cpp @@ -88,6 +88,20 @@ struct RegState { return InvalidReg; } + + void Spill(uint32_t SpillSlot, uint32_t ssa) { + Spills[SpillSlot] = ssa; + } + + uint32_t Unspill(uint32_t SpillSlot) { + if (Spills.contains(SpillSlot)) { + uint32_t Value = Spills[SpillSlot]; + Spills.erase(SpillSlot); + return Value; + } + return UninitializedValue; + } + void Intersect(RegState& other) { for (size_t i = 0; i < GPRs.size(); i++) { if (GPRs[i] != other.GPRs[i]) { @@ -100,6 +114,15 @@ struct RegState { FPRs[i] = ClobberedValue; } } + + for (auto it = Spills.begin(); it != Spills.end(); it++) { + auto& [SlotID, Value] = *it; + if (!other.Spills.contains(SlotID)) { + Spills.erase(it); + } else if (Value != other.Spills[SlotID]) { + Value = ClobberedValue; + } + } } void Filter(uint32_t MaxSSA) { @@ -114,12 +137,21 @@ struct RegState { fpr = ClobberedValue; } } + + for (auto it = Spills.begin(); it != Spills.end(); it++) { + auto& [SlotID, Value] = *it; + if (Value > MaxSSA) { + Spills.erase(it); + } + } } private: std::array GPRs = {}; std::array FPRs = {}; + std::unordered_map Spills; + public: uint32_t Version{}; }; @@ -230,15 +262,11 @@ bool RAValidation::Run(IREmitter *IREmit) { for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) { uint32_t ID = CurrentIR.GetID(CodeNode); - // And check that all args point at the correct SSA - uint8_t NumArgs = IR::GetArgs(IROp->Op); - for (uint32_t i = 0; i < NumArgs; ++i) { - OrderedNodeWrapper Arg = IROp->Args[i]; - + auto CheckArg = [&] (uint32_t i, OrderedNodeWrapper Arg) { const auto PhyReg = RAData->GetNodeRegister(Arg.ID()); if (PhyReg.IsInvalid()) - continue; + return; auto CurrentSSAAtReg = BlockRegState.Get(PhyReg); if (CurrentSSAAtReg == RegState::InvalidReg) { @@ -267,6 +295,45 @@ bool RAValidation::Run(IREmitter *IREmit) { Errors << fmt::format("%ssa{}: Arg[{}] expects reg{} to contain %ssa{}, but it actually contains %ssa{}\n", ID, i, PhyReg.Reg, Arg.ID(), CurrentSSAAtReg); } + }; + + switch (IROp->Op) + { + case OP_SPILLREGISTER: { + auto SpillRegister = IROp->C(); + CheckArg(0, SpillRegister->Value); + + BlockRegState.Spill(SpillRegister->Slot, SpillRegister->Value.ID()); + break; + } + + case OP_FILLREGISTER: { + auto FillRegister = IROp->C(); + uint32_t value = BlockRegState.Unspill(FillRegister->Slot); + + // TODO: This only proves that the Spill has a consistent SSA value + // In the future we need to prove it contains the correct SSA value + + if (value == RegState::UninitializedValue) { + HadError |= true; + Errors << fmt::format("%ssa{}: FillRegister; Spill Slot {} was undefined in at least one control flow path\n", + ID, FillRegister->Slot); + } else if (value == RegState::ClobberedValue) { + HadError |= true; + Errors << fmt::format("%ssa{}: FillRegister; Spill Slot {} contents vary depending on control flow\n", + ID, FillRegister->Slot); + } + break; + } + + default: { + // And check that all args point at the correct SSA + uint8_t NumArgs = IR::GetArgs(IROp->Op); + for (uint32_t i = 0; i < NumArgs; ++i) { + CheckArg(i, IROp->Args[i]); + } + break; + } } // Update BlockState map