mirror of
https://github.com/FEX-Emu/FEX.git
synced 2025-03-06 05:27:18 +00:00
Changes over IRValidation to the new format.
We still can't pass this validation pass
This commit is contained in:
parent
b7c7a34cf6
commit
b199ce70e9
@ -6,13 +6,10 @@
|
||||
namespace FEXCore::IR::Validation {
|
||||
|
||||
struct BlockInfo {
|
||||
IR::OrderedNodeWrapper *Begin;
|
||||
IR::OrderedNodeWrapper *End;
|
||||
|
||||
bool HasExit;
|
||||
|
||||
std::vector<IR::OrderedNodeWrapper*> Predecessors;
|
||||
std::vector<IR::OrderedNodeWrapper*> Successors;
|
||||
std::vector<IR::OrderedNode const*> Predecessors;
|
||||
std::vector<IR::OrderedNode const*> Successors;
|
||||
};
|
||||
|
||||
class IRValidation final : public FEXCore::IR::Pass {
|
||||
@ -25,139 +22,167 @@ private:
|
||||
|
||||
bool IRValidation::Run(OpDispatchBuilder *Disp) {
|
||||
bool HadError = false;
|
||||
bool HadWarning = false;
|
||||
OffsetToBlockMap.clear();
|
||||
auto CurrentIR = Disp->ViewIR();
|
||||
uintptr_t ListBegin = CurrentIR.GetListData();
|
||||
uintptr_t DataBegin = CurrentIR.GetData();
|
||||
|
||||
IR::NodeWrapperIterator Begin = CurrentIR.begin();
|
||||
IR::NodeWrapperIterator End = CurrentIR.end();
|
||||
|
||||
bool InBlock = false;
|
||||
BlockInfo *CurrentBlock {};
|
||||
std::ostringstream Errors;
|
||||
std::ostringstream Warnings;
|
||||
|
||||
while (Begin != End) {
|
||||
OrderedNodeWrapper *WrapperOp = Begin();
|
||||
OrderedNode *RealNode = WrapperOp->GetNode(ListBegin);
|
||||
FEXCore::IR::IROp_Header *IROp = RealNode->Op(DataBegin);
|
||||
auto Begin = CurrentIR.begin();
|
||||
auto Op = Begin();
|
||||
|
||||
uint8_t OpSize = IROp->Size;
|
||||
OrderedNode *RealNode = Op->GetNode(ListBegin);
|
||||
auto HeaderOp = RealNode->Op(DataBegin)->CW<FEXCore::IR::IROp_IRHeader>();
|
||||
LogMan::Throw::A(HeaderOp->Header.Op == OP_IRHEADER, "First op wasn't IRHeader");
|
||||
|
||||
if (IROp->HasDest) {
|
||||
HadError |= OpSize == 0;
|
||||
if (OpSize == 0) {
|
||||
Errors << "%ssa" << WrapperOp->ID() << ": Had destination but with no size" << std::endl;
|
||||
OrderedNode *BlockNode = HeaderOp->Blocks.GetNode(ListBegin);
|
||||
while (1) {
|
||||
auto BlockIROp = BlockNode->Op(DataBegin)->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
LogMan::Throw::A(BlockIROp->Header.Op == OP_CODEBLOCK, "IR type failed to be a code block");
|
||||
|
||||
CurrentBlock = &OffsetToBlockMap.try_emplace(BlockNode->Wrapped(ListBegin).ID()).first->second;
|
||||
|
||||
// We grab these nodes this way so we can iterate easily
|
||||
auto CodeBegin = CurrentIR.at(BlockIROp->Begin);
|
||||
auto CodeLast = CurrentIR.at(BlockIROp->Last);
|
||||
|
||||
while (1) {
|
||||
auto CodeOp = CodeBegin();
|
||||
OrderedNode *CodeNode = CodeOp->GetNode(ListBegin);
|
||||
auto IROp = CodeNode->Op(DataBegin);
|
||||
|
||||
uint8_t OpSize = IROp->Size;
|
||||
|
||||
if (IROp->HasDest) {
|
||||
HadError |= OpSize == 0;
|
||||
// Does the op have a destination of size 0?
|
||||
if (OpSize == 0) {
|
||||
Errors << "%ssa" << CodeOp->ID() << ": Had destination but with no size" << std::endl;
|
||||
}
|
||||
|
||||
// Does the node have zero uses? Should have been DCE'd
|
||||
if (RealNode->GetUses() == 0) {
|
||||
HadWarning |= true;
|
||||
Warnings << "%ssa" << CodeOp->ID() << ": Destination created but had no uses" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (RealNode->GetUses() == 0) {
|
||||
HadError |= true;
|
||||
Errors << "%ssa" << WrapperOp->ID() << ": Destination created but had no uses" << std::endl;
|
||||
uint8_t NumArgs = IR::GetArgs(IROp->Op);
|
||||
for (uint32_t i = 0; i < NumArgs; ++i) {
|
||||
OrderedNodeWrapper Arg = IROp->Args[i];
|
||||
// Does the SSA(with RA) argument have an invalid argument?
|
||||
if (Arg.ID() == 0) {
|
||||
HadError |= true;
|
||||
Errors << "%ssa" << CodeOp->ID() <<": Arg[" << i << "] has invalid target of %ssa0" << std::endl;
|
||||
}
|
||||
|
||||
// Was an argument defined after this node?
|
||||
if (Arg.ID() >= CodeOp->ID()) {
|
||||
HadError |= true;
|
||||
Errors << "%ssa" << CodeOp->ID() << ": Arg[" << i << "] has definition after use at %ssa" << Arg.ID() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
switch (IROp->Op) {
|
||||
case IR::OP_EXITFUNCTION:
|
||||
case IR::OP_ENDFUNCTION: {
|
||||
CurrentBlock->HasExit = true;
|
||||
break;
|
||||
}
|
||||
case IR::OP_CONDJUMP: {
|
||||
auto Op = IROp->C<IR::IROp_CondJump>();
|
||||
|
||||
OrderedNode const *TrueTargetNode = Op->Header.Args[1].GetNode(ListBegin);
|
||||
OrderedNode const *FalseTargetNode = Op->Header.Args[1].GetNode(ListBegin);
|
||||
|
||||
CurrentBlock->Successors.emplace_back(TrueTargetNode);
|
||||
CurrentBlock->Successors.emplace_back(FalseTargetNode);
|
||||
|
||||
FEXCore::IR::IROp_Header const *TrueTargetOp = TrueTargetNode->Op(DataBegin);
|
||||
FEXCore::IR::IROp_Header const *FalseTargetOp = FalseTargetNode->Op(DataBegin);
|
||||
|
||||
if (TrueTargetOp->Op != OP_CODEBLOCK) {
|
||||
HadError |= true;
|
||||
Errors << "CondJump %ssa" << CodeOp->ID() << ": True Target Jumps to Op that isn't the begining of a block" << std::endl;
|
||||
}
|
||||
else {
|
||||
auto Block = OffsetToBlockMap.try_emplace(Op->Header.Args[1].ID()).first;
|
||||
Block->second.Predecessors.emplace_back(BlockNode);
|
||||
}
|
||||
|
||||
if (FalseTargetOp->Op != OP_CODEBLOCK) {
|
||||
HadError |= true;
|
||||
Errors << "CondJump %ssa" << CodeOp->ID() << ": False Target Jumps to Op that isn't the begining of a block" << std::endl;
|
||||
}
|
||||
else {
|
||||
auto Block = OffsetToBlockMap.try_emplace(Op->Header.Args[2].ID()).first;
|
||||
Block->second.Predecessors.emplace_back(BlockNode);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case IR::OP_JUMP: {
|
||||
auto Op = IROp->C<IR::IROp_Jump>();
|
||||
OrderedNode const *TargetNode = Op->Header.Args[0].GetNode(ListBegin);
|
||||
CurrentBlock->Successors.emplace_back(TargetNode);
|
||||
|
||||
FEXCore::IR::IROp_Header const *TargetOp = TargetNode->Op(DataBegin);
|
||||
if (TargetOp->Op != OP_CODEBLOCK) {
|
||||
HadError |= true;
|
||||
Errors << "Jump %ssa" << CodeOp->ID() << ": Jump to Op that isn't the begining of a block" << std::endl;
|
||||
}
|
||||
else {
|
||||
auto Block = OffsetToBlockMap.try_emplace(Op->Header.Args[0].ID()).first;
|
||||
Block->second.Predecessors.emplace_back(BlockNode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// LogMan::Msg::A("Unknown IR Op: %d(%s)", IROp->Op, FEXCore::IR::GetName(IROp->Op).data());
|
||||
break;
|
||||
}
|
||||
|
||||
// CodeLast is inclusive. So we still need to dump the CodeLast op as well
|
||||
if (CodeBegin == CodeLast) {
|
||||
break;
|
||||
}
|
||||
++CodeBegin;
|
||||
}
|
||||
|
||||
uint8_t NumArgs = IR::GetArgs(IROp->Op);
|
||||
for (uint8_t i = 0; i < NumArgs; ++i) {
|
||||
OrderedNodeWrapper Arg = IROp->Args[i];
|
||||
if (Arg.ID() == 0) {
|
||||
HadError |= true;
|
||||
Errors << "Op" << WrapperOp->ID() <<": Arg[" << i << "] has invalid target of %ssa0" << std::endl;
|
||||
}
|
||||
// Blocks can only have zero (Exit), 1 (Unconditional branch) or 2 (Conditional) successors
|
||||
size_t NumSuccessors = CurrentBlock->Successors.size();
|
||||
if (NumSuccessors > 2) {
|
||||
HadError |= true;
|
||||
Errors << "%ssa" << BlockNode->Wrapped(ListBegin).ID() << " Has " << NumSuccessors << " successors which is too many" << std::endl;
|
||||
}
|
||||
|
||||
switch (IROp->Op) {
|
||||
case OP_BEGINBLOCK: {
|
||||
HadError |= InBlock;
|
||||
if (InBlock) {
|
||||
Errors << "BasicBlock " << WrapperOp->ID() << ": Begin in middle of block" << std::endl;
|
||||
}
|
||||
|
||||
auto Block = OffsetToBlockMap.try_emplace(WrapperOp->ID()).first;
|
||||
CurrentBlock = &Block->second;
|
||||
CurrentBlock->Begin = WrapperOp;
|
||||
InBlock = true;
|
||||
break;
|
||||
if (BlockIROp->Next.ID() == 0) {
|
||||
break;
|
||||
} else {
|
||||
BlockNode = BlockIROp->Next.GetNode(ListBegin);
|
||||
}
|
||||
|
||||
case OP_ENDBLOCK: {
|
||||
HadError |= !InBlock;
|
||||
if (!InBlock) {
|
||||
Errors << "BasicBlock " << WrapperOp->ID() << ": End loose without a begin" << std::endl;
|
||||
}
|
||||
|
||||
if (CurrentBlock) {
|
||||
// XXX: Enable once fallthrough is handled
|
||||
// HadError |= !CurrentBlock->HasExit && CurrentBlock->Successors.size() == 0;
|
||||
// if (!CurrentBlock->HasExit && CurrentBlock->Successors.size() == 0) {
|
||||
// Errors << "BasicBlock " << WrapperOp->ID() << ": Didn't have an exit and didn't have any successors. (Fallthrough?)" << std::endl;
|
||||
// }
|
||||
CurrentBlock->End = WrapperOp;
|
||||
CurrentBlock = nullptr;
|
||||
}
|
||||
InBlock = false;
|
||||
break;
|
||||
}
|
||||
case IR::OP_EXITFUNCTION:
|
||||
case IR::OP_ENDFUNCTION: {
|
||||
if (CurrentBlock) {
|
||||
CurrentBlock->HasExit = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IR::OP_CONDJUMP: {
|
||||
auto Op = IROp->C<IR::IROp_CondJump>();
|
||||
auto IterLocation = NodeWrapperIterator(ListBegin, Op->Header.Args[1]);
|
||||
if (CurrentBlock) {
|
||||
CurrentBlock->Successors.emplace_back(IterLocation());
|
||||
}
|
||||
|
||||
OrderedNode *TargetNode = IterLocation()->GetNode(ListBegin);
|
||||
FEXCore::IR::IROp_Header *TargetOp = TargetNode->Op(DataBegin);
|
||||
HadError |= TargetOp->Op != OP_BEGINBLOCK;
|
||||
if (TargetOp->Op != OP_BEGINBLOCK) {
|
||||
Errors << "CondJump " << WrapperOp->ID() << ": CondJump to Op that isn't the begining of a block" << std::endl;
|
||||
}
|
||||
else {
|
||||
auto Block = OffsetToBlockMap.try_emplace(IterLocation()->NodeOffset).first;
|
||||
Block->second.Predecessors.emplace_back(CurrentBlock->Begin);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case IR::OP_JUMP: {
|
||||
auto Op = IROp->C<IR::IROp_Jump>();
|
||||
auto IterLocation = NodeWrapperIterator(ListBegin, Op->Header.Args[0]);
|
||||
if (CurrentBlock) {
|
||||
CurrentBlock->Successors.emplace_back(IterLocation());
|
||||
}
|
||||
|
||||
OrderedNode *TargetNode = IterLocation()->GetNode(ListBegin);
|
||||
FEXCore::IR::IROp_Header *TargetOp = TargetNode->Op(DataBegin);
|
||||
HadError |= TargetOp->Op != OP_BEGINBLOCK;
|
||||
if (TargetOp->Op != OP_BEGINBLOCK) {
|
||||
Errors << "Jump " << WrapperOp->ID() << ": Jump to Op that isn't the begining of a block" << std::endl;
|
||||
}
|
||||
else {
|
||||
auto Block = OffsetToBlockMap.try_emplace(IterLocation()->NodeOffset).first;
|
||||
Block->second.Predecessors.emplace_back(CurrentBlock->Begin);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
//LogMan::Msg::A("Unknown IR Op: %d(%s)", IROp->Op, FEXCore::IR::GetName(IROp->Op).data());
|
||||
break;
|
||||
}
|
||||
|
||||
++Begin;
|
||||
}
|
||||
|
||||
if (HadError) {
|
||||
std::stringstream Out;
|
||||
std::stringstream Out;
|
||||
|
||||
HadWarning = false;
|
||||
if (HadError || HadWarning) {
|
||||
FEXCore::IR::Dump(&Out, &CurrentIR);
|
||||
|
||||
std::cerr << Errors.str() << std::endl << Out.str() << std::endl;
|
||||
if (HadError) {
|
||||
Out << "Errors:" << std::endl << Errors.str() << std::endl;
|
||||
}
|
||||
|
||||
if (HadWarning) {
|
||||
Out << "Warnings:" << std::endl << Warnings.str() << std::endl;
|
||||
}
|
||||
|
||||
std::cerr << Out.str() << std::endl;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user