public bool JoinBasicBlocks(List<ILNode> body, ILBasicBlock head, int pos) { ILLabel nextLabel; ILBasicBlock nextBB; if (!head.Body.ElementAtOrDefault(head.Body.Count - 2).IsConditionalControlFlow() && head.Body.Last().Match(ILCode.Br, out nextLabel) && labelGlobalRefCount[nextLabel] == 1 && labelToBasicBlock.TryGetValue(nextLabel, out nextBB) && body.Contains(nextBB) && nextBB.Body.First() == nextLabel && !nextBB.Body.OfType<ILTryCatchBlock>().Any() ) { var tail = head.Body.RemoveTail(ILCode.Br); tail[0].AddSelfAndChildrenRecursiveILRanges(nextBB.ILRanges); nextBB.Body[0].AddSelfAndChildrenRecursiveILRanges(nextBB.ILRanges); nextBB.Body.RemoveAt(0); // Remove label if (head.Body.Count > 0) head.Body[head.Body.Count - 1].EndILRanges.AddRange(nextBB.ILRanges); else head.ILRanges.AddRange(nextBB.ILRanges); head.EndILRanges.AddRange(nextBB.EndILRanges); head.Body.AddRange(nextBB.Body); body.RemoveOrThrow(nextBB); return true; } return false; }
public bool InlineAllInBasicBlock(ILBasicBlock bb) { bool modified = false; List<ILNode> body = bb.Body; for(int i = 0; i < body.Count;) { ILVariable locVar; ILExpression expr; if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(bb.Body, i, aggressive: false)) { modified = true; i = Math.Max(0, i - 1); // Go back one step } else { i++; } } return modified; }
public bool JoinBasicBlocks(IList <ILNode> body, ILBasicBlock head, int pos) { ILLabel nextLabel; ILBasicBlock nextBB; if (!head.Body.ElementAtOrDefault(head.Body.Count - 2).IsConditionalControlFlow() && head.Body.Last().Match(GMCode.B, out nextLabel) && labelGlobalRefCount[nextLabel] == 1 && labelToBasicBlock.TryGetValue(nextLabel, out nextBB) && body.Contains(nextBB) && nextBB.Body.First() == nextLabel ) { head.Body.RemoveTail(GMCode.B); nextBB.Body.RemoveAt(0); // Remove label foreach (var a in nextBB.Body) { head.Body.Add(a); // head.Body.AddRange(nextBB.Body); } body.RemoveOrThrow(nextBB); return(true); } return(false); }
// special case. Since we are modifying the block alot in one go, lets try to do the entire block at once public static bool RunOptimizationAndRestart(this ILBasicBlock bb, params Func <IList <ILNode>, ILExpression, int, bool>[] optimizations) { bool modified = false; IList <ILNode> body = bb.Body; for (int i = bb.Body.Count - 1; i >= 0; i--) { ILExpression expr = bb.Body.ElementAtOrDefault(i) as ILExpression; if (expr != null) // && optimization(bb.Body, expr, i)) { bool test = false; while (CheckBlockBody(body, expr, i, optimizations)) { test = true; } modified |= test; if (test) { i = bb.Body.Count; // backup } } } return(modified); }
protected virtual ILBasicBlock VisitBasicBlock(ILBasicBlock basicBlock) { foreach (var child in basicBlock.GetChildren()) Visit(child); return basicBlock; }
// This is before the expression is processed, so ILValue's and constants havn't been assigned public bool SimplifyTernaryOperator(IList <ILNode> body, ILBasicBlock head, int pos) { Debug.Assert(body.Contains(head)); // Debug.Assert((head.Body[0] as ILLabel).Name != "Block_54"); // Debug.Assert((head.Body[0] as ILLabel).Name != "L1257"); ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; ILExpression trueExpr; ILLabel trueFall; ILExpression falseExpr; ILLabel falseFall; ILLabel finalFalseFall; ILLabel finalTrueFall; if (head.MatchLastAndBr(GMCode.Bt, out trueLabel, out condExpr, out falseLabel) && labelGlobalRefCount[trueLabel] == 1 && labelGlobalRefCount[falseLabel] == 1 && labelToBasicBlock[trueLabel].MatchSingleAndBr(GMCode.Push, out trueExpr, out trueFall) && labelToBasicBlock[falseLabel].MatchSingleAndBr(GMCode.Push, out falseExpr, out falseFall) && trueFall == falseFall && body.Contains(labelToBasicBlock[trueFall]) // finalFall.Code == GMCode.Pop ) // (finalFall == null || finalFall.Code == GMCode.Pop) { ILBasicBlock trueBlock = labelToBasicBlock[trueLabel]; ILBasicBlock falseBlock = labelToBasicBlock[falseLabel]; ILBasicBlock fallBlock = labelToBasicBlock[trueFall]; ILExpression newExpr = ResolveTernaryExpression(condExpr, trueExpr, falseExpr); head.Body.RemoveTail(GMCode.Bt, GMCode.B); body.RemoveOrThrow(trueBlock); body.RemoveOrThrow(falseBlock); IList <ILExpression> finalFall; // figure out if its a wierd short or not if (fallBlock.MatchSingleAndBr(GMCode.Bt, out finalTrueFall, out finalFall, out finalFalseFall) && finalFall.Count == 0) { head.Body.Add(new ILExpression(GMCode.Bt, finalTrueFall, newExpr)); if (labelGlobalRefCount[trueFall] == 2) { body.RemoveOrThrow(fallBlock); } } else if (fallBlock.Body.Count == 2) // wierd break, { finalFalseFall = fallBlock.GotoLabel(); head.Body.Add(new ILExpression(GMCode.Push, null, newExpr)); // we want to push it for next pass if (labelGlobalRefCount[trueFall] == 2) { body.RemoveOrThrow(fallBlock); } } else if (fallBlock.MatchAt(1, GMCode.Pop)) // generated? wierd instance? { finalFalseFall = fallBlock.EntryLabel(); error.Info("Wierd Generated Pop here", newExpr); head.Body.Add(new ILExpression(GMCode.Push, null, newExpr)); // It should be combined in JoinBasicBlocks function // so don't remove failblock } else if (fallBlock.MatchAt(1, GMCode.Assign, out finalFall)) // This is an assignment case and unsure what its for { finalFall.Add(newExpr); finalFalseFall = fallBlock.EntryLabel(); } if (finalFalseFall == null && fallBlock.MatchLastAt(1, GMCode.Ret)) { head.Body.Add(new ILExpression(GMCode.Ret, null)); } else { Debug.Assert(finalFalseFall != null); head.Body.Add(new ILExpression(GMCode.B, finalFalseFall)); } return(true); } return(false); }
public static ILNode First(this ILBasicBlock bb) { return(bb.Body.First()); }
public bool DetectSwitchAndConvertToBranches(IList <ILNode> body, ILBasicBlock head, int pos) { ILExpression condition; ILLabel trueLabel; ILLabel fallThough; // REMEMBER: The searching goes backwards, so we find the LAST case here, so here is the problem // Evey time we run this, we have to search for the first block of the case statement backwards and if // the push var is not resolved, we have to drop out... evey single time till the push IS resolved // so be sure to run this at the bottom of the decision chain. Its fine if the push is a simple var // but I can bet cash this runs 2-3 times if the push is some kind of expression like 5 + (3 % switch_var)) // we could change the way the optimizing loop works by going from the start and building a que of things // to remove, delete or change hummm... Take longer but it would make building this functions MUCH simpler if (MatchSwitchCase(head, out trueLabel, out fallThough, out condition)) // && head.MatchLastAt(6, GMCode.Push,out switch_expr)) { List <ILBasicBlock> caseBlocks = GetAllCaseBlocks(body, head, pos, out condition, out fallThough); foreach (var bb in caseBlocks) // replace the dup statements { Debug.Assert(bb.MatchLastAt(5, GMCode.Dup)); bb.Body[bb.Body.Count - 5] = new ILExpression(GMCode.Push, null, condition); ILExpression expr = bb.Body[bb.Body.Count - 3] as ILExpression; // expr.Code = GMCode.Case; // conver the equals to a case } // search the blocks for ending popz's HashSet <ILBasicBlock> blocks_done = new HashSet <ILBasicBlock>(); Stack <ILBasicBlock> agenda = new Stack <ILBasicBlock>(caseBlocks); while (agenda.Count > 0) { ILBasicBlock bb = agenda.Pop(); if (blocks_done.Contains(bb)) { continue; // already did it } ILExpression popz = bb.Body.OfType <ILExpression>().Where(e => e.Code == GMCode.Popz).SingleOrDefault(); if (popz != null) { bb.Body.Remove(popz); // remove it blocks_done.Add(bb); } else { ILLabel exit = bb.OperandLabelLastAt(0); if (exit != null && !blocks_done.Contains(labelToBasicBlock[exit])) { agenda.Push(labelToBasicBlock[exit]); } exit = bb.OperandLabelLastAt(1); // check if we have a bt or something if (exit != null && !blocks_done.Contains(labelToBasicBlock[exit])) { agenda.Push(labelToBasicBlock[exit]); } } } return(true); } return(false); }
public bool MatchRepeatStructure(IList <ILNode> body, ILBasicBlock head, int pos) { ILExpression rcount; ILExpression pushZero; int dupMode = 0; ILLabel fallthough; ILLabel repeatBlock; if (head.MatchLastAt(6, GMCode.Push, out rcount) && // header for a repeat, sets it up head.MatchLastAt(5, GMCode.Dup, out dupMode) && head.MatchLastAt(4, GMCode.Push, out pushZero) && pushZero.Code == GMCode.Constant && (pushZero.Operand as ILValue).IntValue == 0 && head.MatchLastAt(3, GMCode.Sle) && head.MatchLastAndBr(GMCode.Bt, out fallthough, out repeatBlock)) { // We have to seeperate the head from other bits of the block // humm, mabye have to put this in the general build routine like we did with push V:( head.Body.RemoveTail(GMCode.Push, GMCode.Dup, GMCode.Push, GMCode.Sle, GMCode.Bt, GMCode.B); ILBasicBlock header_block; ILLabel header_label; if (head.Body.Count == 1) {// The head only has the label, so its safe to use this as the header header_block = head; header_label = head.EntryLabel(); } else { header_label = ILLabel.Generate("R"); // We have to seperate the head. header_block = new ILBasicBlock(); head.Body.Add(new ILExpression(GMCode.B, header_label)); header_block.Body.Add(header_label); body.Insert(pos + 1, header_block); // insert before the block so it looks in order } header_block.Body.Add(new ILExpression(GMCode.Repeat, repeatBlock, rcount)); header_block.Body.Add(new ILExpression(GMCode.B, fallthough)); // NOW we got to find the block that matches ILExpression subOneConstant; ILLabel footerContinue, footerfallThough; /* * * * while (!(start.MatchLastAt(5, GMCode.Push, out subOneConstant) && * subOneConstant.Code == GMCode.Constant && (subOneConstant.Operand as ILValue).IntValue == 1 && * start.MatchLastAt(4, GMCode.Sub) && * start.MatchLastAt(3, GMCode.Dup, out dupMode) && dupMode == 0 && * start.MatchLastAndBr(GMCode.Bt, out footerContinue, out footerfallThough))) * { * ILLabel next = start.GotoLabel(); * start = labelToBasicBlock[next]; * } */// ok, on more complicated stuf like an internal loop, this f***s up, so we are going to do a hack // The popz fallthough comes RIGHT after the decrment for the repeate loop, so we are going to move up one from that // then check it ILBasicBlock popzBlock = labelToBasicBlock[fallthough]; // Debug.Assert((popzBlock.Body[1] as ILExpression).Code == GMCode.Popz); popzBlock.Body.RemoveAt(1); // remove the popz ILBasicBlock footer = body[body.IndexOf(popzBlock) - 1] as ILBasicBlock; if (footer.MatchLastAt(5, GMCode.Push, out subOneConstant) && subOneConstant.Code == GMCode.Constant && (subOneConstant.Operand as ILValue).IntValue == 1 && footer.MatchLastAt(4, GMCode.Sub) && footer.MatchLastAt(3, GMCode.Dup, out dupMode) && dupMode == 0 && footer.MatchLastAndBr(GMCode.Bt, out footerContinue, out footerfallThough)) { Debug.Assert(footerfallThough == fallthough && repeatBlock == footerContinue); // sanity check footer.Body.RemoveTail(GMCode.Push, GMCode.Sub, GMCode.Dup, GMCode.Bt, GMCode.B); footer.Body.Add(new ILExpression(GMCode.B, header_block.EntryLabel())); } else { throw new Exception("F**k me"); } // Found! Some sanity checks thogh /* MAJOR BUG UNFINSHED WORK ALERT! * Ok, so this isn't used in undertale, but at some point, somone might want to do a break or continue * Inside of a repeat statment. I have NO clue why though, use a while? * Anyway, if thats the case then you MUST change the target label of evetyhing going to start, to fallthough, otherwise * the goto cleaner will start screaming at you and do alot of weird stuff * fyi, like the with statments, I am converting this thing into a while loop so I don't have to have * custom loop graph code for these things * So, for now? convert start to loop back to head, head jumps to fallthough, and we remove the popz from the fall though */ // ILLabel.Generate("Block_", nextLabelIndex++); return(true); } return(false); }
/// <summary> /// Group input into a set of blocks that can be later arbitraliby schufled. /// The method adds necessary branches to make control flow between blocks /// explicit and thus order independent. /// </summary> /// public static void SplitToBasicBlocks(ILBlock block, bool reducebranches = false) { int nextLabelIndex = 0; List <ILNode> basicBlocks = new List <ILNode>(); ILLabel entryLabel = block.Body.FirstOrDefault() as ILLabel ?? ILLabel.Generate("Block_", nextLabelIndex++); ILBasicBlock basicBlock = new ILBasicBlock(); basicBlocks.Add(basicBlock); basicBlock.Body.Add(entryLabel); block.EntryGoto = new ILExpression(GMCode.B, entryLabel); if (block.Body.Count > 0) { if (block.Body[0] != entryLabel) { basicBlock.Body.Add(block.Body[0]); } for (int i = 1; i < block.Body.Count; i++) { ILNode lastNode = block.Body[i - 1]; ILNode currNode = block.Body[i]; // Start a new basic block if necessary if (currNode is ILLabel || lastNode.IsConditionalControlFlow() || lastNode.IsUnconditionalControlFlow()) { // Try to reuse the label ILLabel label = currNode as ILLabel ?? ILLabel.Generate("Block_", nextLabelIndex++); // Terminate the last block if (!lastNode.IsUnconditionalControlFlow()) { // Explicit branch from one block to other basicBlock.Body.Add(new ILExpression(GMCode.B, label)); } // Start the new block basicBlock = new ILBasicBlock(); basicBlocks.Add(basicBlock); basicBlock.Body.Add(label); // Add the node to the basic block if (currNode != label) { basicBlock.Body.Add(currNode); } } else { basicBlock.Body.Add(currNode); } } } block.Body = basicBlocks; if (reducebranches) { if (basicBlocks.Count > 0) { for (int i = 0; i < block.Body.Count; i++) { ILBasicBlock bb = block.Body[i] as ILBasicBlock; if (bb == null) { continue; } ILLabel trueLabel; ILLabel falseLabel; if (bb.MatchLastAndBr(GMCode.Bf, out falseLabel, out trueLabel)) { ILExpression bf = bb.Body[bb.Body.Count - 2] as ILExpression; ILExpression b = bb.Body[bb.Body.Count - 1] as ILExpression; bf.Code = GMCode.Bt; b.Operand = falseLabel; bf.Operand = trueLabel; } } } } return; }
List <ILNode> FindConditions(HashSet <ControlFlowNode> scope, ControlFlowNode entryNode) { List <ILNode> result = new List <ILNode>(); // Do not modify entry data scope = new HashSet <ControlFlowNode>(scope); Stack <ControlFlowNode> agenda = new Stack <ControlFlowNode>(); agenda.Push(entryNode); while (agenda.Count > 0) { ControlFlowNode node = agenda.Pop(); // Find a block that represents a simple condition if (scope.Contains(node)) { ILBasicBlock block = (ILBasicBlock)node.UserData; { IList <ILExpression> conditions; ILLabel fallLabel; // Switch FakeSwitch fswitch; if (block.MatchLastAndBr(GMCode.Switch, out fswitch, out conditions, out fallLabel)) { ILSwitch ilSwitch = new ILSwitch() { Condition = fswitch.SwitchExpression }; block.Body[block.Body.Count - 2] = ilSwitch; // replace it, nothing else needs to be done! result.Add(block); // except add it to the result, DOLT scope.RemoveOrThrow(node); // Remove the item so that it is not picked up as content // Pull in code of cases ControlFlowNode fallTarget = null; labelToCfNode.TryGetValue(fallLabel, out fallTarget); HashSet <ControlFlowNode> frontiers = new HashSet <ControlFlowNode>(); if (fallTarget != null) { frontiers.UnionWith(fallTarget.DominanceFrontier.Except(new[] { fallTarget })); } foreach (ILLabel condLabel in fswitch.CaseExpressions.Select(x => x.Value)) { ControlFlowNode condTarget = null; labelToCfNode.TryGetValue(condLabel, out condTarget); if (condTarget != null) { frontiers.UnionWith(condTarget.DominanceFrontier.Except(new[] { condTarget })); } } for (int i = 0; i < fswitch.CaseExpressions.Count; i++) { ILLabel condLabel = fswitch.CaseExpressions[i].Value; // Find or create new case block ILSwitch.ILCase caseBlock = ilSwitch.Cases.FirstOrDefault(b => b.EntryGoto.Operand == condLabel); if (caseBlock == null) { caseBlock = new ILSwitch.ILCase() { Values = new List <ILExpression>(), EntryGoto = new ILExpression(GMCode.B, condLabel) }; ilSwitch.Cases.Add(caseBlock); ControlFlowNode condTarget = null; labelToCfNode.TryGetValue(condLabel, out condTarget); if (condTarget != null && !frontiers.Contains(condTarget)) { HashSet <ControlFlowNode> content = FindDominatedNodes(scope, condTarget); scope.ExceptWith(content); foreach (var con in FindConditions(content, condTarget)) { caseBlock.Body.Add(con); } // caseBlock.Body.AddRange(FindConditions(content, condTarget)); // Add explicit break which should not be used by default, but the goto removal might decide to use it caseBlock.Body.Add(new ILBasicBlock() { Body = { ILLabel.Generate("SwitchBreak", (int)nextLabelIndex++), new ILExpression(GMCode.LoopOrSwitchBreak, null) } }); } } caseBlock.Values.Add(fswitch.CaseExpressions[i].Key); } // Heuristis to determine if we want to use fallthough as default case if (fallTarget != null && !frontiers.Contains(fallTarget)) { HashSet <ControlFlowNode> content = FindDominatedNodes(scope, fallTarget); if (content.Any()) { var caseBlock = new ILSwitch.ILCase() { EntryGoto = new ILExpression(GMCode.B, fallLabel) }; ilSwitch.Cases.Add(caseBlock); block.Body.RemoveTail(GMCode.B); scope.ExceptWith(content); foreach (var con in FindConditions(content, fallTarget)) { caseBlock.Body.Add(con); } // Add explicit break which should not be used by default, but the goto removal might decide to use it caseBlock.Body.Add(new ILBasicBlock() { Body = { ILLabel.Generate("SwitchBreak", (int)nextLabelIndex++), new ILExpression(GMCode.LoopOrSwitchBreak, null) } }); } } } // Debug.Assert((block.Body.First() as ILLabel).Name != "L1938"); // Two-way branch ILLabel trueLabel; ILLabel falseLabel; IList <ILExpression> condExprs; if (block.MatchLastAndBr(GMCode.Bt, out trueLabel, out condExprs, out falseLabel) && // be sure to invert this condition condExprs.Count > 0) // its resolved { ILExpression condExpr = condExprs[0]; IList <ILNode> body = block.Body; // this is a simple condition, skip anything short curiket for now // Match a condition patern // Convert the brtrue to ILCondition ILCondition ilCond = new ILCondition() { Condition = condExpr, //code == GMCode.Bf ? condExpr : condExpr.NegateCondition(), TrueBlock = new ILBlock() { EntryGoto = new ILExpression(GMCode.B, trueLabel) }, FalseBlock = new ILBlock() { EntryGoto = new ILExpression(GMCode.B, falseLabel) } }; block.Body.RemoveTail(GMCode.Bt, GMCode.B); block.Body.Add(ilCond); result.Add(block); // Remove the item immediately so that it is not picked up as content scope.RemoveOrThrow(node); ControlFlowNode trueTarget = null; labelToCfNode.TryGetValue(trueLabel, out trueTarget); ControlFlowNode falseTarget = null; labelToCfNode.TryGetValue(falseLabel, out falseTarget); // Pull in the conditional code if (trueTarget != null && HasSingleEdgeEnteringBlock(trueTarget)) { HashSet <ControlFlowNode> content = FindDominatedNodes(scope, trueTarget); scope.ExceptWith(content); foreach (var con in FindConditions(content, trueTarget)) { ilCond.TrueBlock.Body.Add(con); } } if (falseTarget != null && HasSingleEdgeEnteringBlock(falseTarget)) { HashSet <ControlFlowNode> content = FindDominatedNodes(scope, falseTarget); scope.ExceptWith(content); foreach (var con in FindConditions(content, falseTarget)) { ilCond.FalseBlock.Body.Add(con); } } } } // Add the node now so that we have good ordering if (scope.Contains(node)) { result.Add((ILNode)node.UserData); scope.Remove(node); } } // depth-first traversal of dominator tree for (int i = node.DominatorTreeChildren.Count - 1; i >= 0; i--) { agenda.Push(node.DominatorTreeChildren[i]); } } // Add whatever is left foreach (var node in scope) { result.Add((ILNode)node.UserData); } return(result); }
List <ILNode> FindLoops(HashSet <ControlFlowNode> scope, ControlFlowNode entryPoint, bool excludeEntryPoint) { List <ILNode> result = new List <ILNode>(); // Do not modify entry data scope = new HashSet <ControlFlowNode>(scope); Queue <ControlFlowNode> agenda = new Queue <ControlFlowNode>(); agenda.Enqueue(entryPoint); while (agenda.Count > 0) { ControlFlowNode node = agenda.Dequeue(); // If the node is a loop header if (scope.Contains(node) && node.DominanceFrontier.Contains(node) && (node != entryPoint || !excludeEntryPoint)) { HashSet <ControlFlowNode> loopContents = FindLoopContent(scope, node); // If the first expression is a loop condition ILBasicBlock basicBlock = (ILBasicBlock)node.UserData; ILExpression condExpr = null; ILLabel trueLabel; ILLabel falseLabel; // It has to be just brtrue - any preceding code would introduce goto error.Assert(!basicBlock.MatchLastAndBr(GMCode.Bt, out trueLabel, out condExpr, out falseLabel), "Unrolled loop"); if (basicBlock.MatchLastAndBr(GMCode.Bt, out trueLabel, out condExpr, out falseLabel) || basicBlock.MatchLastAndBr(GMCode.Pushenv, out falseLabel, out condExpr, out trueLabel) || // built it the same way from the dissasembler, this needs inverted basicBlock.MatchLastAndBr(GMCode.Repeat, out trueLabel, out condExpr, out falseLabel)) // repeate loop is built like a while { GMCode loopType = (basicBlock.Body.ElementAt(basicBlock.Body.Count - 2) as ILExpression).Code; ControlFlowNode trueTarget; labelToCfNode.TryGetValue(trueLabel, out trueTarget); ControlFlowNode falseTarget; labelToCfNode.TryGetValue(falseLabel, out falseTarget); // If one point inside the loop and the other outside if ((!loopContents.Contains(trueTarget) && loopContents.Contains(falseTarget)) || (loopContents.Contains(trueTarget) && !loopContents.Contains(falseTarget))) { loopContents.RemoveOrThrow(node); scope.RemoveOrThrow(node); bool mustNegate = false; if (loopContents.Contains(falseTarget) || falseTarget == node) { // Negate the condition mustNegate = true; // condExpr = new ILExpression(GMCode.Not, null, condExpr); ILLabel tmp = trueLabel; trueLabel = falseLabel; falseLabel = tmp; } // HACK ControlFlowNode postLoopTarget; labelToCfNode.TryGetValue(falseLabel, out postLoopTarget); if (postLoopTarget != null) { // Pull more nodes into the loop HashSet <ControlFlowNode> postLoopContents = FindDominatedNodes(scope, postLoopTarget); var pullIn = scope.Except(postLoopContents).Where(n => node.Dominates(n)); loopContents.UnionWith(pullIn); } //Debug.Assert(false); Debug.Assert(condExpr != null); switch (loopType) { case GMCode.Pushenv: basicBlock.Body.RemoveTail(GMCode.Pushenv, GMCode.B); basicBlock.Body.Add(new ILWithStatement() { Condition = condExpr, // we never negate Body = new ILBlock() { EntryGoto = new ILExpression(GMCode.B, trueLabel), Body = FindLoops(loopContents, node, false) } }); break; case GMCode.Bt: basicBlock.Body.RemoveTail(GMCode.Bt, GMCode.B); basicBlock.Body.Add(new ILWhileLoop() { Condition = mustNegate ? condExpr.NegateCondition() : condExpr, Body = new ILBlock() { EntryGoto = new ILExpression(GMCode.B, trueLabel), Body = FindLoops(loopContents, node, false) } }); break; case GMCode.Repeat: basicBlock.Body.RemoveTail(GMCode.Repeat, GMCode.B); basicBlock.Body.Add(new ILRepeat() { Condition = condExpr, // we never negate Body = new ILBlock() { EntryGoto = new ILExpression(GMCode.B, trueLabel), Body = FindLoops(loopContents, node, false) } }); break; } basicBlock.Body.Add(new ILExpression(GMCode.B, falseLabel)); result.Add(basicBlock); scope.ExceptWith(loopContents); } } // Fallback method: while(true) if (scope.Contains(node)) { Debug.Assert(false); result.Add(new ILBasicBlock() { Body = new List <ILNode>() { ILLabel.Generate("Loop"), new ILWhileLoop() { Condition = new ILExpression(GMCode.Constant, new ILValue(true)), Body = new ILBlock() { EntryGoto = new ILExpression(GMCode.B, (ILLabel)basicBlock.Body.First()), Body = FindLoops(loopContents, node, true) } }, }, }); scope.ExceptWith(loopContents); } } // Using the dominator tree should ensure we find the the widest loop first foreach (var child in node.DominatorTreeChildren) { agenda.Enqueue(child); } } // Add whatever is left foreach (var node in scope) { result.Add((ILNode)node.UserData); } scope.Clear(); return(result); }
// scope is modified if successful bool TrySimplifyShortCircuit(List<ILNode> scope, ILBasicBlock head) { Debug.Assert(scope.Contains(head)); ILExpression branchExpr = null; ILLabel trueLabel = null; ILLabel falseLabel = null; if(IsConditionalBranch(head, ref branchExpr, ref trueLabel, ref falseLabel)) { for (int pass = 0; pass < 2; pass++) { // On the second pass, swap labels and negate expression of the first branch // It is slightly ugly, but much better then copy-pasting this whole block ILLabel nextLabel = (pass == 0) ? trueLabel : falseLabel; ILLabel otherLablel = (pass == 0) ? falseLabel : trueLabel; bool negate = (pass == 1); ILBasicBlock nextBasicBlock = labelToBasicBlock[nextLabel]; ILExpression nextBranchExpr = null; ILLabel nextTrueLablel = null; ILLabel nextFalseLabel = null; if (scope.Contains(nextBasicBlock) && nextBasicBlock != head && labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 && IsConditionalBranch(nextBasicBlock, ref nextBranchExpr, ref nextTrueLablel, ref nextFalseLabel) && (otherLablel == nextFalseLabel || otherLablel == nextTrueLablel)) { // We are using the branches as expressions now, so do not keep their labels alive branchExpr.Operand = null; nextBranchExpr.Operand = null; // Create short cicuit branch if (otherLablel == nextFalseLabel) { head.Body[0] = new ILExpression(ILCode.BrLogicAnd, nextTrueLablel, negate ? new ILExpression(ILCode.LogicNot, null, branchExpr) : branchExpr, nextBranchExpr); } else { head.Body[0] = new ILExpression(ILCode.BrLogicOr, nextTrueLablel, negate ? branchExpr : new ILExpression(ILCode.LogicNot, null, branchExpr), nextBranchExpr); } head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel); // Remove the inlined branch from scope labelGlobalRefCount[nextBasicBlock.EntryLabel] = 0; if (!scope.Remove(nextBasicBlock)) throw new Exception("Element not found"); return true; } } } return false; }
bool IsConditionalBranch(ILBasicBlock bb, ref ILExpression branchExpr, ref ILLabel trueLabel, ref ILLabel falseLabel) { if (bb.Body.Count == 1) { branchExpr = bb.Body[0] as ILExpression; if (branchExpr != null && branchExpr.Operand is ILLabel && branchExpr.Arguments.Count > 0) { trueLabel = (ILLabel)branchExpr.Operand; falseLabel = (ILLabel)((ILExpression)bb.FallthoughGoto).Operand; return true; } } return false; }
public bool SimplifyShortCircuit(List<ILNode> body, ILBasicBlock head, int pos) { Debug.Assert(body.Contains(head)); ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; if(head.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { for (int pass = 0; pass < 2; pass++) { // On the second pass, swap labels and negate expression of the first branch // It is slightly ugly, but much better then copy-pasting this whole block ILLabel nextLabel = (pass == 0) ? trueLabel : falseLabel; ILLabel otherLablel = (pass == 0) ? falseLabel : trueLabel; bool negate = (pass == 1); ILBasicBlock nextBasicBlock = labelToBasicBlock[nextLabel]; ILExpression nextCondExpr; ILLabel nextTrueLablel; ILLabel nextFalseLabel; if (body.Contains(nextBasicBlock) && nextBasicBlock != head && labelGlobalRefCount[(ILLabel)nextBasicBlock.Body.First()] == 1 && nextBasicBlock.MatchSingleAndBr(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) && (otherLablel == nextFalseLabel || otherLablel == nextTrueLablel)) { // Create short cicuit branch ILExpression logicExpr; if (otherLablel == nextFalseLabel) { logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr); } else { logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr); } var tail = head.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); nextCondExpr.ILRanges.AddRange(tail[0].ILRanges); // brtrue nextCondExpr.ILRanges.AddRange(nextBasicBlock.ILRanges); nextBasicBlock.Body[0].AddSelfAndChildrenRecursiveILRanges(nextCondExpr.ILRanges); // label nextCondExpr.ILRanges.AddRange(nextBasicBlock.Body[1].ILRanges); // brtrue head.Body.Add(new ILExpression(ILCode.Brtrue, nextTrueLablel, logicExpr)); ILExpression brFalseLbl; head.Body.Add(brFalseLbl = new ILExpression(ILCode.Br, nextFalseLabel)); nextBasicBlock.Body[2].AddSelfAndChildrenRecursiveILRanges(brFalseLbl.ILRanges); // br brFalseLbl.ILRanges.AddRange(nextBasicBlock.EndILRanges); tail[1].AddSelfAndChildrenRecursiveILRanges(brFalseLbl.ILRanges); // br // Remove the inlined branch from scope body.RemoveOrThrow(nextBasicBlock); return true; } } } return false; }
public bool SimplifyTernaryOperator(List<ILNode> body, ILBasicBlock head, int pos) { Debug.Assert(body.Contains(head)); ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; ILVariable trueLocVar = null; ILExpression trueExpr; ILLabel trueFall; ILVariable falseLocVar = null; ILExpression falseExpr; ILLabel falseFall; object unused; if (head.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) && labelGlobalRefCount[trueLabel] == 1 && labelGlobalRefCount[falseLabel] == 1 && ((labelToBasicBlock[trueLabel].MatchSingleAndBr(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) && labelToBasicBlock[falseLabel].MatchSingleAndBr(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall) && trueLocVar == falseLocVar && trueFall == falseFall) || (labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr) && labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr))) && body.Contains(labelToBasicBlock[trueLabel]) && body.Contains(labelToBasicBlock[falseLabel]) ) { bool isStloc = trueLocVar != null; ILCode opCode = isStloc ? ILCode.Stloc : ILCode.Ret; TypeSig retType = isStloc ? trueLocVar.Type : this.context.CurrentMethod.ReturnType; bool retTypeIsBoolean = retType.GetElementType() == ElementType.Boolean; int leftBoolVal; int rightBoolVal; ILExpression newExpr; // a ? true:false is equivalent to a // a ? false:true is equivalent to !a // a ? true : b is equivalent to a || b // a ? b : true is equivalent to !a || b // a ? b : false is equivalent to a && b // a ? false : b is equivalent to !a && b if (retTypeIsBoolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) && ((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0)) ) { // It can be expressed as trivilal expression if (leftBoolVal != 0) { newExpr = condExpr; } else { newExpr = new ILExpression(ILCode.LogicNot, null, condExpr) { InferredType = corLib.Boolean }; } } else if ((retTypeIsBoolean || falseExpr.InferredType.GetElementType() == ElementType.Boolean) && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) && (leftBoolVal == 0 || leftBoolVal == 1)) { // It can be expressed as logical expression if (leftBoolVal != 0) { newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, condExpr, falseExpr); } else { newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr); } } else if ((retTypeIsBoolean || trueExpr.InferredType.GetElementType() == ElementType.Boolean) && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) && (rightBoolVal == 0 || rightBoolVal == 1)) { // It can be expressed as logical expression if (rightBoolVal != 0) { newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr); } else { newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr); } } else { // Ternary operator tends to create long complicated return statements if (opCode == ILCode.Ret) return false; // Only simplify generated variables if (opCode == ILCode.Stloc && !trueLocVar.GeneratedByDecompiler) return false; // Create ternary expression newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr); } var tail = head.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); var listNodes = new List<ILNode>(); var newExprNodes = newExpr.GetSelfAndChildrenRecursive<ILNode>(listNodes).ToArray(); foreach (var node in labelToBasicBlock[trueLabel].GetSelfAndChildrenRecursive<ILNode>(listNodes).Except(newExprNodes)) newExpr.ILRanges.AddRange(node.AllILRanges); foreach (var node in labelToBasicBlock[falseLabel].GetSelfAndChildrenRecursive<ILNode>(listNodes).Except(newExprNodes)) newExpr.ILRanges.AddRange(node.AllILRanges); newExpr.ILRanges.AddRange(tail[0].ILRanges); tail[1].AddSelfAndChildrenRecursiveILRanges(newExpr.ILRanges); head.Body.Add(new ILExpression(opCode, trueLocVar, newExpr)); if (isStloc) head.Body.Add(new ILExpression(ILCode.Br, trueFall)); // Remove the old basic blocks body.RemoveOrThrow(labelToBasicBlock[trueLabel]); body.RemoveOrThrow(labelToBasicBlock[falseLabel]); return true; } return false; }
public static bool MatchSingleAndBr(this ILBasicBlock bb, GMCode code, out ILExpression arg, out ILLabel brLabel) { object filler; return(bb.MatchSingleAndBr <object>(code, out filler, out arg, out brLabel)); }
public static void RemoveAtLast(this ILBasicBlock bb, int i) { bb.Body.RemoveAtLast(i); }
/// <summary> /// Group input into a set of blocks that can be later arbitraliby schufled. /// The method adds necessary branches to make control flow between blocks /// explicit and thus order independent. /// </summary> void SplitToBasicBlocks(ILBlock block) { // Remve no-ops // TODO: Assign the no-op range to someting block.Body = block.Body.Where(n => !(n is ILExpression && ((ILExpression)n).Code == ILCode.Nop)).ToList(); List<ILNode> basicBlocks = new List<ILNode>(); ILBasicBlock basicBlock = new ILBasicBlock() { EntryLabel = new ILLabel() { Name = "Block_" + (nextBlockIndex++) } }; basicBlocks.Add(basicBlock); block.EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); if (block.Body.Count > 0) { basicBlock.Body.Add(block.Body[0]); for (int i = 1; i < block.Body.Count; i++) { ILNode lastNode = block.Body[i - 1]; ILNode currNode = block.Body[i]; bool added = false; // Insert split if (currNode is ILLabel || lastNode is ILTryCatchBlock || currNode is ILTryCatchBlock || (lastNode is ILExpression) && ((ILExpression)lastNode).IsBranch() || (currNode is ILExpression) && (((ILExpression)currNode).IsBranch() && basicBlock.Body.Count > 0)) { ILBasicBlock lastBlock = basicBlock; basicBlock = new ILBasicBlock(); basicBlocks.Add(basicBlock); if (currNode is ILLabel) { // Reuse the first label basicBlock.EntryLabel = (ILLabel)currNode; added = true; } else { basicBlock.EntryLabel = new ILLabel() { Name = "Block_" + (nextBlockIndex++) }; } // Explicit branch from one block to other // (unless the last expression was unconditional branch) if (!(lastNode is ILExpression) || ((ILExpression)lastNode).Code.CanFallThough()) { lastBlock.FallthoughGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); } } if (!added) basicBlock.Body.Add(currNode); } } block.Body = basicBlocks; return; }
ILBasicBlock FindEndOfSwitch(ILBasicBlock start) { ILLabel callTo; return(FindEndOfSwitch(start, out callTo)); }
public bool SimplifyNullCoalescing(List<ILNode> body, ILBasicBlock head, int pos) { // ... // v = ldloc(leftVar) // brtrue(endBBLabel, ldloc(leftVar)) // br(rightBBLabel) // // rightBBLabel: // v = rightExpr // br(endBBLabel) // ... // => // ... // v = NullCoalescing(ldloc(leftVar), rightExpr) // br(endBBLabel) ILVariable v, v2; ILExpression leftExpr, leftExpr2; ILVariable leftVar; ILLabel endBBLabel, endBBLabel2; ILLabel rightBBLabel; ILBasicBlock rightBB; ILExpression rightExpr; if (head.Body.Count >= 3 && head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out v, out leftExpr) && leftExpr.Match(ILCode.Ldloc, out leftVar) && head.MatchLastAndBr(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) && leftExpr2.MatchLdloc(leftVar) && labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) && rightBB.MatchSingleAndBr(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) && v == v2 && endBBLabel == endBBLabel2 && labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 && body.Contains(rightBB) ) { head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br); head.Body.Add(new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr))); head.Body.Add(new ILExpression(ILCode.Br, endBBLabel)); body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]); return true; } return false; }
public bool DetectSwitch_GenerateSwitch(IList <ILNode> body, ILBasicBlock head, int pos) { ILExpression condition; ILLabel trueLabel; ILLabel fallThough; // REMEMBER: The searching goes backwards, so we find the FIRST case here, so here is the problem // Evey time we run this, we have to search for the first block of the case statement backwards and if // the push var is not resolved, we have to drop out... evey single time till the push IS resolved // so be sure to run this at the bottom of the decision chain. Its fine if the push is a simple var // but I can bet cash this runs 2-3 times if the push is some kind of expression like 5 + (3 % switch_var)) // we could change the way the optimizing loop works by going from the start and building a que of things // to remove, delete or change hummm... Take longer but it would make building this functions MUCH simpler if (MatchSwitchCase(head, out trueLabel, out fallThough, out condition) && !MatchSwitchCase(body.ElementAtOrDefault(pos - 1) as ILBasicBlock, out trueLabel, out fallThough, out condition) ) // && head.MatchLastAt(6, GMCode.Push,out switch_expr)) { List <ILBasicBlock> caseBlocks = GetAllCaseBlocks(body, head, pos, out condition, out fallThough); if (caseBlocks == null) { return(false); } ILExpression fswitch = new ILExpression(GMCode.Switch, null); FakeSwitch args = new FakeSwitch(); args.SwitchExpression = condition; args.CaseExpressions = caseBlocks.Select(bb => new KeyValuePair <ILExpression, ILLabel>((bb.ElementAtLast(3) as ILExpression).Arguments[0], (bb.ElementAtLast(1) as ILExpression).Operand as ILLabel) ).ToList(); fswitch.Operand = args; // search the blocks for ending popz's and remove the popz HashSet <ILBasicBlock> blocks_done = new HashSet <ILBasicBlock>(); Stack <ILBasicBlock> agenda = new Stack <ILBasicBlock>(caseBlocks); while (agenda.Count > 0) { ILBasicBlock bb = agenda.Pop(); if (blocks_done.Contains(bb)) { continue; // already did it } ILExpression popz = bb.Body.OfType <ILExpression>().Where(e => e.Code == GMCode.Popz).SingleOrDefault(); if (popz != null) { bb.Body.Remove(popz); // remove it } else { ILLabel exit = bb.OperandLabelLastAt(0); if (exit != null && !blocks_done.Contains(labelToBasicBlock[exit])) { agenda.Push(labelToBasicBlock[exit]); } exit = bb.OperandLabelLastAt(1); // check if we have a bt or something if (exit != null && !blocks_done.Contains(labelToBasicBlock[exit])) { agenda.Push(labelToBasicBlock[exit]); } } blocks_done.Add(bb); } ILBasicBlock startOfAllCases = caseBlocks.First(); caseBlocks.Remove(startOfAllCases); startOfAllCases.Body.RemoveTail(GMCode.Dup, GMCode.Push, GMCode.Seq, GMCode.Bt, GMCode.B); startOfAllCases.Body.Add(fswitch); startOfAllCases.Body.Add(new ILExpression(GMCode.B, fallThough)); // end_of_switch.EntryLabel())); body.RemoveAll(caseBlocks); return(true); } return(false); }
public bool SimplifyTernaryOperator(List<ILNode> body, ILBasicBlock head, int pos) { Debug.Assert(body.Contains(head)); ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; ILVariable trueLocVar = null; ILExpression trueExpr; ILLabel trueFall; ILVariable falseLocVar = null; ILExpression falseExpr; ILLabel falseFall; object unused; if (head.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) && labelGlobalRefCount[trueLabel] == 1 && labelGlobalRefCount[falseLabel] == 1 && ((labelToBasicBlock[trueLabel].MatchSingleAndBr(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) && labelToBasicBlock[falseLabel].MatchSingleAndBr(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall) && trueLocVar == falseLocVar && trueFall == falseFall) || (labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr) && labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr))) && body.Contains(labelToBasicBlock[trueLabel]) && body.Contains(labelToBasicBlock[falseLabel]) ) { bool isStloc = trueLocVar != null; ILCode opCode = isStloc ? ILCode.Stloc : ILCode.Ret; TypeReference retType = isStloc ? trueLocVar.Type : this.context.CurrentMethod.ReturnType; bool retTypeIsBoolean = TypeAnalysis.IsBoolean(retType); int leftBoolVal; int rightBoolVal; ILExpression newExpr; // a ? true:false is equivalent to a // a ? false:true is equivalent to !a // a ? true : b is equivalent to a || b // a ? b : true is equivalent to !a || b // a ? b : false is equivalent to a && b // a ? false : b is equivalent to !a && b if (retTypeIsBoolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) && ((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0)) ) { // It can be expressed as trivilal expression if (leftBoolVal != 0) { newExpr = condExpr; } else { newExpr = new ILExpression(ILCode.LogicNot, null, condExpr); } } else if (retTypeIsBoolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal)) { // It can be expressed as logical expression if (leftBoolVal != 0) { newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, condExpr, falseExpr); } else { newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr); } } else if (retTypeIsBoolean && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal)) { // It can be expressed as logical expression if (rightBoolVal != 0) { newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr); } else { newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr); } } else { // Ternary operator tends to create long complicated return statements if (opCode == ILCode.Ret) return false; // Only simplify generated variables if (opCode == ILCode.Stloc && !trueLocVar.IsGenerated) return false; // Create ternary expression newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr); } head.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); head.Body.Add(new ILExpression(opCode, trueLocVar, newExpr)); if (isStloc) head.Body.Add(new ILExpression(ILCode.Br, trueFall)); // Remove the old basic blocks body.RemoveOrThrow(labelToBasicBlock[trueLabel]); body.RemoveOrThrow(labelToBasicBlock[falseLabel]); return true; } return false; }
public bool DetectSwitch_GenerateBranches(IList <ILNode> body, ILBasicBlock head, int pos) { bool modified = false; ILExpression condition; ILLabel trueLabel; ILLabel falseLabel; ILLabel fallThough; // Debug.Assert(head.EntryLabel().Name != "Block_473"); if (MatchSwitchCase(head, out trueLabel, out fallThough, out condition)) // we ignore this first match, but remember the position { List <ILExpression> cases = new List <ILExpression>(); List <ILNode> caseBlocks = new List <ILNode>(); ILLabel prev = head.EntryLabel(); ILBasicBlock startOfCases = head; cases.Add(PreSetUpCaseBlock(startOfCases, condition)); caseBlocks.Add(startOfCases); for (int i = pos - 1; i >= 0; i--) { ILBasicBlock bb = body[i] as ILBasicBlock; if (MatchSwitchCase(bb, out trueLabel, out falseLabel, out condition)) { caseBlocks.Add(bb); cases.Add(PreSetUpCaseBlock(bb, condition)); Debug.Assert(falseLabel == prev); prev = bb.EntryLabel(); startOfCases = bb; } else { break; } } // we have all the cases // head is at the "head" of the cases ILExpression left; if (startOfCases.Body[startOfCases.Body.Count - 3].Match(GMCode.Push, out left)) { startOfCases.Body.RemoveAt(startOfCases.Body.Count - 3); foreach (var e in cases) { e.Arguments.Insert(0, new ILExpression(left)); // add the expression to all the branches } } else { throw new Exception("switch failure"); } // It seems GM makes a default case that just jumps to the end of the switch but I cannot // rely on it always being there ILBasicBlock default_case = body[pos + 1] as ILBasicBlock; Debug.Assert(default_case.EntryLabel() == head.GotoLabel()); ILBasicBlock end_of_switch = labelToBasicBlock[default_case.GotoLabel()]; if ((end_of_switch.Body[1] as ILExpression).Code == GMCode.Popz) { end_of_switch.Body.RemoveAt(1); // yeaa! } else // booo { // We have a default case so now we have to find where the popz ends, // this could be bad if we had a wierd generated for loop, but we are just doing stupid search ILBasicBlock test1 = FindEndOfSwitch(end_of_switch); // we take a sample from one of the cases to make sure we do end up at the same place ILBasicBlock test2 = FindEndOfSwitch(head); if (test1 == test2) { // two matches are good enough for me test1.Body.RemoveAt(1); // yeaa! } else { error.Error("Cannot find end of switch", end_of_switch); // booo } } // tricky part, finding that damn popz // Ok, we have all the case blocks, they are all fixed, and its like a big chain of ifs now. // But for anything OTHER than lua that has switch statments, we want to mark this for latter modified |= true; } return(modified); }
public bool SimplifyCustomShortCircuit(List<ILNode> body, ILBasicBlock head, int pos) { Debug.Assert(body.Contains(head)); // --- looking for the following pattern --- // stloc(targetVar, leftVar) // brtrue(exitLabel, call(op_False, leftVar) // br(followingBlock) // // FollowingBlock: // stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression)) // br(exitLabel) // --- if (head.Body.Count < 3) return false; // looking for: // stloc(targetVar, leftVar) ILVariable targetVar; ILExpression targetVarInitExpr; if (!head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out targetVar, out targetVarInitExpr)) return false; ILVariable leftVar; if (!targetVarInitExpr.Match(ILCode.Ldloc, out leftVar)) return false; // looking for: // brtrue(exitLabel, call(op_False, leftVar) // br(followingBlock) ILExpression callExpr; ILLabel exitLabel; ILLabel followingBlock; if(!head.MatchLastAndBr(ILCode.Brtrue, out exitLabel, out callExpr, out followingBlock)) return false; if (labelGlobalRefCount[followingBlock] > 1) return false; IMethod opFalse; ILExpression opFalseArg; if (!callExpr.Match(ILCode.Call, out opFalse, out opFalseArg)) return false; // ignore operators other than op_False and op_True if (opFalse.Name != "op_False" && opFalse.Name != "op_True") return false; if (!opFalseArg.MatchLdloc(leftVar)) return false; ILBasicBlock followingBasicBlock = labelToBasicBlock[followingBlock]; // FollowingBlock: // stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression)) // br(exitLabel) ILVariable _targetVar; ILExpression opBitwiseCallExpr; ILLabel _exitLabel; if (!followingBasicBlock.MatchSingleAndBr(ILCode.Stloc, out _targetVar, out opBitwiseCallExpr, out _exitLabel)) return false; if (_targetVar != targetVar || exitLabel != _exitLabel) return false; IMethod opBitwise; ILExpression leftVarExpression; ILExpression rightExpression; if (!opBitwiseCallExpr.Match(ILCode.Call, out opBitwise, out leftVarExpression, out rightExpression)) return false; if (!leftVarExpression.MatchLdloc(leftVar)) return false; // ignore operators other than op_BitwiseAnd and op_BitwiseOr if (opBitwise.Name != "op_BitwiseAnd" && opBitwise.Name != "op_BitwiseOr") return false; // insert: // stloc(targetVar, LogicAnd(C::op_BitwiseAnd, leftVar, rightExpression) // br(exitLabel) ILCode op = opBitwise.Name == "op_BitwiseAnd" ? ILCode.LogicAnd : ILCode.LogicOr; if (op == ILCode.LogicAnd && opFalse.Name != "op_False") return false; if (op == ILCode.LogicOr && opFalse.Name != "op_True") return false; ILExpression shortCircuitExpr = MakeLeftAssociativeShortCircuit(op, opFalseArg, rightExpression); shortCircuitExpr.Operand = opBitwise; var tail = head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br); //TODO: Keep tail's ILRanges //TODO: Keep ILRanges of other things that are removed by this method head.Body.Add(new ILExpression(ILCode.Stloc, targetVar, shortCircuitExpr)); head.Body.Add(new ILExpression(ILCode.Br, exitLabel)); body.Remove(followingBasicBlock); return true; }
public static ILNode ElementAtOrDefault(this ILBasicBlock bb, int i) { return(bb.Body.ElementAtOrDefault(i)); }
public bool SimplifyNullCoalescing(List<ILNode> body, ILBasicBlock head, int pos) { // ... // v = ldloc(leftVar) // brtrue(endBBLabel, ldloc(leftVar)) // br(rightBBLabel) // // rightBBLabel: // v = rightExpr // br(endBBLabel) // ... // => // ... // v = NullCoalescing(ldloc(leftVar), rightExpr) // br(endBBLabel) ILVariable v, v2; ILExpression leftExpr, leftExpr2; ILVariable leftVar; ILLabel endBBLabel, endBBLabel2; ILLabel rightBBLabel; ILBasicBlock rightBB; ILExpression rightExpr; if (head.Body.Count >= 3 && head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out v, out leftExpr) && leftExpr.Match(ILCode.Ldloc, out leftVar) && head.MatchLastAndBr(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) && leftExpr2.MatchLdloc(leftVar) && labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) && rightBB.MatchSingleAndBr(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) && v == v2 && endBBLabel == endBBLabel2 && labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 && body.Contains(rightBB) ) { var tail = head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br); ILExpression nullCoal, stloc; head.Body.Add(stloc = new ILExpression(ILCode.Stloc, v, nullCoal = new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr))); head.Body.Add(new ILExpression(ILCode.Br, endBBLabel)); tail[0].AddSelfAndChildrenRecursiveILRanges(stloc.ILRanges); tail[1].AddSelfAndChildrenRecursiveILRanges(nullCoal.ILRanges); tail[2].AddSelfAndChildrenRecursiveILRanges(rightExpr.ILRanges); // br (to rightBB) rightExpr.ILRanges.AddRange(rightBB.AllILRanges); rightBB.Body[0].AddSelfAndChildrenRecursiveILRanges(rightExpr.ILRanges); // label rightExpr.ILRanges.AddRange(rightBB.Body[1].ILRanges); // stloc: no recursive add rightBB.Body[2].AddSelfAndChildrenRecursiveILRanges(rightExpr.ILRanges); // br body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]); return true; } return false; }
public static T ElementAtOrDefault <T>(this ILBasicBlock bb, int i) where T : ILNode { return(bb.Body.ElementAtOrDefault(i) as T); }
private void ProcessMethodDecencies(InterMethod method, ILNode node, List <InterGenericArgument> genericArgs) { if (node is ILBlock) { ILBlock block = node as ILBlock; foreach (ILNode n in block.Body) { ProcessMethodDecencies(method, n, genericArgs); } } else if (node is ILBasicBlock) { ILBasicBlock block = node as ILBasicBlock; foreach (ILNode n in block.Body) { ProcessMethodDecencies(method, n, genericArgs); } } else if (node is ILTryCatchBlock) { ILTryCatchBlock block = node as ILTryCatchBlock; foreach (ILNode n in block.TryBlock.Body) { ProcessMethodDecencies(method, n, genericArgs); } if (block.FaultBlock != null) { foreach (ILNode n in block.FaultBlock.Body) { ProcessMethodDecencies(method, n, genericArgs); } } if (block.FinallyBlock != null) { foreach (ILNode n in block.FinallyBlock.Body) { ProcessMethodDecencies(method, n, genericArgs); } } foreach (var catchBlock in block.CatchBlocks) { ((IResolver)this).Resolve(catchBlock.ExceptionType, genericArgs); ProcessMethodDecencies(method, catchBlock, genericArgs); } } else if (node is ILExpression) { ILExpression e = node as ILExpression; foreach (var n in e.Arguments) { ProcessMethodDecencies(method, n, genericArgs); } if ((e.Code == ILCode.Mkrefany) || (e.Code == ILCode.Refanyval)) { ((IResolver)this).Resolve(ClassNames.SystemTypedReference.ClassName); } if (e.Code == ILCode.Refanytype) { ((IResolver)this).Resolve(ClassNames.SystemTypedReference.ClassName); ((IResolver)this).Resolve(ClassNames.SystemRuntimeTypeHandle.ClassName); } if (e.Code == ILCode.Arglist) { ((IResolver)this).Resolve(ClassNames.SystemRuntimeArgumentHandle.ClassName); } if (e.Code.IsExternalRealization()) { ((IResolver)this).Resolve(ClassNames.CIL2JavaVESInstructions.ClassName); } if (e.Code == ILCode.Ldc_Decimal) { ((IResolver)this).Resolve(ClassNames.SystemDecimal.ClassNames); } if (e.Code == ILCode.Ldtoken) { if (e.Operand is TypeReference) { ((IResolver)this).Resolve(ClassNames.SystemRuntimeTypeHandle.ClassName); } else if (e.Operand is FieldReference) { ((IResolver)this).Resolve(ClassNames.SystemRuntimeFieldHandle.ClassName); } else if (e.Operand is MethodReference) { ((IResolver)this).Resolve(ClassNames.SystemRuntimeMethodHandle.ClassName); } } if (e.Operand is ILVariable) { ((IResolver)this).Resolve(((ILVariable)e.Operand).Type, genericArgs); } if (e.Operand is TypeReference) { ((IResolver)this).Resolve((TypeReference)e.Operand, genericArgs); } if (e.Operand is MethodReference) { ((IResolver)this).Resolve((MethodReference)e.Operand, genericArgs); } if (e.Operand is FieldReference) { InterField fld = ((IResolver)this).Resolve((FieldReference)e.Operand, genericArgs); bool needAccessor = false; if ((fld.IsPrivate) && (fld.DeclaringType != method.DeclaringType)) { needAccessor = true; } else if ((fld.IsProtected) && (fld.DeclaringType != method.DeclaringType) && (!method.DeclaringType.IsSuper(fld.DeclaringType))) { needAccessor = true; } if (needAccessor) { switch (e.Code) { case ILCode.Ldflda: case ILCode.Ldsflda: if (fld.FieldType.IsValueType) { fld.DeclaringType.AddFieldAccessor(new FieldAccessor(FieldAccessorType.Getter, fld)); } break; case ILCode.Ldfld: case ILCode.Ldsfld: fld.DeclaringType.AddFieldAccessor(new FieldAccessor(FieldAccessorType.Getter, fld)); break; case ILCode.Stfld: case ILCode.Stsfld: fld.DeclaringType.AddFieldAccessor(new FieldAccessor(FieldAccessorType.Setter, fld)); break; } } } InterType expected = null; InterType inferred = null; if (e.ExpectedType != null) { expected = ((IResolver)this).Resolve(e.ExpectedType, genericArgs); } if (e.InferredType != null) { inferred = ((IResolver)this).Resolve(e.InferredType, genericArgs); } if ((expected != null) && (expected.IsInterface) && (inferred != null) && (inferred.IsArray)) { ((IResolver)this).Resolve(ClassNames.ArraysInterfaceAdapterTypeName, new List <InterGenericArgument>() { new InterGenericArgument(GenericArgumentOwnerType.Type, 0, inferred.ElementType) }); } } else if (node is ILWhileLoop) { ILWhileLoop loop = node as ILWhileLoop; ProcessMethodDecencies(method, loop.Condition, genericArgs); ProcessMethodDecencies(method, loop.BodyBlock, genericArgs); } else if (node is ILCondition) { ILCondition cond = node as ILCondition; ProcessMethodDecencies(method, cond.Condition, genericArgs); ProcessMethodDecencies(method, cond.TrueBlock, genericArgs); ProcessMethodDecencies(method, cond.FalseBlock, genericArgs); } else if (node is ILSwitch) { ILSwitch sw = node as ILSwitch; ProcessMethodDecencies(method, sw.Condition, genericArgs); foreach (var c in sw.CaseBlocks) { ProcessMethodDecencies(method, c, genericArgs); } } else if (node is ILFixedStatement) { ILFixedStatement fs = node as ILFixedStatement; foreach (var n in fs.Initializers) { ProcessMethodDecencies(method, n, genericArgs); } ProcessMethodDecencies(method, fs.BodyBlock, genericArgs); } }