| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459 |
- using Ryujinx.Graphics.Shader.IntermediateRepresentation;
- using System;
- using System.Collections.Generic;
- using static Ryujinx.Graphics.Shader.StructuredIr.AstHelper;
- namespace Ryujinx.Graphics.Shader.StructuredIr
- {
- static class GotoElimination
- {
- // This is a modified version of the algorithm presented on the paper
- // "Taming Control Flow: A Structured Approach to Eliminating Goto Statements".
- public static void Eliminate(GotoStatement[] gotos)
- {
- for (int index = gotos.Length - 1; index >= 0; index--)
- {
- GotoStatement stmt = gotos[index];
- AstBlock gBlock = ParentBlock(stmt.Goto);
- AstBlock lBlock = ParentBlock(stmt.Label);
- int gLevel = Level(gBlock);
- int lLevel = Level(lBlock);
- if (IndirectlyRelated(gBlock, lBlock, gLevel, lLevel))
- {
- AstBlock drBlock = gBlock;
- int drLevel = gLevel;
- do
- {
- drBlock = drBlock.Parent;
- drLevel--;
- }
- while (!DirectlyRelated(drBlock, lBlock, drLevel, lLevel));
- MoveOutward(stmt, gLevel, drLevel);
- gBlock = drBlock;
- gLevel = drLevel;
- if (Previous(stmt.Goto) is AstBlock elseBlock && elseBlock.Type == AstBlockType.Else)
- {
- // It's possible that the label was enclosed inside an else block,
- // in this case we need to update the block and level.
- // We also need to set the IsLoop for the case when the label is
- // now before the goto, due to the newly introduced else block.
- lBlock = ParentBlock(stmt.Label);
- lLevel = Level(lBlock);
- if (!IndirectlyRelated(elseBlock, lBlock, gLevel + 1, lLevel))
- {
- stmt.IsLoop = true;
- }
- }
- }
- if (DirectlyRelated(gBlock, lBlock, gLevel, lLevel))
- {
- if (gLevel > lLevel)
- {
- MoveOutward(stmt, gLevel, lLevel);
- }
- else
- {
- if (stmt.IsLoop)
- {
- Lift(stmt);
- }
- MoveInward(stmt);
- }
- }
- gBlock = ParentBlock(stmt.Goto);
- if (stmt.IsLoop)
- {
- EncloseDoWhile(stmt, gBlock, stmt.Label);
- }
- else
- {
- Enclose(gBlock, AstBlockType.If, stmt.Condition, Next(stmt.Goto), stmt.Label);
- }
- gBlock.Remove(stmt.Goto);
- }
- }
- private static bool IndirectlyRelated(AstBlock lBlock, AstBlock rBlock, int lLevel, int rlevel)
- {
- return !(lBlock == rBlock || DirectlyRelated(lBlock, rBlock, lLevel, rlevel));
- }
- private static bool DirectlyRelated(AstBlock lBlock, AstBlock rBlock, int lLevel, int rLevel)
- {
- // If the levels are equal, they can be either siblings or indirectly related.
- if (lLevel == rLevel)
- {
- return false;
- }
- IAstNode block;
- IAstNode other;
- int blockLvl, otherLvl;
- if (lLevel > rLevel)
- {
- block = lBlock;
- blockLvl = lLevel;
- other = rBlock;
- otherLvl = rLevel;
- }
- else /* if (rLevel > lLevel) */
- {
- block = rBlock;
- blockLvl = rLevel;
- other = lBlock;
- otherLvl = lLevel;
- }
- while (blockLvl >= otherLvl)
- {
- if (block == other)
- {
- return true;
- }
- block = block.Parent;
- blockLvl--;
- }
- return false;
- }
- private static void Lift(GotoStatement stmt)
- {
- AstBlock block = ParentBlock(stmt.Goto);
- AstBlock[] path = BackwardsPath(block, ParentBlock(stmt.Label));
- AstBlock loopFirstStmt = path[path.Length - 1];
- if (loopFirstStmt.Type == AstBlockType.Else)
- {
- loopFirstStmt = Previous(loopFirstStmt) as AstBlock;
- if (loopFirstStmt == null || loopFirstStmt.Type != AstBlockType.If)
- {
- throw new InvalidOperationException("Found an else without a matching if.");
- }
- }
- AstBlock newBlock = EncloseDoWhile(stmt, block, loopFirstStmt);
- block.Remove(stmt.Goto);
- newBlock.AddFirst(stmt.Goto);
- stmt.IsLoop = false;
- }
- private static void MoveOutward(GotoStatement stmt, int gLevel, int lLevel)
- {
- AstBlock origin = ParentBlock(stmt.Goto);
- AstBlock block = origin;
- // Check if a loop is enclosing the goto, and the block that is
- // directly related to the label is above the loop block.
- // In that case, we need to introduce a break to get out of the loop.
- AstBlock loopBlock = origin;
- int loopLevel = gLevel;
- while (loopLevel > lLevel)
- {
- AstBlock child = loopBlock;
- loopBlock = loopBlock.Parent;
- loopLevel--;
- if (child.Type == AstBlockType.DoWhile)
- {
- EncloseSingleInst(stmt, Instruction.LoopBreak);
- block.Remove(stmt.Goto);
- loopBlock.AddAfter(child, stmt.Goto);
- block = loopBlock;
- gLevel = loopLevel;
- }
- }
- // Insert ifs to skip the parts that shouldn't be executed due to the goto.
- bool tryInsertElse = stmt.IsUnconditional && origin.Type == AstBlockType.If;
- while (gLevel > lLevel)
- {
- Enclose(block, AstBlockType.If, stmt.Condition, Next(stmt.Goto));
- block.Remove(stmt.Goto);
- AstBlock child = block;
- // We can't move the goto in the middle of a if and a else block, in
- // this case we need to move it after the else.
- // IsLoop may need to be updated if the label is inside the else, as
- // introducing a loop is the only way to ensure the else will be executed.
- if (Next(child) is AstBlock elseBlock && elseBlock.Type == AstBlockType.Else)
- {
- child = elseBlock;
- }
- block = block.Parent;
- block.AddAfter(child, stmt.Goto);
- gLevel--;
- if (tryInsertElse && child == origin)
- {
- AstBlock lBlock = ParentBlock(stmt.Label);
- IAstNode last = block == lBlock && !stmt.IsLoop ? stmt.Label : null;
- AstBlock newBlock = Enclose(block, AstBlockType.Else, null, Next(stmt.Goto), last);
- if (newBlock != null)
- {
- block.Remove(stmt.Goto);
- block.AddAfter(newBlock, stmt.Goto);
- }
- }
- }
- }
- private static void MoveInward(GotoStatement stmt)
- {
- AstBlock block = ParentBlock(stmt.Goto);
- AstBlock[] path = BackwardsPath(block, ParentBlock(stmt.Label));
- for (int index = path.Length - 1; index >= 0; index--)
- {
- AstBlock child = path[index];
- AstBlock last = child;
- if (child.Type == AstBlockType.If)
- {
- // Modify the if condition to allow it to be entered by the goto.
- if (!ContainsCondComb(child.Condition, Instruction.LogicalOr, stmt.Condition))
- {
- child.OrCondition(stmt.Condition);
- }
- }
- else if (child.Type == AstBlockType.Else)
- {
- // Modify the matching if condition to force the else to be entered by the goto.
- if (!(Previous(child) is AstBlock ifBlock) || ifBlock.Type != AstBlockType.If)
- {
- throw new InvalidOperationException("Found an else without a matching if.");
- }
- IAstNode cond = InverseCond(stmt.Condition);
- if (!ContainsCondComb(ifBlock.Condition, Instruction.LogicalAnd, cond))
- {
- ifBlock.AndCondition(cond);
- }
- last = ifBlock;
- }
- Enclose(block, AstBlockType.If, stmt.Condition, Next(stmt.Goto), last);
- block.Remove(stmt.Goto);
- child.AddFirst(stmt.Goto);
- block = child;
- }
- }
- private static bool ContainsCondComb(IAstNode node, Instruction inst, IAstNode newCond)
- {
- while (node is AstOperation operation && operation.SourcesCount == 2)
- {
- if (operation.Inst == inst && IsSameCond(operation.GetSource(1), newCond))
- {
- return true;
- }
- node = operation.GetSource(0);
- }
- return false;
- }
- private static AstBlock EncloseDoWhile(GotoStatement stmt, AstBlock block, IAstNode first)
- {
- if (block.Type == AstBlockType.DoWhile && first == block.First)
- {
- // We only need to insert the continue if we're not at the end of the loop,
- // or if our condition is different from the loop condition.
- if (Next(stmt.Goto) != null || block.Condition != stmt.Condition)
- {
- EncloseSingleInst(stmt, Instruction.LoopContinue);
- }
- // Modify the do-while condition to allow it to continue.
- if (!ContainsCondComb(block.Condition, Instruction.LogicalOr, stmt.Condition))
- {
- block.OrCondition(stmt.Condition);
- }
- return block;
- }
- return Enclose(block, AstBlockType.DoWhile, stmt.Condition, first, stmt.Goto);
- }
- private static void EncloseSingleInst(GotoStatement stmt, Instruction inst)
- {
- AstBlock block = ParentBlock(stmt.Goto);
- AstBlock newBlock = new AstBlock(AstBlockType.If, stmt.Condition);
- block.AddAfter(stmt.Goto, newBlock);
- newBlock.AddFirst(new AstOperation(inst));
- }
- private static AstBlock Enclose(
- AstBlock block,
- AstBlockType type,
- IAstNode cond,
- IAstNode first,
- IAstNode last = null)
- {
- if (first == last)
- {
- return null;
- }
- if (type == AstBlockType.If)
- {
- cond = InverseCond(cond);
- }
- // Do a quick check, if we are enclosing a single block,
- // and the block type/condition matches the one we're going
- // to create, then we don't need a new block, we can just
- // return the old one.
- bool hasSingleNode = Next(first) == last;
- if (hasSingleNode && BlockMatches(first, type, cond))
- {
- return first as AstBlock;
- }
- AstBlock newBlock = new AstBlock(type, cond);
- block.AddBefore(first, newBlock);
- while (first != last)
- {
- IAstNode next = Next(first);
- block.Remove(first);
- newBlock.Add(first);
- first = next;
- }
- return newBlock;
- }
- private static bool BlockMatches(IAstNode node, AstBlockType type, IAstNode cond)
- {
- if (!(node is AstBlock block))
- {
- return false;
- }
- return block.Type == type && IsSameCond(block.Condition, cond);
- }
- private static bool IsSameCond(IAstNode lCond, IAstNode rCond)
- {
- if (lCond is AstOperation lCondOp && lCondOp.Inst == Instruction.LogicalNot)
- {
- if (!(rCond is AstOperation rCondOp) || rCondOp.Inst != lCondOp.Inst)
- {
- return false;
- }
- lCond = lCondOp.GetSource(0);
- rCond = rCondOp.GetSource(0);
- }
- return lCond == rCond;
- }
- private static AstBlock ParentBlock(IAstNode node)
- {
- if (node is AstBlock block)
- {
- return block.Parent;
- }
- while (!(node is AstBlock))
- {
- node = node.Parent;
- }
- return node as AstBlock;
- }
- private static AstBlock[] BackwardsPath(AstBlock top, AstBlock bottom)
- {
- AstBlock block = bottom;
- List<AstBlock> path = new List<AstBlock>();
- while (block != top)
- {
- path.Add(block);
- block = block.Parent;
- }
- return path.ToArray();
- }
- private static int Level(IAstNode node)
- {
- int level = 0;
- while (node != null)
- {
- level++;
- node = node.Parent;
- }
- return level;
- }
- }
- }
|