public Tracker(TypedInstructionCollection instructions) { DBC.Pre(instructions != null, "instructions is null"); m_instructions = instructions; m_initialState = DoGetInitialState(m_instructions); }
public Lattice(TypedInstructionCollection instructions, State state) { DBC.Pre(instructions != null, "instructions is null"); m_instructions = instructions; m_state = state; }
public Tracker(TypedInstructionCollection instructions) { var graph = new ControlFlowGraph(instructions); var initialState = new Lattice(instructions, new State(0)); var data = new DataFlow <Lattice>(instructions, graph.Roots); Dictionary <BasicBlock, Lattice> lattices = data.Analyze(new Lattice.Functions(), initialState); m_states = new State[instructions.Length]; foreach (var entry in lattices) { BasicBlock block = entry.Key; if (block.Length > 0) { Lattice lattice = entry.Value; for (int index = block.First.Index; index <= block.Last.Index; ++index) // it'd be nice to assert that every index was set, but methods often have dead code so it's a little difficult { m_states[index] = lattice.State; lattice = lattice.Transform(index); } } } for (int index = 0; index < instructions.Length; ++index) { Log.DebugLine(this, "{0:X2}: {1}", instructions[index].Untyped.Offset, m_states[index]); } }
public Tracker(TypedInstructionCollection instructions) { DBC.Pre(instructions != null, "instructions is null"); m_instructions = instructions; m_initialState = DoGetInitialState(m_instructions); }
// This is another case where the lattice meet and transform paradigm // doesn't work so well. What we want to do is set locals and arguments // based on the result of null pointer comparisons. Doing this in meet // would be really ugly and would break commutativity so we handle it // by splicing in a special block to each edge leading out from the branch. private void DoSpliceNullCheck(TypedInstructionCollection instructions, BasicBlock block, List <BasicBlock> visited) { if (visited.IndexOf(block) < 0) { visited.Add(block); if (block.Next.Length == 2) { DoTrySpliceNull(instructions, block); } for (int i = 0; i < block.Next.Length; ++i) { DoSpliceNullCheck(instructions, block.Next[i], visited); } if (block.Finally != null) { DoSpliceNullCheck(instructions, block.Finally, visited); } else if (block.Fault != null) { DoSpliceNullCheck(instructions, block.Fault, visited); } } }
internal TryCatchCollection(TypedInstructionCollection instructions) { if (instructions.Method.Body != null) { DoGetTryCatches(instructions); } }
private static string DoGetMissedOffsets(TypedInstructionCollection instructions, List <BasicBlock> reachable, List <BasicBlock> deadBlocks) { // Get a list of all the offsets in the method. List <int> offsets = new List <int>(instructions.Length); foreach (TypedInstruction instruction in instructions) { offsets.Add(instruction.Untyped.Offset); } // Remove the visited instructions. foreach (BasicBlock block in reachable) { DoRemoveOffsets(instructions, offsets, block); } // Remove the dead code. foreach (BasicBlock block in deadBlocks) { DoRemoveOffsets(instructions, offsets, block); } // Anything left is code we should have visited but didn't. return(ListExtensions.Accumulate(offsets, string.Empty, (s, e) => s + e.ToString("X2") + " ")); }
private List <int> DoGetLeaders(TypedInstructionCollection instructions) { // The leaders will be: List <int> leaders = new List <int>(); // 1) the first instruction in the method, leaders.Add(0); for (int index = 0; index < instructions.Length; ++index) { TypedInstruction instruction = instructions[index]; // 2) the targets of branches, Branch branch = instruction as Branch; if (branch != null) { m_branchTargets.Add(branch.Target.Index); } else { Switch swtch = instruction as Switch; if (swtch != null) { foreach (TypedInstruction i in swtch.Targets) { m_branchTargets.Add(i.Index); } } } if (index > 0) { // 3) and instructions starting try, catch or finally blocks, TryCatch tc = instructions.TryCatchCollection.HandlerStartsAt(index); if (tc != null) { leaders.Add(index); } // 4) the instructions following branches or end instructions. else if (instructions[index - 1] is Branch || instructions[index - 1] is Switch || instructions[index - 1] is End) { leaders.Add(index); } } } foreach (int target in m_branchTargets) { if (leaders.IndexOf(target) < 0) { leaders.Add(target); } } return(leaders); }
private static void DoRemoveOffsets(TypedInstructionCollection instructions, List <int> offsets, BasicBlock block) { if (block.Length > 0) { for (int i = block.First.Index; i <= block.Last.Index; ++i) { Unused.Value = offsets.Remove(instructions[i].Untyped.Offset); } } }
private static Block DoGetBlock(TypedInstructionCollection instructions, int startOffset, int endOffset) { int startIndex = instructions.OffsetToIndex(startOffset); int endIndex = instructions.OffsetToIndex(endOffset); Block block = new Block(); block.Index = startIndex; block.Length = endIndex - startIndex; return(block); }
private static void DoSpliceEq(TypedInstructionCollection instructions, BasicBlock block) { int index = block.Last.Index; // ldloc.0 V_0 // ldnull // beq 2F LoadArg arg = null; LoadLocal local = null; if (instructions[index - 1].Untyped.OpCode.Code == Code.Ldnull) { arg = instructions[block.Last.Index - 2] as LoadArg; local = instructions[block.Last.Index - 2] as LoadLocal; } else if (instructions[index - 2].Untyped.OpCode.Code == Code.Ldnull) { arg = instructions[block.Last.Index - 1] as LoadArg; local = instructions[block.Last.Index - 1] as LoadLocal; } if (arg != null || local != null) { TypedInstruction instruction = instructions[block.Last.Index]; Code code = instruction.Untyped.OpCode.Code; if (code == Code.Bne_Un || code == Code.Bne_Un_S) { if (arg != null) { DoSpliceNull(block, FirstNullArgBlockLen - arg.Arg, FirstNonNullArgBlockLen - arg.Arg); } else { DoSpliceNull(block, FirstNullLocalBlockLen - local.Variable, FirstNonNullLocalBlockLen - local.Variable); } } else { if (arg != null) { DoSpliceNull(block, FirstNonNullArgBlockLen - arg.Arg, FirstNullArgBlockLen - arg.Arg); } else { DoSpliceNull(block, FirstNonNullLocalBlockLen - local.Variable, FirstNullLocalBlockLen - local.Variable); } } } }
private int DoGetIndex(TypedInstructionCollection instructions, string text) { foreach (LoadString load in instructions.Match <LoadString>()) { if (load.Value.Contains(text)) { return(load.Index); } } DBC.Fail("Couldn't find {0}", text); return(-1); }
private Dictionary <int, BasicBlock> DoGetWiredBlocks(TypedInstructionCollection instructions) { DBC.Pre(instructions.Length > 0, "Can't get a root if there are no instructions"); m_branchTargets = new List <int>(); Dictionary <int, BasicBlock> blocks = DoGetUnwiredBlocks(instructions); // index -> block foreach (KeyValuePair <int, BasicBlock> entry in blocks) { DoWireBlock(instructions, blocks, entry.Value); } return(blocks); }
private static void DoTrySpliceNull(TypedInstructionCollection instructions, BasicBlock block) { TypedInstruction instruction = instructions[block.Last.Index]; Code code = instruction.Untyped.OpCode.Code; if (code == Code.Brtrue || code == Code.Brtrue_S || code == Code.Brfalse || code == Code.Brfalse_S) { DoSpliceTrue(instructions, block); } else if (code == Code.Beq || code == Code.Beq_S || code == Code.Bne_Un || code == Code.Bne_Un_S) { DoSpliceEq(instructions, block); } }
public void ZNestedFinally() { TypeDefinition type = DoGetType("Cases"); MethodDefinition method = type.Methods.GetMethod("NestedFinally")[0]; TypedInstructionCollection instructions = new TypedInstructionCollection(new SymbolTable(), method); Log.DebugLine((ControlFlowGraph)null, "-----------------------------------"); Log.DebugLine((ControlFlowGraph)null, "{0:F}", instructions); ControlFlowGraph graph = new ControlFlowGraph(instructions); Assert.AreEqual(2, graph.Roots.Length); }
private void DoInit(string methodName) { string loc = Assembly.GetExecutingAssembly().Location; AssemblyDefinition assembly = AssemblyFactory.GetAssembly(loc); string fullName = string.Format("{0}/{1}", GetType().FullName, "Cases"); TypeDefinition type = assembly.MainModule.Types[fullName]; MethodDefinition method = type.Methods.GetMethod(methodName)[0]; m_instructions = new TypedInstructionCollection(new SymbolTable(), method); m_tracker = new Tracker(m_instructions); m_tracker.Analyze(); }
// Before leaving a try or catch block we need to transfer control to // the finally block (if present). private static void DoAddFinally(TypedInstructionCollection instructions, List <BasicBlock> nextBlocks, Dictionary <int, BasicBlock> blocks, BasicBlock block) { TryCatch candidate = null; for (int i = 0; i < instructions.TryCatchCollection.Length; ++i) { TryCatch tc = instructions.TryCatchCollection[i]; if (tc.Finally.Length > 0) { if (block.First.Index >= tc.Try.Index && block.Last.Index < tc.Try.Index + tc.Try.Length) { if (candidate == null || tc.Try.Length < candidate.Try.Length) { candidate = tc; } } else { for (int j = 0; j < tc.Catchers.Count; ++j) { if (block.First.Index >= tc.Catchers[j].Index && block.Last.Index < tc.Catchers[j].Index + tc.Catchers[j].Length) { if (candidate == null || tc.Try.Length < candidate.Try.Length) { candidate = tc; } } } } } else if (tc.Fault.Length > 0) { for (int j = 0; j < tc.Catchers.Count; ++j) { if (block.First.Index >= tc.Catchers[j].Index && block.Last.Index < tc.Catchers[j].Index + tc.Catchers[j].Length) { block.Fault = blocks[tc.Fault.Index]; DoWireCatches(instructions, nextBlocks, blocks, tc.Fault.Index); return; } } } } if (candidate != null) { block.Finally = blocks[candidate.Finally.Index]; DoWireCatches(instructions, nextBlocks, blocks, candidate.Finally.Index); } }
public void Linear() { TypeDefinition type = DoGetType("Cases"); MethodDefinition method = type.Methods.GetMethod("Linear")[0]; var instructions = new TypedInstructionCollection(new SymbolTable(), method); var tracker = new V0.Tracker(instructions); int index = DoGetIndex(instructions, "first"); Assert.AreEqual(0, tracker.State(index).Value); index = DoGetIndex(instructions, "second"); Assert.AreEqual(3, tracker.State(index).Value); }
public void TrashStackTrace() { TypeDefinition type = DoGetType("Cases"); MethodDefinition method = type.Methods.GetMethod("TrashStackTrace")[0]; TypedInstructionCollection instructions = new TypedInstructionCollection(new SymbolTable(), method); Log.DebugLine((ControlFlowGraph)null, "-----------------------------------"); Log.DebugLine((ControlFlowGraph)null, "{0:F}", instructions); ControlFlowGraph graph = new ControlFlowGraph(instructions); Assert.AreEqual(2, graph.Roots.Length); Log.DebugLine((ControlFlowGraph)null, "Root 1:\n{0:T}", graph.Roots[0]); Log.DebugLine((ControlFlowGraph)null, "Root 2:\n{0:T}", graph.Roots[1]); }
public static readonly int LastNonNullLocalBlockLen = -4999; // A_999 #region Private Methods ------------------------------------------- // Catch and finally blocks require special handling to reset their stacks // on entry. We can't really do this in meet without breaking commutativity // and transform is too late. So what we do is splice in a special zero // length block right before the handler and when it is transformed reset // the stack. private void DoSpliceHandlers(TypedInstructionCollection instructions, BasicBlock block, List <BasicBlock> visited) { if (visited.IndexOf(block) < 0) { visited.Add(block); for (int i = 0; i < block.Next.Length; ++i) { BasicBlock next = block.Next[i]; if (next.Length > 0) { TryCatch tc = instructions.TryCatchCollection.HandlerStartsAt(next.First.Index); if (tc != null && tc.Try.Index != next.First.Index) { BasicBlock splice = null; if (tc.Finally.Length > 0 && tc.Finally.Index == next.First.Index) { splice = new BasicBlock(FinallyBlockLen, new BasicBlock[1]); } else if (tc.Fault.Length > 0 && tc.Fault.Index == next.First.Index) { splice = new BasicBlock(FinallyBlockLen, new BasicBlock[1]); } else { splice = new BasicBlock(CatchBlockLen, new BasicBlock[1]); } splice.Next[0] = next; block.Next[i] = splice; } } DoSpliceHandlers(instructions, next, visited); } if (block.Finally != null) { DoSpliceHandlers(instructions, block.Finally, visited); } else if (block.Fault != null) { DoSpliceHandlers(instructions, block.Fault, visited); } } }
private List <BasicBlock> DoGetDeadBlocks(TypedInstructionCollection instructions, Dictionary <int, BasicBlock> blocks, List <BasicBlock> reachable) { List <BasicBlock> deadBlocks = new List <BasicBlock>(); // If the block isn't the target of a branch and it's preceded by an end // instruction it's dead code. This can happen if a catch block throws. foreach (KeyValuePair <int, BasicBlock> entry in blocks) { BasicBlock b = entry.Value; int i = b.First.Index; if (m_branchTargets.IndexOf(i) < 0) { if (instructions.TryCatchCollection.HandlerStartsAt(i) == null) { if (i > 0 && (instructions[i - 1] is End || instructions[i - 1] is UnconditionalBranch)) { deadBlocks.Add(b); } } } } // If a leave is dead code then its target is dead as well. List <BasicBlock> deaderBlocks = new List <BasicBlock>(); foreach (BasicBlock b in deadBlocks) { if (b.Length > 0) { for (int i = b.First.Index; i <= b.Last.Index; ++i) { if (instructions[i].Untyped.OpCode.Code == Code.Leave || instructions[i].Untyped.OpCode.Code == Code.Leave_S) { UnconditionalBranch branch = (UnconditionalBranch)instructions[i]; if (reachable.IndexOf(blocks[branch.Target.Index]) < 0 && deaderBlocks.IndexOf(blocks[branch.Target.Index]) < 0) { deaderBlocks.Add(blocks[branch.Target.Index]); } } } } } deadBlocks.AddRange(deaderBlocks); return(deadBlocks); }
public ControlFlowGraph(TypedInstructionCollection instructions) { DBC.Pre(instructions != null, "instructions is null"); Profile.Start("ControlFlowGraph ctor"); // If we have instructions then, if (instructions.Length > 0) { // get all of the wired up basic blocks, Dictionary <int, BasicBlock> blocks = DoGetWiredBlocks(instructions); m_numBlocks = blocks.Count; // if the very first instruction is a try block then we'll have // mutiple roots, foreach (TryCatch tc in instructions.TryCatchCollection) { if (tc.Try.Index == 0) { m_roots = new BasicBlock[tc.Catchers.Count + 1]; m_roots[0] = blocks[0]; for (int i = 0; i < tc.Catchers.Count; ++i) { CatchBlock cb = tc.Catchers[i]; m_roots[i + 1] = blocks[cb.Index]; } } } // otherwise the first instruction is our root. if (m_roots == null) { m_roots = new BasicBlock[] { blocks[0] } } ; DoInvariantRoots(instructions, blocks, m_roots); } else { m_roots = new BasicBlock[0]; } Profile.Stop("ControlFlowGraph ctor"); }
public void Catch1() { TypeDefinition type = DoGetType("Cases"); MethodDefinition method = type.Methods.GetMethod("Catch1")[0]; var instructions = new TypedInstructionCollection(new SymbolTable(), method); var tracker = new V0.Tracker(instructions); int index = DoGetIndex(instructions, "first"); Assert.AreEqual(2, tracker.State(index).Value); index = DoGetIndex(instructions, "catch"); Assert.AreEqual(0, tracker.State(index).Value); index = DoGetIndex(instructions, "last"); Assert.IsFalse(tracker.State(index).HasValue); }
private static void DoWireCatches(TypedInstructionCollection instructions, List <BasicBlock> nextBlocks, Dictionary <int, BasicBlock> blocks, int targetIndex) { foreach (TryCatch tc in instructions.TryCatchCollection) { if (tc.Try.Index == targetIndex) { foreach (CatchBlock cb in tc.Catchers) { nextBlocks.Add(blocks[cb.Index]); } if (tc.Fault.Length > 0 && tc.Catchers.Count == 0) { nextBlocks.Add(blocks[tc.Fault.Index]); } } } }
public void Linear() { TypeDefinition type = DoGetType("Cases"); MethodDefinition method = type.Methods.GetMethod("Linear")[0]; TypedInstructionCollection instructions = new TypedInstructionCollection(new SymbolTable(), method); Log.DebugLine((ControlFlowGraph)null, "-----------------------------------"); Log.DebugLine((ControlFlowGraph)null, "{0:F}", instructions); ControlFlowGraph graph = new ControlFlowGraph(instructions); Assert.AreEqual(1, graph.Roots.Length); Log.DebugLine((ControlFlowGraph)null, "\n{0:T}", graph.Roots[0]); Assert.AreEqual(1, graph.Length); Assert.AreEqual(0, graph.Roots[0].First.Index); Assert.AreEqual(instructions.Length - 1, graph.Roots[0].Last.Index); }
private static string DoTargetsToString(TypedInstructionCollection instructions, List <int> targets) { StringBuilder builder = new StringBuilder(); builder.Append('['); for (int i = 0; i < targets.Count; ++i) { int index = targets[i]; TypedInstruction instruction = instructions[index]; builder.Append(instruction.Untyped.Offset.ToString("X2")); if (i + 1 < targets.Count) { builder.Append(", "); } } builder.Append(']'); return(builder.ToString()); }
public ControlFlowGraph(TypedInstructionCollection instructions) { DBC.Pre(instructions != null, "instructions is null"); Profile.Start("ControlFlowGraph ctor"); // If we have instructions then, if (instructions.Length > 0) { // get all of the wired up basic blocks, Dictionary<int, BasicBlock> blocks = DoGetWiredBlocks(instructions); m_numBlocks = blocks.Count; // if the very first instruction is a try block then we'll have // mutiple roots, foreach (TryCatch tc in instructions.TryCatchCollection) { if (tc.Try.Index == 0) { m_roots = new BasicBlock[tc.Catchers.Count + 1]; m_roots[0] = blocks[0]; for (int i = 0; i < tc.Catchers.Count; ++i) { CatchBlock cb = tc.Catchers[i]; m_roots[i + 1] = blocks[cb.Index]; } } } // otherwise the first instruction is our root. if (m_roots == null) m_roots = new BasicBlock[]{blocks[0]}; DoInvariantRoots(instructions, blocks, m_roots); } else m_roots = new BasicBlock[0]; Profile.Stop("ControlFlowGraph ctor"); }
private static void DoSpliceTrue(TypedInstructionCollection instructions, BasicBlock block) { int index = block.Last.Index; // ldarg.1 a1 // brtrue 10 (not taken is the first branch) LoadArg arg = instructions[index - 1] as LoadArg; LoadLocal local = instructions[index - 1] as LoadLocal; if (arg != null || local != null) { TypedInstruction instruction = instructions[index]; Code code = instruction.Untyped.OpCode.Code; if (code == Code.Brtrue || code == Code.Brtrue_S) { if (arg != null) { DoSpliceNull(block, FirstNullArgBlockLen - arg.Arg, FirstNonNullArgBlockLen - arg.Arg); } else { DoSpliceNull(block, FirstNullLocalBlockLen - local.Variable, FirstNonNullLocalBlockLen - local.Variable); } } else { if (arg != null) { DoSpliceNull(block, FirstNonNullArgBlockLen - arg.Arg, FirstNullArgBlockLen - arg.Arg); } else { DoSpliceNull(block, FirstNonNullLocalBlockLen - local.Variable, FirstNullLocalBlockLen - local.Variable); } } } }
public DataFlow(TypedInstructionCollection instructions, BasicBlock[] roots) { DBC.Pre(instructions != null, "instructions is null"); DBC.Pre(roots.Length > 0, "no roots"); Log.DebugLine(this, "{0:F}", instructions); if (instructions.Method.Body.Variables.Count < 256) { if (roots.Length == 1) { m_entry = roots[0]; } else { m_entry = new BasicBlock(0, roots); } DoAddReachableToWorkList(m_entry); DoAddPredecessors(null, m_entry); // The algorithm is much faster if predecessor blocks appear first. m_workList.Sort(delegate(BasicBlock lhs, BasicBlock rhs) { return(lhs.SafeIndex.CompareTo(rhs.SafeIndex)); }); #if LOG_BLOCKS DoLogWorkList(); DoLogPredecessors(); #endif } else { // Methods with a very large number of local variables can cause dataflow // to take a very long time so we skip them. m_skipped = true; Log.InfoLine(this, "skipping DataFlow for {0} (it has {1} locals)", instructions.Method, instructions.Method.Body.Variables.Count); } }
private static Lattice DoGetInitialState(TypedInstructionCollection instructions) { // Get the arguments. int numArgs = instructions.Method.Parameters.Count + 1; long?[] args = new long?[numArgs]; for (int i = 0; i < numArgs; ++i) { if (instructions.Method.HasThis && i == 0) { args[i] = 1; } else { args[i] = null; } } // Get the locals. int numLocals = instructions.Method.Body.Variables.Count; long?value = 0; if (!instructions.Method.Body.InitLocals) { value = null; } long?[] locals = new long?[numLocals]; for (int i = 0; i < numLocals; ++i) { locals[i] = value; } // Get the stack. List <StackEntry> stack = new List <StackEntry>(); return(new Lattice(instructions, new State(args, locals, stack))); }
private Dictionary <int, BasicBlock> DoGetUnwiredBlocks(TypedInstructionCollection instructions) { // First get a list of all the instructions which start a block. List <int> leaders = DoGetLeaders(instructions); // Sort them so we can efficiently pop them off the list. leaders.Sort(); Log.TraceLine(this, "Leaders: {0}", DoTargetsToString(instructions, leaders)); leaders.Reverse(); // And form the basic blocks by starting at the first leader and // accumulating instructions until we hit another leader. Dictionary <int, BasicBlock> blocks = new Dictionary <int, BasicBlock>(); while (leaders.Count > 0) { int first = leaders[leaders.Count - 1]; leaders.RemoveAt(leaders.Count - 1); BasicBlock block; if (leaders.Count > 0) { int last = leaders[leaders.Count - 1]; DBC.Assert(first < last, "Leader {0} should be before {1}", first, last); block = new BasicBlock(instructions[first], instructions[last - 1]); } else { block = new BasicBlock(instructions[first], instructions[instructions.Length - 1]); } blocks.Add(first, block); Log.DebugLine(this, "Added block: {0}", block); } return(blocks); }
private int DoCheckCatch(TypedInstructionCollection instructions, CatchBlock cb, int varIndex) { int offset = -1; // Loop through all of the instructions in the catch block, for (int index = cb.Index; index < cb.Index + cb.Length && offset < 0; ++index) { TypedInstruction instruction = instructions[index]; // if we find a throw instruction, if (instruction.Untyped.OpCode.Code == Code.Throw) { // and it's preceded by a load from varIndex then we have a problem. LoadLocal load = instructions[index - 1] as LoadLocal; if (load != null && load.Variable == varIndex) { offset = instruction.Untyped.Offset; Log.DebugLine(this, "bad throw at {0:X2}", offset); } } } return offset; }
public void TwoIfs() { TypeDefinition type = DoGetType("Cases"); MethodDefinition method = type.Methods.GetMethod("TwoIfs")[0]; var instructions = new TypedInstructionCollection(new SymbolTable(), method); var tracker = new V0.Tracker(instructions); int index = DoGetIndex(instructions, "second"); Assert.AreEqual(2, tracker.State(index).Value); index = DoGetIndex(instructions, "third"); Assert.AreEqual(3, tracker.State(index).Value); index = DoGetIndex(instructions, "fourth"); Assert.IsFalse(tracker.State(index).HasValue); index = DoGetIndex(instructions, "fifth"); Assert.AreEqual(5, tracker.State(index).Value); index = DoGetIndex(instructions, "last"); Assert.IsFalse(tracker.State(index).HasValue); }
private Dictionary<int, BasicBlock> DoGetWiredBlocks(TypedInstructionCollection instructions) { DBC.Pre(instructions.Length > 0, "Can't get a root if there are no instructions"); m_branchTargets = new List<int>(); Dictionary<int, BasicBlock> blocks = DoGetUnwiredBlocks(instructions); // index -> block foreach (KeyValuePair<int, BasicBlock> entry in blocks) DoWireBlock(instructions, blocks, entry.Value); return blocks; }
private static string DoTargetsToString(TypedInstructionCollection instructions, List<int> targets) { StringBuilder builder = new StringBuilder(); builder.Append('['); for (int i = 0; i < targets.Count; ++i) { int index = targets[i]; TypedInstruction instruction = instructions[index]; builder.Append(instruction.Untyped.Offset.ToString("X2")); if (i + 1 < targets.Count) builder.Append(", "); } builder.Append(']'); return builder.ToString(); }
private static void DoSpliceEq(TypedInstructionCollection instructions, BasicBlock block) { int index = block.Last.Index; // ldloc.0 V_0 // ldnull // beq 2F LoadArg arg = null; LoadLocal local = null; if (instructions[index - 1].Untyped.OpCode.Code == Code.Ldnull) { arg = instructions[block.Last.Index - 2] as LoadArg; local = instructions[block.Last.Index - 2] as LoadLocal; } else if (instructions[index - 2].Untyped.OpCode.Code == Code.Ldnull) { arg = instructions[block.Last.Index - 1] as LoadArg; local = instructions[block.Last.Index - 1] as LoadLocal; } if (arg != null || local != null) { TypedInstruction instruction = instructions[block.Last.Index]; Code code = instruction.Untyped.OpCode.Code; if (code == Code.Bne_Un || code == Code.Bne_Un_S) { if (arg != null) DoSpliceNull(block, FirstNullArgBlockLen - arg.Arg, FirstNonNullArgBlockLen - arg.Arg); else DoSpliceNull(block, FirstNullLocalBlockLen - local.Variable, FirstNonNullLocalBlockLen - local.Variable); } else { if (arg != null) DoSpliceNull(block, FirstNonNullArgBlockLen - arg.Arg, FirstNullArgBlockLen - arg.Arg); else DoSpliceNull(block, FirstNonNullLocalBlockLen - local.Variable, FirstNullLocalBlockLen - local.Variable); } } }
public void TrashStackTrace() { TypeDefinition type = DoGetType("Cases"); MethodDefinition method = type.Methods.GetMethod("TrashStackTrace")[0]; TypedInstructionCollection instructions = new TypedInstructionCollection(new SymbolTable(), method); Log.DebugLine((ControlFlowGraph) null, "-----------------------------------"); Log.DebugLine((ControlFlowGraph) null, "{0:F}", instructions); ControlFlowGraph graph = new ControlFlowGraph(instructions); Assert.AreEqual(2, graph.Roots.Length); Log.DebugLine((ControlFlowGraph) null, "Root 1:\n{0:T}", graph.Roots[0]); Log.DebugLine((ControlFlowGraph) null, "Root 2:\n{0:T}", graph.Roots[1]); }
public void ZFinally() { TypeDefinition type = DoGetType("Cases"); MethodDefinition method = type.Methods.GetMethod("Finally")[0]; var instructions = new TypedInstructionCollection(new SymbolTable(), method); var tracker = new V0.Tracker(instructions); int index = DoGetIndex(instructions, "first"); Assert.AreEqual(2, tracker.State(index).Value); index = DoGetIndex(instructions, "catch"); Assert.AreEqual(3, tracker.State(index).Value); index = DoGetIndex(instructions, "finally"); Assert.AreEqual(2, tracker.State(index).Value); index = DoGetIndex(instructions, "last"); Assert.AreEqual(2, tracker.State(index).Value); }
public void Linear() { TypeDefinition type = DoGetType("Cases"); MethodDefinition method = type.Methods.GetMethod("Linear")[0]; TypedInstructionCollection instructions = new TypedInstructionCollection(new SymbolTable(), method); Log.DebugLine((ControlFlowGraph) null, "-----------------------------------"); Log.DebugLine((ControlFlowGraph) null, "{0:F}", instructions); ControlFlowGraph graph = new ControlFlowGraph(instructions); Assert.AreEqual(1, graph.Roots.Length); Log.DebugLine((ControlFlowGraph) null, "\n{0:T}", graph.Roots[0]); Assert.AreEqual(1, graph.Length); Assert.AreEqual(0, graph.Roots[0].First.Index); Assert.AreEqual(instructions.Length - 1, graph.Roots[0].Last.Index); }
private static Lattice DoGetInitialState(TypedInstructionCollection instructions) { // Get the arguments. int numArgs = instructions.Method.Parameters.Count + 1; long?[] args = new long?[numArgs]; for (int i = 0; i < numArgs; ++i) if (instructions.Method.HasThis && i == 0) args[i] = 1; else args[i] = null; // Get the locals. int numLocals = instructions.Method.Body.Variables.Count; long? value = 0; if (!instructions.Method.Body.InitLocals) value = null; long?[] locals = new long?[numLocals]; for (int i = 0; i < numLocals; ++i) locals[i] = value; // Get the stack. List<StackEntry> stack = new List<StackEntry>(); return new Lattice(instructions, new State(args, locals, stack)); }
// Before leaving a try or catch block we need to transfer control to // the finally block (if present). private static void DoAddFinally(TypedInstructionCollection instructions, List<BasicBlock> nextBlocks, Dictionary<int, BasicBlock> blocks, BasicBlock block) { TryCatch candidate = null; for (int i = 0; i < instructions.TryCatchCollection.Length; ++i) { TryCatch tc = instructions.TryCatchCollection[i]; if (tc.Finally.Length > 0) { if (block.First.Index >= tc.Try.Index && block.Last.Index < tc.Try.Index + tc.Try.Length) { if (candidate == null || tc.Try.Length < candidate.Try.Length) candidate = tc; } else { for (int j = 0; j < tc.Catchers.Count; ++j) { if (block.First.Index >= tc.Catchers[j].Index && block.Last.Index < tc.Catchers[j].Index + tc.Catchers[j].Length) { if (candidate == null || tc.Try.Length < candidate.Try.Length) candidate = tc; } } } } else if (tc.Fault.Length > 0) { for (int j = 0; j < tc.Catchers.Count; ++j) { if (block.First.Index >= tc.Catchers[j].Index && block.Last.Index < tc.Catchers[j].Index + tc.Catchers[j].Length) { block.Fault = blocks[tc.Fault.Index]; DoWireCatches(instructions, nextBlocks, blocks, tc.Fault.Index); return; } } } } if (candidate != null) { block.Finally = blocks[candidate.Finally.Index]; DoWireCatches(instructions, nextBlocks, blocks, candidate.Finally.Index); } }
private List<int> DoGetLeaders(TypedInstructionCollection instructions) { // The leaders will be: List<int> leaders = new List<int>(); // 1) the first instruction in the method, leaders.Add(0); for (int index = 0; index < instructions.Length; ++index) { TypedInstruction instruction = instructions[index]; // 2) the targets of branches, Branch branch = instruction as Branch; if (branch != null) { m_branchTargets.Add(branch.Target.Index); } else { Switch swtch = instruction as Switch; if (swtch != null) { foreach (TypedInstruction i in swtch.Targets) m_branchTargets.Add(i.Index); } } if (index > 0) { // 3) and instructions starting try, catch or finally blocks, TryCatch tc = instructions.TryCatchCollection.HandlerStartsAt(index); if (tc != null) leaders.Add(index); // 4) the instructions following branches or end instructions. else if (instructions[index - 1] is Branch || instructions[index - 1] is Switch || instructions[index - 1] is End) leaders.Add(index); } } foreach (int target in m_branchTargets) { if (leaders.IndexOf(target) < 0) leaders.Add(target); } return leaders; }
private static void DoWireCatches(TypedInstructionCollection instructions, List<BasicBlock> nextBlocks, Dictionary<int, BasicBlock> blocks, int targetIndex) { foreach (TryCatch tc in instructions.TryCatchCollection) { if (tc.Try.Index == targetIndex) { foreach (CatchBlock cb in tc.Catchers) nextBlocks.Add(blocks[cb.Index]); if (tc.Fault.Length > 0 && tc.Catchers.Count == 0) nextBlocks.Add(blocks[tc.Fault.Index]); } } }
private Dictionary<int, BasicBlock> DoGetUnwiredBlocks(TypedInstructionCollection instructions) { // First get a list of all the instructions which start a block. List<int> leaders = DoGetLeaders(instructions); // Sort them so we can efficiently pop them off the list. leaders.Sort(); Log.TraceLine(this, "Leaders: {0}", DoTargetsToString(instructions, leaders)); leaders.Reverse(); // And form the basic blocks by starting at the first leader and // accumulating instructions until we hit another leader. Dictionary<int, BasicBlock> blocks = new Dictionary<int, BasicBlock>(); while (leaders.Count > 0) { int first = leaders[leaders.Count - 1]; leaders.RemoveAt(leaders.Count - 1); BasicBlock block; if (leaders.Count > 0) { int last = leaders[leaders.Count - 1]; DBC.Assert(first < last, "Leader {0} should be before {1}", first, last); block = new BasicBlock(instructions[first], instructions[last - 1]); } else { block = new BasicBlock(instructions[first], instructions[instructions.Length - 1]); } blocks.Add(first, block); Log.DebugLine(this, "Added block: {0}", block); } return blocks; }
private static void DoSpliceTrue(TypedInstructionCollection instructions, BasicBlock block) { int index = block.Last.Index; // ldarg.1 a1 // brtrue 10 (not taken is the first branch) LoadArg arg = instructions[index - 1] as LoadArg; LoadLocal local = instructions[index - 1] as LoadLocal; if (arg != null || local != null) { TypedInstruction instruction = instructions[index]; Code code = instruction.Untyped.OpCode.Code; if (code == Code.Brtrue || code == Code.Brtrue_S) { if (arg != null) DoSpliceNull(block, FirstNullArgBlockLen - arg.Arg, FirstNonNullArgBlockLen - arg.Arg); else DoSpliceNull(block, FirstNullLocalBlockLen - local.Variable, FirstNonNullLocalBlockLen - local.Variable); } else { if (arg != null) DoSpliceNull(block, FirstNonNullArgBlockLen - arg.Arg, FirstNullArgBlockLen - arg.Arg); else DoSpliceNull(block, FirstNonNullLocalBlockLen - local.Variable, FirstNullLocalBlockLen - local.Variable); } } }
// This is another case where the lattice meet and transform paradigm // doesn't work so well. What we want to do is set locals and arguments // based on the result of null pointer comparisons. Doing this in meet // would be really ugly and would break commutativity so we handle it // by splicing in a special block to each edge leading out from the branch. private void DoSpliceNullCheck(TypedInstructionCollection instructions, BasicBlock block, List<BasicBlock> visited) { if (visited.IndexOf(block) < 0) { visited.Add(block); if (block.Next.Length == 2) DoTrySpliceNull(instructions, block); for (int i = 0; i < block.Next.Length; ++i) DoSpliceNullCheck(instructions, block.Next[i], visited); if (block.Finally != null) DoSpliceNullCheck(instructions, block.Finally, visited); else if (block.Fault != null) DoSpliceNullCheck(instructions, block.Fault, visited); } }
private int DoGetIndex(TypedInstructionCollection instructions, string text) { foreach (LoadString load in instructions.Match<LoadString>()) { if (load.Value.Contains(text)) return load.Index; } DBC.Fail("Couldn't find {0}", text); return -1; }
private void DoInvariantRoots(TypedInstructionCollection instructions, Dictionary<int, BasicBlock> blocks, BasicBlock[] roots) { List<BasicBlock> deadBlocks = null, reachable = null; try { // Get all of the blocks reachable from roots, reachable = new List<BasicBlock>(); foreach (BasicBlock b in roots) DoGetReachableBlocks(reachable, b); int sum = 0; for (int i = 0; i < reachable.Count; ++i) { BasicBlock b1 = reachable[i]; Log.TraceLine(this, "reachable{0} = {1}", i, b1); sum += b1.Last.Index - b1.First.Index + 1; // they should be sane, DBC.Assert(b1.First.Index <= b1.Last.Index, "block {0} has bad indexes", b1); DBC.Assert(b1.Next != null, "block {0} has no next blocks array", b1); // and they should all be disjoint. for (int j = i + 1; j < reachable.Count; ++j) { BasicBlock b2 = reachable[j]; DBC.Assert(b1.First.Index < b2.First.Index || b1.First.Index > b2.Last.Index, "block {0} intersects block {1}", b1, b2); DBC.Assert(b1.Last.Index < b2.First.Index || b1.Last.Index > b2.Last.Index, "block {0} intersects block {1}", b1, b2); } } // And the total number of instructions in them should equal the number of // instructions in instructions plus any dead blocks. deadBlocks = DoGetDeadBlocks(instructions, blocks, reachable); int deadCount = 0; foreach (BasicBlock b in deadBlocks) { deadCount += b.Last.Index - b.First.Index + 1; Log.TraceLine(this, "Block {0} is dead code", b); } DBC.Assert(sum + deadCount == instructions.Length, "blocks cover {0} instructions, but there are {1} instructions", sum, instructions.Length); } catch (AssertException e) { Log.ErrorLine(this, "Assert: {0}", e.Message); if (reachable != null && deadBlocks != null) Log.ErrorLine(this, "Missed: {0}", DoGetMissedOffsets(instructions, reachable, deadBlocks)); Log.ErrorLine(this, "Instructions:"); Log.ErrorLine(this, "{0:F}", instructions); Log.ErrorLine(this, "Blocks are:"); foreach (BasicBlock b in blocks.Values) { Log.ErrorLine(this, "{0}", b); Log.ErrorLine(this); } throw; } }
public void DoWhile() { TypeDefinition type = DoGetType("Cases"); MethodDefinition method = type.Methods.GetMethod("DoWhile")[0]; var instructions = new TypedInstructionCollection(new SymbolTable(), method); var tracker = new V0.Tracker(instructions); int index = DoGetIndex(instructions, "first"); Assert.IsFalse(tracker.State(index).HasValue); index = DoGetIndex(instructions, "second"); Assert.AreEqual(2, tracker.State(index).Value); index = DoGetIndex(instructions, "last"); Assert.IsFalse(tracker.State(index).HasValue); }
internal MethodInfo(TypeDefinition type, MethodDefinition method) { Type = type; Method = method; Instructions = new TypedInstructionCollection(new SymbolTable(), Method); }
public static readonly int LastNonNullLocalBlockLen = -4999; // A_999 #region Private Methods ------------------------------------------- // Catch and finally blocks require special handling to reset their stacks // on entry. We can't really do this in meet without breaking commutativity // and transform is too late. So what we do is splice in a special zero // length block right before the handler and when it is transformed reset // the stack. private void DoSpliceHandlers(TypedInstructionCollection instructions, BasicBlock block, List<BasicBlock> visited) { if (visited.IndexOf(block) < 0) { visited.Add(block); for (int i = 0; i < block.Next.Length; ++i) { BasicBlock next = block.Next[i]; if (next.Length > 0) { TryCatch tc = instructions.TryCatchCollection.HandlerStartsAt(next.First.Index); if (tc != null && tc.Try.Index != next.First.Index) { BasicBlock splice = null; if (tc.Finally.Length > 0 && tc.Finally.Index == next.First.Index) splice = new BasicBlock(FinallyBlockLen, new BasicBlock[1]); else if (tc.Fault.Length > 0 && tc.Fault.Index == next.First.Index) splice = new BasicBlock(FinallyBlockLen, new BasicBlock[1]); else splice = new BasicBlock(CatchBlockLen, new BasicBlock[1]); splice.Next[0] = next; block.Next[i] = splice; } } DoSpliceHandlers(instructions, next, visited); } if (block.Finally != null) DoSpliceHandlers(instructions, block.Finally, visited); else if (block.Fault != null) DoSpliceHandlers(instructions, block.Fault, visited); } }
private static string DoGetMissedOffsets(TypedInstructionCollection instructions, List<BasicBlock> reachable, List<BasicBlock> deadBlocks) { // Get a list of all the offsets in the method. List<int> offsets = new List<int>(instructions.Length); foreach (TypedInstruction instruction in instructions) offsets.Add(instruction.Untyped.Offset); // Remove the visited instructions. foreach (BasicBlock block in reachable) DoRemoveOffsets(instructions, offsets, block); // Remove the dead code. foreach (BasicBlock block in deadBlocks) DoRemoveOffsets(instructions, offsets, block); // Anything left is code we should have visited but didn't. return ListExtensions.Accumulate(offsets, string.Empty, (s, e) => s + e.ToString("X2") + " "); }
private void DoInit(string methodName) { string loc = Assembly.GetExecutingAssembly().Location; AssemblyDefinition assembly = AssemblyFactory.GetAssembly(loc); string fullName = string.Format("{0}/{1}", GetType().FullName, "Cases"); TypeDefinition type = assembly.MainModule.Types[fullName]; MethodDefinition method = type.Methods.GetMethod(methodName)[0]; m_instructions = new TypedInstructionCollection(new SymbolTable(), method); m_tracker = new Tracker(m_instructions); m_tracker.Analyze(); }
private static void DoRemoveOffsets(TypedInstructionCollection instructions, List<int> offsets, BasicBlock block) { if (block.Length > 0) { for (int i = block.First.Index; i <= block.Last.Index; ++i) { Unused.Value = offsets.Remove(instructions[i].Untyped.Offset); } } }
public void ZNestedFinally() { TypeDefinition type = DoGetType("Cases"); MethodDefinition method = type.Methods.GetMethod("NestedFinally")[0]; TypedInstructionCollection instructions = new TypedInstructionCollection(new SymbolTable(), method); Log.DebugLine((ControlFlowGraph) null, "-----------------------------------"); Log.DebugLine((ControlFlowGraph) null, "{0:F}", instructions); ControlFlowGraph graph = new ControlFlowGraph(instructions); Assert.AreEqual(2, graph.Roots.Length); }
private List<BasicBlock> DoGetDeadBlocks(TypedInstructionCollection instructions, Dictionary<int, BasicBlock> blocks, List<BasicBlock> reachable) { List<BasicBlock> deadBlocks = new List<BasicBlock>(); // If the block isn't the target of a branch and it's preceded by an end // instruction it's dead code. This can happen if a catch block throws. foreach (KeyValuePair<int, BasicBlock> entry in blocks) { BasicBlock b = entry.Value; int i = b.First.Index; if (m_branchTargets.IndexOf(i) < 0) { if (instructions.TryCatchCollection.HandlerStartsAt(i) == null) if (i > 0 && (instructions[i - 1] is End || instructions[i - 1] is UnconditionalBranch)) deadBlocks.Add(b); } } // If a leave is dead code then its target is dead as well. List<BasicBlock> deaderBlocks = new List<BasicBlock>(); foreach (BasicBlock b in deadBlocks) { if (b.Length > 0) { for (int i = b.First.Index; i <= b.Last.Index; ++i) { if (instructions[i].Untyped.OpCode.Code == Code.Leave || instructions[i].Untyped.OpCode.Code == Code.Leave_S) { UnconditionalBranch branch = (UnconditionalBranch) instructions[i]; if (reachable.IndexOf(blocks[branch.Target.Index]) < 0 && deaderBlocks.IndexOf(blocks[branch.Target.Index]) < 0) deaderBlocks.Add(blocks[branch.Target.Index]); } } } } deadBlocks.AddRange(deaderBlocks); return deadBlocks; }
private void DoWireBlock(TypedInstructionCollection instructions, Dictionary<int, BasicBlock> blocks, BasicBlock block) { var nextBlocks = new List<BasicBlock>(); if (block.First.Index == 0) DoWireCatches(instructions, nextBlocks, blocks, 0); do { ConditionalBranch conditional = block.Last as ConditionalBranch; if (conditional != null) { nextBlocks.Add(blocks[conditional.Index + 1]); nextBlocks.Add(blocks[conditional.Target.Index]); DoWireCatches(instructions, nextBlocks, blocks, conditional.Index + 1); DoWireCatches(instructions, nextBlocks, blocks, conditional.Target.Index); break; } UnconditionalBranch unconditional = block.Last as UnconditionalBranch; if (unconditional != null) { nextBlocks.Add(blocks[unconditional.Target.Index]); DoWireCatches(instructions, nextBlocks, blocks, unconditional.Target.Index); if (block.Last.Untyped.OpCode.Code == Code.Leave || block.Last.Untyped.OpCode.Code == Code.Leave_S) DoAddFinally(instructions, nextBlocks, blocks, block); break; } Switch swtch = block.Last as Switch; if (swtch != null) { foreach (TypedInstruction t in swtch.Targets) { nextBlocks.Add(blocks[t.Index]); DoWireCatches(instructions, nextBlocks, blocks, t.Index); } nextBlocks.Add(blocks[swtch.Index + 1]); DoWireCatches(instructions, nextBlocks, blocks, swtch.Index + 1); break; } End end = block.Last as End; if (end != null) { DoAddFinally(instructions, nextBlocks, blocks, block); break; } DoWireCatches(instructions, nextBlocks, blocks, block.Last.Index + 1); nextBlocks.Add(blocks[block.Last.Index + 1]); } while (false); block.Next = nextBlocks.ToArray(); Log.DebugLine(this, "Wired block: {0}", block); }
private static void DoTrySpliceNull(TypedInstructionCollection instructions, BasicBlock block) { TypedInstruction instruction = instructions[block.Last.Index]; Code code = instruction.Untyped.OpCode.Code; if (code == Code.Brtrue || code == Code.Brtrue_S || code == Code.Brfalse || code == Code.Brfalse_S) { DoSpliceTrue(instructions, block); } else if (code == Code.Beq || code == Code.Beq_S || code == Code.Bne_Un || code == Code.Bne_Un_S) { DoSpliceEq(instructions, block); } }