/// <summary> /// Flattens all nested basic blocks, except the the top level 'node' argument /// </summary> void FlattenBasicBlocks(ILNode node) { ILBlock block = node as ILBlock; if (block != null) { List <ILNode> flatBody = new List <ILNode>(); foreach (ILNode child in block.GetChildren()) { FlattenBasicBlocks(child); ILBasicBlock childAsBB = child as ILBasicBlock; if (childAsBB != null) { if (!(childAsBB.Body.FirstOrDefault() is ILLabel)) { throw new Exception("Basic block has to start with a label. \n" + childAsBB.ToString()); } if (childAsBB.Body.LastOrDefault() is ILExpression && !childAsBB.Body.LastOrDefault().IsUnconditionalControlFlow()) { throw new Exception("Basci block has to end with unconditional control flow. \n" + childAsBB.ToString()); } flatBody.AddRange(childAsBB.GetChildren()); } else { flatBody.Add(child); } } block.EntryGoto = null; block.Body = flatBody; } else if (node is ILExpression) { // Optimization - no need to check expressions } else if (node != null) { // Recursively find all ILBlocks foreach (ILNode child in node.GetChildren()) { FlattenBasicBlocks(child); } } }
public bool InlineAllInBlock(ILBlock block) { bool modified = false; List <ILNode> body = block.Body; if (block is ILTryCatchBlock.CatchBlock && body.Count > 1) { ILVariable v = ((ILTryCatchBlock.CatchBlock)block).ExceptionVariable; if (v != null && v.IsGenerated) { if (numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1) { ILVariable v2; ILExpression ldException; if (body[0].Match(ILCode.Stloc, out v2, out ldException) && ldException.MatchLdloc(v)) { body.RemoveAt(0); ((ILTryCatchBlock.CatchBlock)block).ExceptionVariable = v2; modified = true; } } } } for (int i = 0; i < body.Count - 1;) { ILVariable locVar; ILExpression expr; if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(block.Body, i, aggressive: false)) { modified = true; i = Math.Max(0, i - 1); // Go back one step } else { i++; } } foreach (ILBasicBlock bb in body.OfType <ILBasicBlock>()) { modified |= InlineAllInBasicBlock(bb); } return(modified); }
/// <summary> /// Looks at the enumerator's get_Current method and figures out which of the fields holds the current value. /// </summary> void AnalyzeCurrentProperty() { MethodDefinition getCurrentMethod = enumeratorType.Methods.FirstOrDefault( m => m.Name.StartsWith("System.Collections.Generic.IEnumerator", StringComparison.Ordinal) && m.Name.EndsWith(".get_Current", StringComparison.Ordinal)); ILBlock method = CreateILAst(getCurrentMethod); if (method.Body.Count == 1) { // release builds directly return the current field ILExpression retExpr; FieldReference field; ILExpression ldFromObj; if (method.Body[0].Match(ILCode.Ret, out retExpr) && retExpr.Match(ILCode.Ldfld, out field, out ldFromObj) && ldFromObj.MatchThis()) { currentField = GetFieldDefinition(field); } } else if (method.Body.Count == 2) { ILVariable v, v2; ILExpression stExpr; FieldReference field; ILExpression ldFromObj; ILExpression retExpr; if (method.Body[0].Match(ILCode.Stloc, out v, out stExpr) && stExpr.Match(ILCode.Ldfld, out field, out ldFromObj) && ldFromObj.MatchThis() && method.Body[1].Match(ILCode.Ret, out retExpr) && retExpr.Match(ILCode.Ldloc, out v2) && v == v2) { currentField = GetFieldDefinition(field); } } if (currentField == null) { throw new SymbolicAnalysisFailedException(); } }
void ConstructExceptionTable() { disposeMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "System.IDisposable.Dispose"); ILBlock ilMethod = CreateILAst(disposeMethod); var rangeAnalysis = new StateRangeAnalysis(ilMethod.Body[0], StateRangeAnalysisMode.IteratorDispose, stateField); rangeAnalysis.AssignStateRanges(ilMethod.Body, ilMethod.Body.Count); finallyMethodToStateInterval = rangeAnalysis.finallyMethodToStateInterval; // Now look at the finally blocks: foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive <ILTryCatchBlock>()) { Interval interval = rangeAnalysis.ranges[tryFinally.TryBlock.Body[0]].ToEnclosingInterval(); var finallyBody = tryFinally.FinallyBlock.Body; if (finallyBody.Count != 2) { throw new SymbolicAnalysisFailedException(); } ILExpression call = finallyBody[0] as ILExpression; if (call == null || call.Code != ILCode.Call || call.Arguments.Count != 1) { throw new SymbolicAnalysisFailedException(); } if (!call.Arguments[0].MatchThis()) { throw new SymbolicAnalysisFailedException(); } if (!finallyBody[1].Match(ILCode.Endfinally)) { throw new SymbolicAnalysisFailedException(); } MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference); if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef)) { throw new SymbolicAnalysisFailedException(); } finallyMethodToStateInterval.Add(mdef, interval); } rangeAnalysis = null; }
public static void Run(DecompilerContext context, ILBlock method) { if (!context.Settings.YieldReturn) { return; // abort if enumerator decompilation is disabled } var yrd = new YieldReturnDecompiler(); yrd.context = context; if (!yrd.MatchEnumeratorCreationPattern(method)) { return; } yrd.enumeratorType = yrd.enumeratorCtor.DeclaringType; #if DEBUG if (Debugger.IsAttached) { yrd.Run(); } else { #endif try { yrd.Run(); } catch (SymbolicAnalysisFailedException) { return; } #if DEBUG } #endif method.Body.Clear(); method.EntryGoto = null; method.Body.AddRange(yrd.newBody); // Repeat the inlining/copy propagation optimization because the conversion of field access // to local variables can open up additional inlining possibilities. ILInlining inlining = new ILInlining(method); inlining.InlineAllVariables(); inlining.CopyPropagation(); }
public void RemoveGotos(ILBlock method) { // Build the navigation data parent[method] = null; foreach (ILNode node in method.GetSelfAndChildrenRecursive <ILNode>()) { ILNode previousChild = null; foreach (ILNode child in node.GetChildren()) { if (parent.ContainsKey(child)) { throw new Exception("The following expression is linked from several locations: " + child.ToString()); } parent[child] = node; if (previousChild != null) { nextSibling[previousChild] = child; } previousChild = child; } if (previousChild != null) { nextSibling[previousChild] = null; } } // Simplify gotos bool modified; do { modified = false; foreach (ILExpression gotoExpr in method.GetSelfAndChildrenRecursive <ILExpression>(e => e.Code == ILCode.Br || e.Code == ILCode.Leave)) { modified |= TrySimplifyGoto(gotoExpr); } } while(modified); RemoveRedundantCode(method); }
/// <summary> /// Replace endfinally with jump to the end of the finally block /// </summary> void RemoveEndFinally(ILBlock method) { // Go thought the list in reverse so that we do the nested blocks first foreach (var tryCatch in method.GetSelfAndChildrenRecursive <ILTryCatchBlock>(tc => tc.FinallyBlock != null).Reverse()) { ILLabel label = new ILLabel() { Name = "EndFinally_" + nextLabelIndex++ }; tryCatch.FinallyBlock.Body.Add(label); foreach (var block in tryCatch.FinallyBlock.GetSelfAndChildrenRecursive <ILBlock>()) { for (int i = 0; i < block.Body.Count; i++) { if (block.Body[i].Match(ILCode.Endfinally)) { block.Body[i] = new ILExpression(ILCode.Br, label).WithILRanges(((ILExpression)block.Body[i]).ILRanges); } } } } }
void RecombineVariables(ILBlock method) { // Recombine variables that were split when the ILAst was created // This ensures that a single IL variable is a single C# variable (gets assigned only one name) // The DeclareVariables transformation might then split up the C# variable again if it is used indendently in two separate scopes. Dictionary <VariableDefinition, ILVariable> dict = new Dictionary <VariableDefinition, ILVariable>(); ReplaceVariables( method, delegate(ILVariable v) { if (v.OriginalVariable == null) { return(v); } ILVariable combinedVariable; if (!dict.TryGetValue(v.OriginalVariable, out combinedVariable)) { dict.Add(v.OriginalVariable, v); combinedVariable = v; } return(combinedVariable); }); }
/// <summary> /// Looks at the enumerator's ctor and figures out which of the fields holds the state. /// </summary> void AnalyzeCtor() { ILBlock method = CreateILAst(enumeratorCtor); foreach (ILNode node in method.Body) { FieldReference field; ILExpression instExpr; ILExpression stExpr; ILVariable arg; if (node.Match(ILCode.Stfld, out field, out instExpr, out stExpr) && instExpr.MatchThis() && stExpr.Match(ILCode.Ldloc, out arg) && arg.IsParameter && arg.OriginalParameter.Index == 0) { stateField = GetFieldDefinition(field); } } if (stateField == null) { throw new SymbolicAnalysisFailedException(); } }
public static void RunStep1(DecompilerContext context, ILBlock method) { if (!context.Settings.AsyncAwait) { return; // abort if async decompilation is disabled } var yrd = new AsyncDecompiler(); yrd.context = context; if (!yrd.MatchTaskCreationPattern(method)) { return; } #if DEBUG if (Debugger.IsAttached) { yrd.Run(); } else { #endif try { yrd.Run(); } catch (SymbolicAnalysisFailedException) { return; } #if DEBUG } #endif context.CurrentMethodIsAsync = true; method.Body.Clear(); method.EntryGoto = null; method.Body.AddRange(yrd.newTopLevelBody); ILAstOptimizer.RemoveRedundantCode(method); }
void AnalyzeStateMachine(ILBlock block) { var body = block.Body; if (body.Count == 0) { throw new SymbolicAnalysisFailedException(); } if (DetectDoFinallyBodies(body)) { body.RemoveAt(0); if (body.Count == 0) { throw new SymbolicAnalysisFailedException(); } } StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.AsyncMoveNext, stateField); int bodyLength = block.Body.Count; int pos = rangeAnalysis.AssignStateRanges(body, bodyLength); rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength); var labelStateRangeMapping = rangeAnalysis.CreateLabelRangeMapping(body, pos, bodyLength); newTopLevelBody = ConvertBody(body, pos, bodyLength, labelStateRangeMapping); newTopLevelBody.Insert(0, MakeGoTo(labelStateRangeMapping, initialState)); newTopLevelBody.Add(setResultAndExitLabel); if (methodType == AsyncMethodType.TaskOfT) { newTopLevelBody.Add(new ILExpression(ILCode.Ret, null, resultExpr)); } else { newTopLevelBody.Add(new ILExpression(ILCode.Ret, null)); } }
ILBlock ConvertFinallyBlock(MethodDefinition finallyMethod) { ILBlock block = CreateILAst(finallyMethod); // Get rid of assignment to state FieldReference stfld; List <ILExpression> args; if (block.Body.Count > 0 && block.Body[0].Match(ILCode.Stfld, out stfld, out args)) { if (GetFieldDefinition(stfld) == stateField && args[0].MatchThis()) { block.Body.RemoveAt(0); } } // Convert ret to endfinally foreach (ILExpression expr in block.GetSelfAndChildrenRecursive <ILExpression>()) { if (expr.Code == ILCode.Ret) { expr.Code = ILCode.Endfinally; } } return(block); }
void ResolveIEnumerableIEnumeratorFieldMapping() { MethodDefinition getEnumeratorMethod = enumeratorType.Methods.FirstOrDefault( m => m.Name.StartsWith("System.Collections.Generic.IEnumerable", StringComparison.Ordinal) && m.Name.EndsWith(".GetEnumerator", StringComparison.Ordinal)); if (getEnumeratorMethod == null) { return; // no mappings (maybe it's just an IEnumerator implementation?) } ILBlock method = CreateILAst(getEnumeratorMethod); foreach (ILNode node in method.Body) { FieldReference stField; ILExpression stToObj; ILExpression stExpr; FieldReference ldField; ILExpression ldFromObj; if (node.Match(ILCode.Stfld, out stField, out stToObj, out stExpr) && stExpr.Match(ILCode.Ldfld, out ldField, out ldFromObj) && ldFromObj.MatchThis()) { FieldDefinition storedField = GetFieldDefinition(stField); FieldDefinition loadedField = GetFieldDefinition(ldField); if (storedField != null && loadedField != null) { ILVariable mappedParameter; if (fieldToParameterMap.TryGetValue(loadedField, out mappedParameter)) { fieldToParameterMap[storedField] = mappedParameter; } } } } }
void AnalyzeMoveNext() { MethodDefinition moveNextMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "MoveNext"); ILBlock ilMethod = CreateILAst(moveNextMethod); if (ilMethod.Body.Count == 0) { throw new SymbolicAnalysisFailedException(); } ILExpression lastReturnArg; if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg)) { throw new SymbolicAnalysisFailedException(); } // There are two possibilities: if (lastReturnArg.Code == ILCode.Ldloc) { // a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks) returnVariable = (ILVariable)lastReturnArg.Operand; returnLabel = ilMethod.Body.ElementAtOrDefault(ilMethod.Body.Count - 2) as ILLabel; if (returnLabel == null) { throw new SymbolicAnalysisFailedException(); } } else { // b) the compiler directly returns constants returnVariable = null; returnLabel = null; // In this case, the last return must return false. if (lastReturnArg.Code != ILCode.Ldc_I4 || (int)lastReturnArg.Operand != 0) { throw new SymbolicAnalysisFailedException(); } } ILTryCatchBlock tryFaultBlock = ilMethod.Body[0] as ILTryCatchBlock; List <ILNode> body; int bodyLength; if (tryFaultBlock != null) { // there are try-finally blocks if (returnVariable == null) // in this case, we must use a return variable { throw new SymbolicAnalysisFailedException(); } // must be a try-fault block: if (tryFaultBlock.CatchBlocks.Count != 0 || tryFaultBlock.FinallyBlock != null || tryFaultBlock.FaultBlock == null) { throw new SymbolicAnalysisFailedException(); } ILBlock faultBlock = tryFaultBlock.FaultBlock; // Ensure the fault block contains the call to Dispose(). if (faultBlock.Body.Count != 2) { throw new SymbolicAnalysisFailedException(); } MethodReference disposeMethodRef; ILExpression disposeArg; if (!faultBlock.Body[0].Match(ILCode.Call, out disposeMethodRef, out disposeArg)) { throw new SymbolicAnalysisFailedException(); } if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !disposeArg.MatchThis()) { throw new SymbolicAnalysisFailedException(); } if (!faultBlock.Body[1].Match(ILCode.Endfinally)) { throw new SymbolicAnalysisFailedException(); } body = tryFaultBlock.TryBlock.Body; bodyLength = body.Count; } else { // no try-finally blocks body = ilMethod.Body; if (returnVariable == null) { bodyLength = body.Count - 1; // all except for the return statement } else { bodyLength = body.Count - 2; // all except for the return label and statement } } // Now verify that the last instruction in the body is 'ret(false)' if (returnVariable != null) { // If we don't have a return variable, we already verified that above. // If we do have one, check for 'stloc(returnVariable, ldc.i4(0))' // Maybe might be a jump to the return label after the stloc: ILExpression leave = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; if (leave != null && (leave.Code == ILCode.Br || leave.Code == ILCode.Leave) && leave.Operand == returnLabel) { bodyLength--; } ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable) { throw new SymbolicAnalysisFailedException(); } if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0) { throw new SymbolicAnalysisFailedException(); } bodyLength--; // don't conside the stloc instruction to be part of the body } // verify that the last element in the body is a label pointing to the 'ret(false)' returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel; if (returnFalseLabel == null) { throw new SymbolicAnalysisFailedException(); } var rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.IteratorMoveNext, stateField); int pos = rangeAnalysis.AssignStateRanges(body, bodyLength); rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength); var labels = rangeAnalysis.CreateLabelRangeMapping(body, pos, bodyLength); ConvertBody(body, pos, bodyLength, labels); }
/// <summary> /// Removes redundatant Br, Nop, Dup, Pop /// Ignore arguments of 'leave' /// </summary> /// <param name="method"></param> internal static void RemoveRedundantCode(ILBlock method) { Dictionary <ILLabel, int> labelRefCount = new Dictionary <ILLabel, int>(); foreach (ILLabel target in method.GetSelfAndChildrenRecursive <ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { labelRefCount[target] = labelRefCount.GetOrDefault(target) + 1; } foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { List <ILNode> body = block.Body; List <ILNode> newBody = new List <ILNode>(body.Count); for (int i = 0; i < body.Count; i++) { ILLabel target; ILExpression popExpr; if (body[i].Match(ILCode.Br, out target) && i + 1 < body.Count && body[i + 1] == target) { // Ignore the branch if (labelRefCount[target] == 1) { i++; // Ignore the label as well } } else if (body[i].Match(ILCode.Nop)) { // Ignore nop } else if (body[i].Match(ILCode.Pop, out popExpr)) { ILVariable v; if (!popExpr.Match(ILCode.Ldloc, out v)) { throw new Exception("Pop should have just ldloc at this stage"); } // Best effort to move the ILRange to previous statement ILVariable prevVar; ILExpression prevExpr; if (i - 1 >= 0 && body[i - 1].Match(ILCode.Stloc, out prevVar, out prevExpr) && prevVar == v) { prevExpr.ILRanges.AddRange(((ILExpression)body[i]).ILRanges); } // Ignore pop } else { ILLabel label = body[i] as ILLabel; if (label != null) { if (labelRefCount.GetOrDefault(label) > 0) { newBody.Add(label); } } else { newBody.Add(body[i]); } } } block.Body = newBody; } // Ignore arguments of 'leave' foreach (ILExpression expr in method.GetSelfAndChildrenRecursive <ILExpression>(e => e.Code == ILCode.Leave)) { if (expr.Arguments.Any(arg => !arg.Match(ILCode.Ldloc))) { throw new Exception("Leave should have just ldloc at this stage"); } expr.Arguments.Clear(); } // 'dup' removal foreach (ILExpression expr in method.GetSelfAndChildrenRecursive <ILExpression>()) { for (int i = 0; i < expr.Arguments.Count; i++) { ILExpression child; if (expr.Arguments[i].Match(ILCode.Dup, out child)) { child.ILRanges.AddRange(expr.Arguments[i].ILRanges); expr.Arguments[i] = child; } } } }
/// <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) { List <ILNode> basicBlocks = new List <ILNode>(); ILLabel entryLabel = block.Body.FirstOrDefault() as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; ILBasicBlock basicBlock = new ILBasicBlock(); basicBlocks.Add(basicBlock); basicBlock.Body.Add(entryLabel); block.EntryGoto = new ILExpression(ILCode.Br, 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 || currNode is ILTryCatchBlock || // Counts as label lastNode.IsConditionalControlFlow() || lastNode.IsUnconditionalControlFlow()) { // Try to reuse the label ILLabel label = currNode as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++).ToString() }; // Terminate the last block if (!lastNode.IsUnconditionalControlFlow()) { // Explicit branch from one block to other basicBlock.Body.Add(new ILExpression(ILCode.Br, 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; return; }
void DuplicateReturnStatements(ILBlock method) { Dictionary <ILLabel, ILNode> nextSibling = new Dictionary <ILLabel, ILNode>(); // Build navigation data foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { for (int i = 0; i < block.Body.Count - 1; i++) { ILLabel curr = block.Body[i] as ILLabel; if (curr != null) { nextSibling[curr] = block.Body[i + 1]; } } } // Duplicate returns foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { for (int i = 0; i < block.Body.Count; i++) { ILLabel targetLabel; if (block.Body[i].Match(ILCode.Br, out targetLabel) || block.Body[i].Match(ILCode.Leave, out targetLabel)) { // Skip extra labels while (nextSibling.ContainsKey(targetLabel) && nextSibling[targetLabel] is ILLabel) { targetLabel = (ILLabel)nextSibling[targetLabel]; } // Inline return statement ILNode target; List <ILExpression> retArgs; if (nextSibling.TryGetValue(targetLabel, out target)) { if (target.Match(ILCode.Ret, out retArgs)) { ILVariable locVar; object constValue; if (retArgs.Count == 0) { block.Body[i] = new ILExpression(ILCode.Ret, null) { Original = new List <ILExpression> { (ILExpression)block.Body[i] } }; } else if (retArgs.Single().Match(ILCode.Ldloc, out locVar)) { block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, locVar)) { Original = new List <ILExpression> { (ILExpression)block.Body[i] } }; } else if (retArgs.Single().Match(ILCode.Ldc_I4, out constValue)) { block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldc_I4, constValue)) { Original = new List <ILExpression> { (ILExpression)block.Body[i] } }; } } } else { if (method.Body.Count > 0 && method.Body.Last() == targetLabel) { // It exits the main method - so it is same as return; block.Body[i] = new ILExpression(ILCode.Ret, null); } } } } } }
bool MatchEnumeratorCreationPattern(ILBlock method) { if (method.Body.Count == 0) { return(false); } ILExpression newObj; if (method.Body.Count == 1) { // ret(newobj(...)) if (method.Body[0].Match(ILCode.Ret, out newObj)) { return(MatchEnumeratorCreationNewObj(newObj, out enumeratorCtor)); } else { return(false); } } // stloc(var_1, newobj(..) ILVariable var1; if (!method.Body[0].Match(ILCode.Stloc, out var1, out newObj)) { return(false); } if (!MatchEnumeratorCreationNewObj(newObj, out enumeratorCtor)) { return(false); } int i; for (i = 1; i < method.Body.Count; i++) { // stfld(..., ldloc(var_1), ldloc(parameter)) FieldReference storedField; ILExpression ldloc, loadParameter; if (!method.Body[i].Match(ILCode.Stfld, out storedField, out ldloc, out loadParameter)) { break; } ILVariable loadedVar, loadedArg; if (!ldloc.Match(ILCode.Ldloc, out loadedVar) || !loadParameter.Match(ILCode.Ldloc, out loadedArg)) { return(false); } storedField = GetFieldDefinition(storedField); if (loadedVar != var1 || storedField == null || !loadedArg.IsParameter) { return(false); } fieldToParameterMap[(FieldDefinition)storedField] = loadedArg; } ILVariable var2; ILExpression ldlocForStloc2; if (i < method.Body.Count && method.Body[i].Match(ILCode.Stloc, out var2, out ldlocForStloc2)) { // stloc(var_2, ldloc(var_1)) if (ldlocForStloc2.Code != ILCode.Ldloc || ldlocForStloc2.Operand != var1) { return(false); } i++; } else { // the compiler might skip the above instruction in release builds; in that case, it directly returns stloc.Operand var2 = var1; } ILExpression retArg; if (i < method.Body.Count && method.Body[i].Match(ILCode.Ret, out retArg)) { // ret(ldloc(var_2)) if (retArg.Code == ILCode.Ldloc && retArg.Operand == var2) { return(true); } } return(false); }
public static void RemoveRedundantCode(ILBlock method) { // Remove dead lables and nops HashSet <ILLabel> liveLabels = new HashSet <ILLabel>(method.GetSelfAndChildrenRecursive <ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())); foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { block.Body = block.Body.Where(n => !n.Match(ILCode.Nop) && !(n is ILLabel && !liveLabels.Contains((ILLabel)n))).ToList(); } // Remove redundant continue foreach (ILWhileLoop loop in method.GetSelfAndChildrenRecursive <ILWhileLoop>()) { var body = loop.BodyBlock.Body; if (body.Count > 0 && body.Last().Match(ILCode.LoopContinue)) { body.RemoveAt(body.Count - 1); } } // Remove redundant break at the end of case // Remove redundant case blocks altogether foreach (ILSwitch ilSwitch in method.GetSelfAndChildrenRecursive <ILSwitch>()) { foreach (ILBlock ilCase in ilSwitch.CaseBlocks) { Debug.Assert(ilCase.EntryGoto == null); int count = ilCase.Body.Count; if (count >= 2) { if (ilCase.Body[count - 2].IsUnconditionalControlFlow() && ilCase.Body[count - 1].Match(ILCode.LoopOrSwitchBreak)) { ilCase.Body.RemoveAt(count - 1); } } } var defaultCase = ilSwitch.CaseBlocks.SingleOrDefault(cb => cb.Values == null); // If there is no default block, remove empty case blocks if (defaultCase == null || (defaultCase.Body.Count == 1 && defaultCase.Body.Single().Match(ILCode.LoopOrSwitchBreak))) { ilSwitch.CaseBlocks.RemoveAll(b => b.Body.Count == 1 && b.Body.Single().Match(ILCode.LoopOrSwitchBreak)); } } // Remove redundant return at the end of method if (method.Body.Count > 0 && method.Body.Last().Match(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) { method.Body.RemoveAt(method.Body.Count - 1); } // Remove unreachable return statements bool modified = false; foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { for (int i = 0; i < block.Body.Count - 1;) { if (block.Body[i].IsUnconditionalControlFlow() && block.Body[i + 1].Match(ILCode.Ret)) { modified = true; block.Body.RemoveAt(i + 1); } else { i++; } } } if (modified) { // More removals might be possible new GotoRemoval().RemoveGotos(method); } }
public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) { this.context = context; this.typeSystem = context.CurrentMethod.Module.TypeSystem; this.method = method; if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode) { return; } RemoveRedundantCode(method); if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) { return; } foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { ReduceBranchInstructionSet(block); } // ReduceBranchInstructionSet runs before inlining because the non-aggressive inlining heuristic // looks at which type of instruction consumes the inlined variable. if (abortBeforeStep == ILAstOptimizationStep.InlineVariables) { return; } // Works better after simple goto removal because of the following debug pattern: stloc X; br Next; Next:; ldloc X ILInlining inlining1 = new ILInlining(method); inlining1.InlineAllVariables(); if (abortBeforeStep == ILAstOptimizationStep.CopyPropagation) { return; } inlining1.CopyPropagation(); if (abortBeforeStep == ILAstOptimizationStep.YieldReturn) { return; } YieldReturnDecompiler.Run(context, method); AsyncDecompiler.RunStep1(context, method); if (abortBeforeStep == ILAstOptimizationStep.AsyncAwait) { return; } AsyncDecompiler.RunStep2(context, method); if (abortBeforeStep == ILAstOptimizationStep.PropertyAccessInstructions) { return; } IntroducePropertyAccessInstructions(method); if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) { return; } foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { SplitToBasicBlocks(block); } if (abortBeforeStep == ILAstOptimizationStep.TypeInference) { return; } // Types are needed for the ternary operator optimization TypeAnalysis.Run(context, method); foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { bool modified; do { modified = false; if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) { return; } modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyShortCircuit); if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) { return; } modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyTernaryOperator); if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) { return; } modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyNullCoalescing); if (abortBeforeStep == ILAstOptimizationStep.JoinBasicBlocks) { return; } modified |= block.RunOptimization(new SimpleControlFlow(context, method).JoinBasicBlocks); if (abortBeforeStep == ILAstOptimizationStep.SimplifyLogicNot) { return; } modified |= block.RunOptimization(SimplifyLogicNot); if (abortBeforeStep == ILAstOptimizationStep.SimplifyShiftOperators) { return; } modified |= block.RunOptimization(SimplifyShiftOperators); if (abortBeforeStep == ILAstOptimizationStep.TypeConversionSimplifications) { return; } modified |= block.RunOptimization(TypeConversionSimplifications); if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) { return; } modified |= block.RunOptimization(SimplifyLdObjAndStObj); if (abortBeforeStep == ILAstOptimizationStep.SimplifyCustomShortCircuit) { return; } modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyCustomShortCircuit); if (abortBeforeStep == ILAstOptimizationStep.SimplifyLiftedOperators) { return; } modified |= block.RunOptimization(SimplifyLiftedOperators); if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) { return; } modified |= block.RunOptimization(TransformArrayInitializers); if (abortBeforeStep == ILAstOptimizationStep.TransformMultidimensionalArrayInitializers) { return; } modified |= block.RunOptimization(TransformMultidimensionalArrayInitializers); if (abortBeforeStep == ILAstOptimizationStep.TransformObjectInitializers) { return; } modified |= block.RunOptimization(TransformObjectInitializers); if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) { return; } if (context.Settings.MakeAssignmentExpressions) { modified |= block.RunOptimization(MakeAssignmentExpression); } modified |= block.RunOptimization(MakeCompoundAssignments); if (abortBeforeStep == ILAstOptimizationStep.IntroducePostIncrement) { return; } if (context.Settings.IntroduceIncrementAndDecrement) { modified |= block.RunOptimization(IntroducePostIncrement); } if (abortBeforeStep == ILAstOptimizationStep.InlineExpressionTreeParameterDeclarations) { return; } if (context.Settings.ExpressionTrees) { modified |= block.RunOptimization(InlineExpressionTreeParameterDeclarations); } if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) { return; } modified |= new ILInlining(method).InlineAllInBlock(block); new ILInlining(method).CopyPropagation(); } while(modified); } if (abortBeforeStep == ILAstOptimizationStep.FindLoops) { return; } foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { new LoopsAndConditions(context).FindLoops(block); } if (abortBeforeStep == ILAstOptimizationStep.FindConditions) { return; } foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { new LoopsAndConditions(context).FindConditions(block); } if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) { return; } FlattenBasicBlocks(method); if (abortBeforeStep == ILAstOptimizationStep.RemoveEndFinally) { return; } RemoveEndFinally(method); if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode2) { return; } RemoveRedundantCode(method); if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) { return; } new GotoRemoval().RemoveGotos(method); if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) { return; } DuplicateReturnStatements(method); if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval2) { return; } new GotoRemoval().RemoveGotos(method); if (abortBeforeStep == ILAstOptimizationStep.ReduceIfNesting) { return; } ReduceIfNesting(method); if (abortBeforeStep == ILAstOptimizationStep.InlineVariables3) { return; } // The 2nd inlining pass is necessary because DuplicateReturns and the introduction of ternary operators // open up additional inlining possibilities. new ILInlining(method).InlineAllVariables(); if (abortBeforeStep == ILAstOptimizationStep.CachedDelegateInitialization) { return; } if (context.Settings.AnonymousMethods) { foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { for (int i = 0; i < block.Body.Count; i++) { // TODO: Move before loops CachedDelegateInitializationWithField(block, ref i); CachedDelegateInitializationWithLocal(block, ref i); } } } if (abortBeforeStep == ILAstOptimizationStep.IntroduceFixedStatements) { return; } // we need post-order traversal, not pre-order, for "fixed" to work correctly foreach (ILBlock block in TreeTraversal.PostOrder <ILNode>(method, n => n.GetChildren()).OfType <ILBlock>()) { for (int i = block.Body.Count - 1; i >= 0; i--) { // TODO: Move before loops if (i < block.Body.Count) { IntroduceFixedStatements(block.Body, i); } } } if (abortBeforeStep == ILAstOptimizationStep.RecombineVariables) { return; } RecombineVariables(method); if (abortBeforeStep == ILAstOptimizationStep.TypeInference2) { return; } TypeAnalysis.Reset(method); TypeAnalysis.Run(context, method); if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode3) { return; } GotoRemoval.RemoveRedundantCode(method); // ReportUnassignedILRanges(method); }
/// <summary> /// Get the first expression to be excecuted if the instruction pointer is at the start of the given node. /// Try blocks may not be entered in any way. If possible, the try block is returned as the node to be executed. /// </summary> ILNode Enter(ILNode node, HashSet <ILNode> visitedNodes) { if (node == null) { throw new ArgumentNullException(); } if (!visitedNodes.Add(node)) { return(null); // Infinite loop } ILLabel label = node as ILLabel; if (label != null) { return(Exit(label, visitedNodes)); } ILExpression expr = node as ILExpression; if (expr != null) { if (expr.Code == ILCode.Br || expr.Code == ILCode.Leave) { ILLabel target = (ILLabel)expr.Operand; // Early exit - same try-block if (GetParents(expr).OfType <ILTryCatchBlock>().FirstOrDefault() == GetParents(target).OfType <ILTryCatchBlock>().FirstOrDefault()) { return(Enter(target, visitedNodes)); } // Make sure we are not entering any try-block var srcTryBlocks = GetParents(expr).OfType <ILTryCatchBlock>().Reverse().ToList(); var dstTryBlocks = GetParents(target).OfType <ILTryCatchBlock>().Reverse().ToList(); // Skip blocks that we are already in int i = 0; while (i < srcTryBlocks.Count && i < dstTryBlocks.Count && srcTryBlocks[i] == dstTryBlocks[i]) { i++; } if (i == dstTryBlocks.Count) { return(Enter(target, visitedNodes)); } else { ILTryCatchBlock dstTryBlock = dstTryBlocks[i]; // Check that the goto points to the start ILTryCatchBlock current = dstTryBlock; while (current != null) { foreach (ILNode n in current.TryBlock.Body) { if (n is ILLabel) { if (n == target) { return(dstTryBlock); } } else if (!n.Match(ILCode.Nop)) { current = n as ILTryCatchBlock; break; } } } return(null); } } else if (expr.Code == ILCode.Nop) { return(Exit(expr, visitedNodes)); } else if (expr.Code == ILCode.LoopOrSwitchBreak) { ILNode breakBlock = GetParents(expr).First(n => n is ILWhileLoop || n is ILSwitch); return(Exit(breakBlock, new HashSet <ILNode>() { expr })); } else if (expr.Code == ILCode.LoopContinue) { ILNode continueBlock = GetParents(expr).First(n => n is ILWhileLoop); return(Enter(continueBlock, new HashSet <ILNode>() { expr })); } else { return(expr); } } ILBlock block = node as ILBlock; if (block != null) { if (block.EntryGoto != null) { return(Enter(block.EntryGoto, visitedNodes)); } else if (block.Body.Count > 0) { return(Enter(block.Body[0], visitedNodes)); } else { return(Exit(block, visitedNodes)); } } ILCondition cond = node as ILCondition; if (cond != null) { return(cond.Condition); } ILWhileLoop loop = node as ILWhileLoop; if (loop != null) { if (loop.Condition != null) { return(loop.Condition); } else { return(Enter(loop.BodyBlock, visitedNodes)); } } ILTryCatchBlock tryCatch = node as ILTryCatchBlock; if (tryCatch != null) { return(tryCatch); } ILSwitch ilSwitch = node as ILSwitch; if (ilSwitch != null) { return(ilSwitch.Condition); } throw new NotSupportedException(node.GetType().ToString()); }
public ILInlining(ILBlock method) { this.method = method; AnalyzeMethod(); }
bool MatchTaskCreationPattern(ILBlock method) { if (method.Body.Count < 5) { return(false); } // Check the second-to-last instruction (the start call) first, as we can get the most information from that MethodReference startMethod; ILExpression loadStartTarget, loadStartArgument; // call(AsyncTaskMethodBuilder::Start, ldloca(builder), ldloca(stateMachine)) if (!method.Body[method.Body.Count - 2].Match(ILCode.Call, out startMethod, out loadStartTarget, out loadStartArgument)) { return(false); } if (startMethod.Name != "Start" || startMethod.DeclaringType == null || startMethod.DeclaringType.Namespace != "System.Runtime.CompilerServices") { return(false); } switch (startMethod.DeclaringType.Name) { case "AsyncTaskMethodBuilder`1": methodType = AsyncMethodType.TaskOfT; break; case "AsyncTaskMethodBuilder": methodType = AsyncMethodType.Task; break; case "AsyncVoidMethodBuilder": methodType = AsyncMethodType.Void; break; default: return(false); } ILVariable stateMachineVar, builderVar; if (!loadStartTarget.Match(ILCode.Ldloca, out builderVar)) { return(false); } if (!loadStartArgument.Match(ILCode.Ldloca, out stateMachineVar)) { return(false); } stateMachineStruct = stateMachineVar.Type.ResolveWithinSameModule(); if (stateMachineStruct == null || !stateMachineStruct.IsValueType) { return(false); } moveNextMethod = stateMachineStruct.Methods.FirstOrDefault(f => f.Name == "MoveNext"); if (moveNextMethod == null) { return(false); } // Check third-to-last instruction (copy of builder): // stloc(builder, ldfld(StateMachine::<>t__builder, ldloca(stateMachine))) ILExpression loadBuilderExpr; if (!method.Body[method.Body.Count - 3].MatchStloc(builderVar, out loadBuilderExpr)) { return(false); } FieldReference builderFieldRef; ILExpression loadStateMachineForBuilderExpr; if (!loadBuilderExpr.Match(ILCode.Ldfld, out builderFieldRef, out loadStateMachineForBuilderExpr)) { return(false); } if (!loadStateMachineForBuilderExpr.MatchLdloca(stateMachineVar)) { return(false); } builderField = builderFieldRef.ResolveWithinSameModule(); if (builderField == null) { return(false); } // Check the last instruction (ret) if (methodType == AsyncMethodType.Void) { if (!method.Body[method.Body.Count - 1].Match(ILCode.Ret)) { return(false); } } else { // ret(call(AsyncTaskMethodBuilder::get_Task, ldflda(StateMachine::<>t__builder, ldloca(stateMachine)))) ILExpression returnValue; if (!method.Body[method.Body.Count - 1].Match(ILCode.Ret, out returnValue)) { return(false); } MethodReference getTaskMethod; ILExpression builderExpr; if (!returnValue.Match(ILCode.Call, out getTaskMethod, out builderExpr)) { return(false); } ILExpression loadStateMachineForBuilderExpr2; FieldReference builderField2; if (!builderExpr.Match(ILCode.Ldflda, out builderField2, out loadStateMachineForBuilderExpr2)) { return(false); } if (builderField2.ResolveWithinSameModule() != builderField || !loadStateMachineForBuilderExpr2.MatchLdloca(stateMachineVar)) { return(false); } } // Check the last field assignment - this should be the state field ILExpression initialStateExpr; if (!MatchStFld(method.Body[method.Body.Count - 4], stateMachineVar, out stateField, out initialStateExpr)) { return(false); } if (!initialStateExpr.Match(ILCode.Ldc_I4, out initialState)) { return(false); } if (initialState != -1) { return(false); } // Check the second-to-last field assignment - this should be the builder field FieldDefinition builderField3; ILExpression builderInitialization; if (!MatchStFld(method.Body[method.Body.Count - 5], stateMachineVar, out builderField3, out builderInitialization)) { return(false); } MethodReference createMethodRef; if (builderField3 != builderField || !builderInitialization.Match(ILCode.Call, out createMethodRef)) { return(false); } if (createMethodRef.Name != "Create") { return(false); } for (int i = 0; i < method.Body.Count - 5; i++) { FieldDefinition field; ILExpression fieldInit; if (!MatchStFld(method.Body[i], stateMachineVar, out field, out fieldInit)) { return(false); } ILVariable v; if (!fieldInit.Match(ILCode.Ldloc, out v)) { return(false); } if (!v.IsParameter) { return(false); } fieldToParameterMap[field] = v; } return(true); }