public bool SimplifyShortCircuit(IList <ILNode> body, ILBasicBlock head, int pos) { Debug.Assert(body.Contains(head)); ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; if (head.MatchLastAndBr(GMCode.Bt, 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(GMCode.Bt, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) && nextCondExpr.Code != GMCode.Pop && // ugh (otherLablel == nextFalseLabel || otherLablel == nextTrueLablel)) { // Debug.Assert(nextCondExpr.Arguments.Count != 2); // Create short cicuit branch ILExpression logicExpr; if (otherLablel == nextFalseLabel) { logicExpr = MakeLeftAssociativeShortCircuit(GMCode.LogicAnd, negate ? new ILExpression(GMCode.Not, null, condExpr) : condExpr, nextCondExpr); } else { logicExpr = MakeLeftAssociativeShortCircuit(GMCode.LogicOr, negate ? condExpr : new ILExpression(GMCode.Not, null, condExpr), nextCondExpr); } head.Body.RemoveTail(GMCode.Bt, GMCode.B); head.Body.Add(new ILExpression(GMCode.Bt, nextTrueLablel, logicExpr)); head.Body.Add(new ILExpression(GMCode.B, nextFalseLabel)); // Remove the inlined branch from scope body.RemoveOrThrow(nextBasicBlock); return(true); } } } return(false); }
// Detect a switch block, combine them all, and either build a switch block or // just a bunch of if statements // the trick is to get rid of the popv at the end of all these case statements // might just have to be removed with the "remove redudent code" system bool MatchSwitchCase(ILBasicBlock head, out ILLabel trueLabel, out ILLabel falseLabel, out ILExpression condition) { if (head != null && head.MatchLastAndBr(GMCode.Bt, out trueLabel, out falseLabel) && head.MatchLastAt(3, GMCode.Seq) && head.MatchLastAt(4, GMCode.Push, out condition) && head.MatchLastAt(5, GMCode.Dup)) { return(true); } trueLabel = default(ILLabel); falseLabel = default(ILLabel); condition = default(ILExpression); return(false); }
public static bool MatchCaseBlock(this ILBasicBlock bb, out ILExpression caseCondition, out ILLabel caseLabel, out ILLabel nextCase) { int dupType = 0; ILExpression btExpresion; if (bb.Body.Count == 6 && bb.Body[0] is ILLabel && bb.Body[1].Match(GMCode.Dup, out dupType) && dupType == 0 && bb.Body[2].Match(GMCode.Push, out caseCondition) && bb.Body[3].Match(GMCode.Seq) && bb.MatchLastAndBr(GMCode.Bt, out caseLabel, out btExpresion, out nextCase) ) { return(true); } caseCondition = default(ILExpression); caseLabel = nextCase = default(ILLabel); return(false); }
// 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 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); }
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 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 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 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 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 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; }
/// <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); }