/// <summary> /// Reduces the branch codes to just br and brtrue. /// Moves ILRanges to the branch argument /// </summary> void ReduceBranchInstructionSet(ILBlock block) { for (int i = 0; i < block.Body.Count; i++) { ILExpression expr = block.Body[i] as ILExpression; if (expr != null && expr.Prefixes == null) { switch (expr.Code) { case ILCode.Switch: case ILCode.Brtrue: expr.Arguments.Single().ILRanges.AddRange(expr.ILRanges); expr.ILRanges.Clear(); continue; case ILCode.__Brfalse: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, expr.Arguments.Single())); break; case ILCode.__Beq: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Ceq, null, expr.Arguments)); break; case ILCode.__Bne_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Ceq, null, expr.Arguments))); break; case ILCode.__Bgt: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Cgt, null, expr.Arguments)); break; case ILCode.__Bgt_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Cgt_Un, null, expr.Arguments)); break; case ILCode.__Ble: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Cgt, null, expr.Arguments))); break; case ILCode.__Ble_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Cgt_Un, null, expr.Arguments))); break; case ILCode.__Blt: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Clt, null, expr.Arguments)); break; case ILCode.__Blt_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Clt_Un, null, expr.Arguments)); break; case ILCode.__Bge: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Clt, null, expr.Arguments))); break; case ILCode.__Bge_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Clt_Un, null, expr.Arguments))); break; default: continue; } ((ILExpression)block.Body[i]).Arguments.Single().ILRanges.AddRange(expr.ILRanges); } } }
/// <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.GeneratedByDecompiler) { 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[0].AddSelfAndChildrenRecursiveILRanges(((ILTryCatchBlock.CatchBlock)block).StlocILRanges); 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, 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> /// Converts call and callvirt instructions that read/write properties into CallGetter/CallSetter instructions. /// /// CallGetter/CallSetter is used to allow the ILAst to represent "while ((SomeProperty = value) != null)". /// </summary> void IntroducePropertyAccessInstructions(ILBlock method) { foreach (ILExpression expr in method.GetSelfAndChildrenRecursive <ILExpression>()) { if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) { MethodReference cecilMethod = (MethodReference)expr.Operand; if (cecilMethod.DeclaringType is ArrayType) { switch (cecilMethod.Name) { case "Get": expr.Code = ILCode.CallGetter; break; case "Set": expr.Code = ILCode.CallSetter; break; case "Address": expr.Code = ILCode.CallGetter; expr.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress)); break; } } else { MethodDefinition cecilMethodDef = cecilMethod.Resolve(); if (cecilMethodDef != null) { if (cecilMethodDef.IsGetter) { expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallGetter : ILCode.CallvirtGetter; } else if (cecilMethodDef.IsSetter) { expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter; } } } } } }
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(); }
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; }
/// <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(); } }
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; var list = method.GetSelfAndChildrenRecursive <ILExpression>(e => e.Code == ILCode.Br || e.Code == ILCode.Leave); for (int i = list.Count - 1; i >= 0; i--) { var gotoExpr = list[i]; modified |= TrySimplifyGoto(gotoExpr); } } while(modified); RemoveRedundantCode(method); }
public static void Run(DecompilerContext context, ILBlock method, List <ILNode> list_ILNode, Func <ILBlock, ILInlining> getILInlining) { 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 && CRASH_IN_DEBUG_MODE if (Debugger.IsAttached) { yrd.Run(); } else { #endif try { yrd.Run(); } catch (SymbolicAnalysisFailedException) { return; } #if DEBUG && CRASH_IN_DEBUG_MODE } #endif method.Body.Clear(); method.EntryGoto = null; method.Body.AddRange(yrd.newBody); //TODO: Make sure that the removed ILRanges from Clear() above is saved in the new body // Repeat the inlining/copy propagation optimization because the conversion of field access // to local variables can open up additional inlining possibilities. var inlining = getILInlining(method); inlining.InlineAllVariables(); inlining.CopyPropagation(list_ILNode); }
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>(); foreach (ILExpression expr in method.GetSelfAndChildrenRecursive <ILExpression>()) { ILVariable v = expr.Operand as ILVariable; if (v != null && v.OriginalVariable != null) { ILVariable combinedVariable; if (!dict.TryGetValue(v.OriginalVariable, out combinedVariable)) { dict.Add(v.OriginalVariable, v); combinedVariable = v; } expr.Operand = combinedVariable; } } }
/// <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); } } } } }
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); }
/// <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(); } }
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); }); }
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; } } } } }
public static void RunStep1(DecompilerContext context, ILBlock method, List <ILExpression> listExpr, List <ILBlock> listBlock, Dictionary <ILLabel, int> labelRefCount) { 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); //TODO: Make sure that the removed ILRanges from Clear() above is saved in the new body ILAstOptimizer.RemoveRedundantCode(method, listExpr, listBlock, labelRefCount); }
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); }
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); } else if (retArgs.Single().Match(ILCode.Ldloc, out locVar)) { block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, locVar)); } else if (retArgs.Single().Match(ILCode.Ldc_I4, out constValue)) { block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldc_I4, constValue)); } } } 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); } } } } } }
public SimpleControlFlow(DecompilerContext context, ILBlock method) { Initialize(context, method); }
void CachedDelegateInitialization(ILBlock block, ref int i) { // if (logicnot(ldsfld(field))) { // stsfld(field, newobj(Action::.ctor, ldnull(), ldftn(method))) // } else { // } // ...(..., ldsfld(field), ...) ILCondition c = block.Body[i] as ILCondition; if (c == null || c.Condition == null && c.TrueBlock == null || c.FalseBlock == null) { return; } if (!(c.TrueBlock.Body.Count == 1 && c.FalseBlock.Body.Count == 0)) { return; } if (!c.Condition.Match(ILCode.LogicNot)) { return; } ILExpression condition = c.Condition.Arguments.Single() as ILExpression; if (condition == null || condition.Code != ILCode.Ldsfld) { return; } FieldDefinition field = ((FieldReference)condition.Operand).ResolveWithinSameModule(); // field is defined in current assembly if (field == null || !field.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) { return; } ILExpression stsfld = c.TrueBlock.Body[0] as ILExpression; if (!(stsfld != null && stsfld.Code == ILCode.Stsfld && ((FieldReference)stsfld.Operand).ResolveWithinSameModule() == field)) { return; } ILExpression newObj = stsfld.Arguments[0]; if (!(newObj.Code == ILCode.Newobj && newObj.Arguments.Count == 2)) { return; } if (newObj.Arguments[0].Code != ILCode.Ldnull) { return; } if (newObj.Arguments[1].Code != ILCode.Ldftn) { return; } MethodDefinition anonymousMethod = ((MethodReference)newObj.Arguments[1].Operand).ResolveWithinSameModule(); // method is defined in current assembly if (!Ast.Transforms.DelegateConstruction.IsAnonymousMethod(context, anonymousMethod)) { return; } ILNode followingNode = block.Body.ElementAtOrDefault(i + 1); if (followingNode != null && followingNode.GetSelfAndChildrenRecursive <ILExpression>().Count( e => e.Code == ILCode.Ldsfld && ((FieldReference)e.Operand).ResolveWithinSameModule() == field) == 1) { foreach (ILExpression parent in followingNode.GetSelfAndChildrenRecursive <ILExpression>()) { for (int j = 0; j < parent.Arguments.Count; j++) { if (parent.Arguments[j].Code == ILCode.Ldsfld && ((FieldReference)parent.Arguments[j].Operand).ResolveWithinSameModule() == field) { parent.Arguments[j] = newObj; block.Body.RemoveAt(i); i -= new ILInlining(method).InlineInto(block.Body, i, aggressive: true); return; } } } } }
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 ILInlining(ILBlock method) { this.method = method; AnalyzeMethod(); }
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); } if (method.Body.Count > 0 && method.Body.Last().Match(ILCode.YieldBreak) && ((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); 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.TransformDecimalCtorToConstant) { return; } modified |= block.RunOptimization(TransformDecimalCtorToConstant); modified |= block.RunOptimization(SimplifyLdcI4ConvI8); if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) { return; } modified |= block.RunOptimization(SimplifyLdObjAndStObj); if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) { return; } modified |= block.RunOptimization(TransformArrayInitializers); if (abortBeforeStep == ILAstOptimizationStep.TransformObjectInitializers) { return; } modified |= block.RunOptimization(TransformObjectInitializers); if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) { return; } modified |= block.RunOptimization(MakeAssignmentExpression); modified |= block.RunOptimization(MakeCompoundAssignments); if (abortBeforeStep == ILAstOptimizationStep.IntroducePostIncrement) { return; } modified |= block.RunOptimization(IntroducePostIncrement); 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.RemoveRedundantCode2) { return; } RemoveRedundantCode(method); if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) { return; } new GotoRemoval().RemoveGotos(method); if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) { return; } DuplicateReturnStatements(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; } 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); }
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); }
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>()) { var newBody = new List <ILNode>(block.Body.Count); for (int i = 0; i < block.Body.Count; i++) { var node = block.Body[i]; if (node.Match(ILCode.Nop)) { Utils.NopMergeILRanges(block, newBody, i); } else if (node is ILLabel && !liveLabels.Contains((ILLabel)node)) { Utils.LabelMergeILRanges(block, newBody, i); } else { newBody.Add(node); } } block.Body = newBody; } // Remove redundant continue foreach (ILWhileLoop loop in method.GetSelfAndChildrenRecursive <ILWhileLoop>()) { var body = loop.BodyBlock.Body; if (body.Count > 0 && body.Last().Match(ILCode.LoopContinue)) { loop.EndILRanges.AddRange(body[body.Count - 1].GetSelfAndChildrenRecursiveILRanges()); 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)) { var prev = ilCase.Body[count - 2]; prev.EndILRanges.AddRange(ilCase.Body[count - 1].GetSelfAndChildrenRecursiveILRanges()); 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))) { for (int i = ilSwitch.CaseBlocks.Count - 1; i >= 0; i--) { var caseBlock = ilSwitch.CaseBlocks[i]; if (caseBlock.Body.Count != 1 || !caseBlock.Body.Single().Match(ILCode.LoopOrSwitchBreak)) { continue; } ilSwitch.EndILRanges.AddRange(caseBlock.Body[0].GetSelfAndChildrenRecursiveILRanges()); ilSwitch.CaseBlocks.RemoveAt(i); } } } // 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.EndILRanges.AddRange(method.Body[method.Body.Count - 1].GetSelfAndChildrenRecursiveILRanges()); 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.EndILRanges.AddRange(block.Body[i + 1].GetSelfAndChildrenRecursiveILRanges()); block.Body.RemoveAt(i + 1); } else { i++; } } } if (modified) { // More removals might be possible new GotoRemoval().RemoveGotos(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()); }
/// <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 is ILLabel ? ((ILLabel)currNode) : new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; // 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; }