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); } } } }
//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); } }