From b758c392b22a32d0f39e25d440c7bd2bc94352e1 Mon Sep 17 00:00:00 2001 From: gompoc <91314780+gompoc@users.noreply.github.com> Date: Mon, 26 Aug 2024 11:00:29 +0100 Subject: [PATCH 1/5] Core.Graphs: Abstract out CFG --- Cpp2IL.Core.Tests/Graphing/BasicGraph.cs | 4 +- .../Graphing/ExceptionThrowingGraph.cs | 3 +- Cpp2IL.Core/AST/Expression.cs | 5 + Cpp2IL.Core/Extensions/BlockExtensions.cs | 44 +++ Cpp2IL.Core/Graphs/ASTControlFlowGraph.cs | 51 ++++ Cpp2IL.Core/Graphs/Block.cs | 59 +--- Cpp2IL.Core/Graphs/ControlFlowGraph.cs | 86 ++++++ Cpp2IL.Core/Graphs/DominatorInfo.cs | 253 ++++++++++++++++++ Cpp2IL.Core/Graphs/ISILControlFlowGraph.cs | 169 +++--------- .../Graphs/Processors/CallProcessor.cs | 4 +- .../Graphs/Processors/IBlockProcessor.cs | 3 +- .../Graphs/Processors/MetadataProcessor.cs | 5 +- .../Model/Contexts/MethodAnalysisContext.cs | 4 +- 13 files changed, 502 insertions(+), 188 deletions(-) create mode 100644 Cpp2IL.Core/AST/Expression.cs create mode 100644 Cpp2IL.Core/Extensions/BlockExtensions.cs create mode 100644 Cpp2IL.Core/Graphs/ASTControlFlowGraph.cs create mode 100644 Cpp2IL.Core/Graphs/ControlFlowGraph.cs create mode 100644 Cpp2IL.Core/Graphs/DominatorInfo.cs diff --git a/Cpp2IL.Core.Tests/Graphing/BasicGraph.cs b/Cpp2IL.Core.Tests/Graphing/BasicGraph.cs index 5a7865d67..d93fed854 100644 --- a/Cpp2IL.Core.Tests/Graphing/BasicGraph.cs +++ b/Cpp2IL.Core.Tests/Graphing/BasicGraph.cs @@ -31,8 +31,8 @@ public void Setup() isilBuilder.FixJumps(); - graph = new(); - graph.Build(isilBuilder.BackingStatementList); + + graph = ISILControlFlowGraph.Build(isilBuilder.BackingStatementList); } [Test] diff --git a/Cpp2IL.Core.Tests/Graphing/ExceptionThrowingGraph.cs b/Cpp2IL.Core.Tests/Graphing/ExceptionThrowingGraph.cs index caaddd9e9..918038ef0 100644 --- a/Cpp2IL.Core.Tests/Graphing/ExceptionThrowingGraph.cs +++ b/Cpp2IL.Core.Tests/Graphing/ExceptionThrowingGraph.cs @@ -65,8 +65,7 @@ public void Setup() isilBuilder.FixJumps(); - graph = new(); - graph.Build(isilBuilder.BackingStatementList); + graph = ISILControlFlowGraph.Build(isilBuilder.BackingStatementList); } [Test] diff --git a/Cpp2IL.Core/AST/Expression.cs b/Cpp2IL.Core/AST/Expression.cs new file mode 100644 index 000000000..62a1f34c5 --- /dev/null +++ b/Cpp2IL.Core/AST/Expression.cs @@ -0,0 +1,5 @@ +namespace Cpp2IL.Core.AST; + +public class Expression +{ +} diff --git a/Cpp2IL.Core/Extensions/BlockExtensions.cs b/Cpp2IL.Core/Extensions/BlockExtensions.cs new file mode 100644 index 000000000..69318f62a --- /dev/null +++ b/Cpp2IL.Core/Extensions/BlockExtensions.cs @@ -0,0 +1,44 @@ +using Cpp2IL.Core.Graphs; +using Cpp2IL.Core.ISIL; +using System.Linq; + +namespace Cpp2IL.Core.Extensions; + +internal static class BlockExtensions +{ + public static void CaculateBlockType(this Block block) + { + // This enum is kind of redundant, can be possibly swapped for IsilFlowControl and no need for BlockType? + if (block.Instructions.Count > 0) + { + var instruction = block.Instructions.Last(); + switch (instruction.FlowControl) + { + case IsilFlowControl.UnconditionalJump: + block.BlockType = BlockType.OneWay; + break; + case IsilFlowControl.ConditionalJump: + block.BlockType = BlockType.TwoWay; + break; + case IsilFlowControl.IndexedJump: + block.BlockType = BlockType.NWay; + break; + case IsilFlowControl.MethodCall: + block.BlockType = BlockType.Call; + break; + case IsilFlowControl.MethodReturn: + block.BlockType = BlockType.Return; + break; + case IsilFlowControl.Interrupt: + block.BlockType = BlockType.Interrupt; + break; + case IsilFlowControl.Continue: + block.BlockType = BlockType.Fall; + break; + default: + block.BlockType = BlockType.Unknown; + break; + } + } + } +} diff --git a/Cpp2IL.Core/Graphs/ASTControlFlowGraph.cs b/Cpp2IL.Core/Graphs/ASTControlFlowGraph.cs new file mode 100644 index 000000000..b4a19330f --- /dev/null +++ b/Cpp2IL.Core/Graphs/ASTControlFlowGraph.cs @@ -0,0 +1,51 @@ +using System.Linq; +using System.Collections.Generic; +using Cpp2IL.Core.AST; +using Cpp2IL.Core.ISIL; + +namespace Cpp2IL.Core.Graphs; + +public sealed class ASTControlFlowGraph : ControlFlowGraph +{ + private ASTControlFlowGraph() + { + } + + public static ASTControlFlowGraph From(ISILControlFlowGraph graph) + { + var astGraph = new ASTControlFlowGraph(); + var map = new Dictionary, Block>(); + foreach (var block in graph.Blocks) + { + map[block] = ConvertBlock(block); + } + foreach (var block in graph.Blocks) + { + map[block].Predecessors.AddRange(block.Predecessors.Select(predecessor => map[predecessor])); + map[block].Successors.AddRange(block.Successors.Select(successor => map[successor])); + } + return astGraph; + } + + + private static Block ConvertBlock(Block block) + { + var newBlock = new Block + { + BlockType = block.BlockType + }; + foreach (var instruction in block.Instructions) + { + var expression = ConvertInstruction(instruction); + if (expression != null) + newBlock.AddInstruction(expression); + } + return newBlock; + } + + private static Expression ConvertInstruction(InstructionSetIndependentInstruction instruction) + { + return new Expression(); + // throw new NotImplementedException(); + } +} diff --git a/Cpp2IL.Core/Graphs/Block.cs b/Cpp2IL.Core/Graphs/Block.cs index 0bfc7dfa4..8cf566a35 100644 --- a/Cpp2IL.Core/Graphs/Block.cs +++ b/Cpp2IL.Core/Graphs/Block.cs @@ -1,75 +1,32 @@ -using System.Collections.Generic; -using System.Linq; using System.Text; -using Cpp2IL.Core.ISIL; +using System.Collections.Generic; namespace Cpp2IL.Core.Graphs; -public class Block +public class Block where Instruction : notnull { public BlockType BlockType { get; set; } = BlockType.Unknown; - public List Predecessors = []; - public List Successors = []; - - public List isilInstructions = []; + public List> Predecessors = []; + public List> Successors = []; - public int ID { get; set; } = -1; + public List Instructions = []; public bool Dirty { get; set; } - public bool Visited = false; - public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Type: " + BlockType); stringBuilder.AppendLine(); - foreach (var instruction in isilInstructions) + foreach (var instruction in Instructions) { stringBuilder.AppendLine(instruction.ToString()); } - return stringBuilder.ToString(); } - public void AddInstruction(InstructionSetIndependentInstruction instruction) + public void AddInstruction(Instruction instruction) { - isilInstructions.Add(instruction); - } - - public void CaculateBlockType() - { - // This enum is kind of redundant, can be possibly swapped for IsilFlowControl and no need for BlockType? - if (isilInstructions.Count > 0) - { - var instruction = isilInstructions.Last(); - switch (instruction.FlowControl) - { - case IsilFlowControl.UnconditionalJump: - BlockType = BlockType.OneWay; - break; - case IsilFlowControl.ConditionalJump: - BlockType = BlockType.TwoWay; - break; - case IsilFlowControl.IndexedJump: - BlockType = BlockType.NWay; - break; - case IsilFlowControl.MethodCall: - BlockType = BlockType.Call; - break; - case IsilFlowControl.MethodReturn: - BlockType = BlockType.Return; - break; - case IsilFlowControl.Interrupt: - BlockType = BlockType.Interrupt; - break; - case IsilFlowControl.Continue: - BlockType = BlockType.Fall; - break; - default: - BlockType = BlockType.Unknown; - break; - } - } + Instructions.Add(instruction); } } diff --git a/Cpp2IL.Core/Graphs/ControlFlowGraph.cs b/Cpp2IL.Core/Graphs/ControlFlowGraph.cs new file mode 100644 index 000000000..82bbdc438 --- /dev/null +++ b/Cpp2IL.Core/Graphs/ControlFlowGraph.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.ObjectModel; + +namespace Cpp2IL.Core.Graphs; + +public abstract class ControlFlowGraph where Instruction : notnull +{ + public Collection> Blocks => blockSet; + public Block EntryBlock => entryBlock; + public Block ExitBlock => exitBlock; + + private Block exitBlock; + private Block entryBlock; + + private Collection> blockSet; + + public int Count => blockSet.Count; + + public ControlFlowGraph() + { + entryBlock = new Block() { BlockType = BlockType.Entry }; + exitBlock = new Block() { BlockType = BlockType.Exit }; + blockSet = + [ + entryBlock, + exitBlock + ]; + } + + public void AddDirectedEdge(Block from, Block to) + { + from.Successors.Add(to); + to.Predecessors.Add(from); + } + + public void AddNode(Block block) => blockSet.Add(block); + + + public Block SplitAndCreate(Block target, int index) + { + if (index < 0 || index >= target.Instructions.Count) + throw new ArgumentOutOfRangeException(nameof(index)); + + // Don't need to split... + if (index == 0) + return target; + + var newNode = new Block(); + + // target split in two + // targetFirstPart -> targetSecondPart aka newNode + + // Take the instructions for the secondPart + var instructions = target.Instructions.GetRange(index, target.Instructions.Count - index); + target.Instructions.RemoveRange(index, target.Instructions.Count - index); + + // Add those to the newNode + newNode.Instructions.AddRange(instructions); + // Transfer control flow + newNode.BlockType = target.BlockType; + target.BlockType = BlockType.Fall; + + // Transfer successors + newNode.Successors = target.Successors; + if (target.Dirty) + newNode.Dirty = true; + target.Dirty = false; + target.Successors = []; + + // Correct the predecessors for all the successors + foreach (var successor in newNode.Successors) + { + for (int i = 0; i < successor.Predecessors.Count; i++) + { + if (successor.Predecessors[i] == target) + successor.Predecessors[i] = newNode; + } + } + + // Add newNode and connect it + AddNode(newNode); + AddDirectedEdge(target, newNode); + + return newNode; + } +} diff --git a/Cpp2IL.Core/Graphs/DominatorInfo.cs b/Cpp2IL.Core/Graphs/DominatorInfo.cs new file mode 100644 index 000000000..190a58abc --- /dev/null +++ b/Cpp2IL.Core/Graphs/DominatorInfo.cs @@ -0,0 +1,253 @@ +using System.Collections.Generic; + +namespace Cpp2IL.Core.Graphs; + +public sealed class DominatorInfo where T : notnull +{ + private Dictionary, HashSet>> domFrontier = new(); + private Dictionary, Block?> idom = new(); + private Dictionary, Block?> iPostDom = new(); + private Dictionary, HashSet>> pDominators = new(); + private Dictionary, HashSet>> dominators = new(); + private DominatorInfo() + { + } + + public static DominatorInfo From(ControlFlowGraph graph) + { + var dominatorInfo = new DominatorInfo(); + + dominatorInfo.CalculateDominators(graph); + dominatorInfo.CalculatePostDominators(graph); + dominatorInfo.CalculateImmediateDominators(graph); + dominatorInfo.CalculateImmediatePostDominators(graph); + dominatorInfo.CalculateDominanceFrontiers(graph); + + return dominatorInfo; + } + + public bool Dominates(Block a, Block b) + { + if (a == b) + return true; + if (dominators.ContainsKey(b) && dominators.ContainsKey(a)) + return dominators[b].Contains(a); + return false; + } + + // TODO: Implement api & tests + + + private void CalculateDominanceFrontiers(ControlFlowGraph graph) + { + // The dominance frontier of a basic block N is the set of all blocks that are + // immediate successors to blocks dominated by N, but which aren’t themselves + // strictly dominated by N. In other words, it represents the blocks that + // are “first reached” on paths from N. + domFrontier.Clear(); + foreach (var block in graph.Blocks) + { + domFrontier[block] = new(); + } + + foreach (var block in graph.Blocks) + { + if (block.Predecessors.Count >= 2) + { + foreach (var predecessor in block.Predecessors) + { + var runner = predecessor; + while (runner != idom[block] && runner != null) + { + domFrontier[runner].Add(block); + runner = idom[runner]; + } + } + } + } + } + + private void CalculateImmediatePostDominators(ControlFlowGraph graph) + { + foreach (var block in graph.Blocks) + { + // TODO: Technically the exit block should be the only block with no successors + // Requires switch & try/catch blocks to be properly handled + if (block.Successors.Count == 0 || block.BlockType == BlockType.Exit) + { + iPostDom[block] = null; + continue; + } + + foreach (var candidate in pDominators[block]) + { + if (candidate == block) + continue; + + if (pDominators[block].Count == 2) + { + iPostDom[block] = candidate; + break; + } + + foreach (var otherCandiate in pDominators[block]) + { + if (candidate == otherCandiate || candidate == block) + continue; + + if (!pDominators[otherCandiate].Contains(candidate)) + { + iPostDom[block] = candidate; + break; + } + } + } + } + } + + private void CalculateImmediateDominators(ControlFlowGraph graph) + { + foreach (var block in graph.Blocks) + { + // TODO: Technically the exit block should be the only block with no successors + // Requires switch & try/catch blocks to be properly handled + if (block.Predecessors.Count == 0 || block.BlockType == BlockType.Entry) + { + idom[block] = null; + continue; + } + + // The idom of a node n is the unique node in Dom(n) that strictly dominates n + // but does not strictly dominate any other node that strictly dominates n + foreach (var candidate in dominators[block]) + { + if (candidate == block) + continue; + + if (dominators[block].Count == 2) + { + idom[block] = candidate; + break; + } + + foreach (var otherCandiate in dominators[block]) + { + if (candidate == otherCandiate || candidate == block) + continue; + + if (!dominators[otherCandiate].Contains(candidate)) + { + idom[block] = candidate; + break; + } + } + } + } + } + + private void CalculatePostDominators(ControlFlowGraph graph) + { + pDominators.Clear(); + foreach (var block in graph.Blocks) + { + + if (block.BlockType == BlockType.Exit) + { + pDominators[block] = new(); + pDominators[block].Add(block); + } + else + { + pDominators[block] = new HashSet>(graph.Blocks); + } + } + + + bool changed = true; + + while (changed) + { + changed = false; + + foreach (var block in graph.Blocks) + { + if (block.BlockType == BlockType.Exit) + continue; + + + // if (block.Successors.Count == 0) + // { + // return; + // } + + var tmpPDominators = block.Successors.Count == 0 ? new HashSet>() : new HashSet>(pDominators[block.Successors[0]]); + for (int i = 1; i < block.Successors.Count; i++) + { + tmpPDominators.IntersectWith(pDominators[block.Successors[i]]); + } + tmpPDominators.Add(block); + + if (!tmpPDominators.SetEquals(pDominators[block])) + { + pDominators[block] = tmpPDominators; + changed = true; + } + } + } + } + + private void CalculateDominators(ControlFlowGraph graph) + { + dominators.Clear(); + foreach (var block in graph.Blocks) + { + if (block.BlockType == BlockType.Entry) + { + dominators[block] = new(); + dominators[block].Add(block); + } + else + { + dominators[block] = new HashSet>(graph.Blocks); + } + } + + + bool changed = true; + + while (changed) + { + changed = false; + + foreach (var block in graph.Blocks) + { + if (block.BlockType == BlockType.Entry) + continue; + + + // In a perfect world the entry block should be the only block with no predecessors + // Our world isn't perfect thanks to the existance to jump tables and try catch with + // the catch block being only reachable via exception handler magic + // We could bail out here but we could also just continue anyway + // See: UnityEngine.AndroidJNISafe and look at cfg for any of the CallxxxxMethod methods + // if (block.Predecessors.Count == 0) + // { + // return; + // } + + var tmpDominators = block.Predecessors.Count == 0 ? new HashSet>() : new HashSet>(dominators[block.Predecessors[0]]); + for (int i = 1; i < block.Predecessors.Count; i++) + { + tmpDominators.IntersectWith(dominators[block.Predecessors[i]]); + } + tmpDominators.Add(block); + + if (!tmpDominators.SetEquals(dominators[block])) + { + dominators[block] = tmpDominators; + changed = true; + } + } + } + } +} diff --git a/Cpp2IL.Core/Graphs/ISILControlFlowGraph.cs b/Cpp2IL.Core/Graphs/ISILControlFlowGraph.cs index c4ac34a25..499e2c1f8 100644 --- a/Cpp2IL.Core/Graphs/ISILControlFlowGraph.cs +++ b/Cpp2IL.Core/Graphs/ISILControlFlowGraph.cs @@ -1,36 +1,15 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; -using System.Text; using Cpp2IL.Core.ISIL; +using Cpp2IL.Core.Extensions; namespace Cpp2IL.Core.Graphs; -public class ISILControlFlowGraph +public sealed class ISILControlFlowGraph : ControlFlowGraph { - public Block EntryBlock => entryBlock; - public Block ExitBlock => exitBlock; - public int Count => blockSet.Count; - public Collection Blocks => blockSet; - - - private int idCounter; - private Collection blockSet; - private Block exitBlock; - private Block entryBlock; - - public ISILControlFlowGraph() + private ISILControlFlowGraph() { - entryBlock = new Block() { ID = idCounter++ }; - entryBlock.BlockType = BlockType.Entry; - exitBlock = new Block() { ID = idCounter++ }; - exitBlock.BlockType = BlockType.Exit; - blockSet = - [ - entryBlock, - exitBlock - ]; } private bool TryGetTargetJumpInstructionIndex(InstructionSetIndependentInstruction instruction, out uint jumpInstructionIndex) @@ -50,15 +29,16 @@ private bool TryGetTargetJumpInstructionIndex(InstructionSetIndependentInstructi } - public void Build(List instructions) + public static ISILControlFlowGraph Build(List instructions) { if (instructions == null) throw new ArgumentNullException(nameof(instructions)); - var currentBlock = new Block() { ID = idCounter++ }; - AddNode(currentBlock); - AddDirectedEdge(entryBlock, currentBlock); + var graph = new ISILControlFlowGraph(); + var currentBlock = new Block(); + graph.AddNode(currentBlock); + graph.AddDirectedEdge(graph.EntryBlock, currentBlock); for (var i = 0; i < instructions.Count; i++) { var isLast = i == instructions.Count - 1; @@ -68,16 +48,16 @@ public void Build(List instructions) currentBlock.AddInstruction(instructions[i]); if (!isLast) { - var newNodeFromJmp = new Block() { ID = idCounter++ }; - AddNode(newNodeFromJmp); - if (TryGetTargetJumpInstructionIndex(instructions[i], out uint jumpTargetIndex)) + var newNodeFromJmp = new Block(); + graph.AddNode(newNodeFromJmp); + if (graph.TryGetTargetJumpInstructionIndex(instructions[i], out uint jumpTargetIndex)) { // var result = instructions.Any(instruction => instruction.InstructionIndex == jumpTargetIndex); currentBlock.Dirty = true; } else { - AddDirectedEdge(currentBlock, exitBlock); + graph.AddDirectedEdge(currentBlock, graph.ExitBlock); } currentBlock.CaculateBlockType(); @@ -85,7 +65,7 @@ public void Build(List instructions) } else { - AddDirectedEdge(currentBlock, exitBlock); + graph.AddDirectedEdge(currentBlock, graph.ExitBlock); currentBlock.Dirty = true; } @@ -94,15 +74,15 @@ public void Build(List instructions) currentBlock.AddInstruction(instructions[i]); if (!isLast) { - var newNodeFromCall = new Block() { ID = idCounter++ }; - AddNode(newNodeFromCall); - AddDirectedEdge(currentBlock, newNodeFromCall); + var newNodeFromCall = new Block(); + graph.AddNode(newNodeFromCall); + graph.AddDirectedEdge(currentBlock, newNodeFromCall); currentBlock.CaculateBlockType(); currentBlock = newNodeFromCall; } else { - AddDirectedEdge(currentBlock, exitBlock); + graph.AddDirectedEdge(currentBlock, graph.ExitBlock); currentBlock.CaculateBlockType(); } @@ -111,7 +91,7 @@ public void Build(List instructions) currentBlock.AddInstruction(instructions[i]); if (isLast) { - // TODO: Investiage + // TODO: Investigate /* This shouldn't happen, we've either smashed into another method or random data such as a jump table */ } @@ -120,15 +100,15 @@ public void Build(List instructions) currentBlock.AddInstruction(instructions[i]); if (!isLast) { - var newNodeFromReturn = new Block() { ID = idCounter++ }; - AddNode(newNodeFromReturn); - AddDirectedEdge(currentBlock, exitBlock); + var newNodeFromReturn = new Block(); + graph.AddNode(newNodeFromReturn); + graph.AddDirectedEdge(currentBlock, graph.ExitBlock); currentBlock.CaculateBlockType(); currentBlock = newNodeFromReturn; } else { - AddDirectedEdge(currentBlock, exitBlock); + graph.AddDirectedEdge(currentBlock, graph.ExitBlock); currentBlock.CaculateBlockType(); } @@ -137,24 +117,24 @@ public void Build(List instructions) currentBlock.AddInstruction(instructions[i]); if (!isLast) { - var newNodeFromConditionalBranch = new Block() { ID = idCounter++ }; - AddNode(newNodeFromConditionalBranch); - AddDirectedEdge(currentBlock, newNodeFromConditionalBranch); + var newNodeFromConditionalBranch = new Block(); + graph.AddNode(newNodeFromConditionalBranch); + graph.AddDirectedEdge(currentBlock, newNodeFromConditionalBranch); currentBlock.CaculateBlockType(); currentBlock.Dirty = true; currentBlock = newNodeFromConditionalBranch; } else { - AddDirectedEdge(currentBlock, exitBlock); + graph.AddDirectedEdge(currentBlock, graph.ExitBlock); } break; case IsilFlowControl.Interrupt: currentBlock.AddInstruction(instructions[i]); - var newNodeFromInterrupt = new Block() { ID = idCounter++ }; - AddNode(newNodeFromInterrupt); - AddDirectedEdge(currentBlock, exitBlock); + var newNodeFromInterrupt = new Block(); + graph.AddNode(newNodeFromInterrupt); + graph.AddDirectedEdge(currentBlock, graph.ExitBlock); currentBlock.CaculateBlockType(); currentBlock = newNodeFromInterrupt; break; @@ -167,28 +147,22 @@ public void Build(List instructions) } - for (var index = 0; index < blockSet.Count; index++) + for (var index = 0; index < graph.Blocks.Count; index++) { - var node = blockSet[index]; + var node = graph.Blocks[index]; if (node.Dirty) - FixBlock(node); + graph.FixBlock(node); } - } - public void CalculateDominations() - { - foreach (var block in blockSet) - { - throw new NotImplementedException(); - } + return graph; } - private void FixBlock(Block block, bool removeJmp = false) + private void FixBlock(Block block, bool removeJmp = false) { if (block.BlockType is BlockType.Fall) return; - var jump = block.isilInstructions.Last(); + var jump = block.Instructions.Last(); var targetInstruction = jump.Operands[0].Data as InstructionSetIndependentInstruction; @@ -196,13 +170,12 @@ private void FixBlock(Block block, bool removeJmp = false) if (destination == null) { - //We assume that we're tail calling another method somewhere. Need to verify if this breaks anywhere but it shouldn't in general + // We assume that we're tail calling another method somewhere. Need to verify if this breaks anywhere but it shouldn't in general block.BlockType = BlockType.Call; return; } - - int index = destination.isilInstructions.FindIndex(instruction => instruction == targetInstruction); + int index = destination.Instructions.FindIndex(instruction => instruction == targetInstruction); var targetNode = SplitAndCreate(destination, index); @@ -210,20 +183,20 @@ private void FixBlock(Block block, bool removeJmp = false) block.Dirty = false; if (removeJmp) - block.isilInstructions.Remove(jump); + block.Instructions.Remove(jump); } - protected Block? FindNodeByInstruction(InstructionSetIndependentInstruction? instruction) + private Block? FindNodeByInstruction(InstructionSetIndependentInstruction? instruction) { if (instruction == null) return null; - for (var i = 0; i < blockSet.Count; i++) + for (var i = 0; i < Blocks.Count; i++) { - var block = blockSet[i]; - for (var j = 0; j < block.isilInstructions.Count; j++) + var block = Blocks[i]; + for (var j = 0; j < block.Instructions.Count; j++) { - var instr = block.isilInstructions[j]; + var instr = block.Instructions[j]; if (instr == instruction) { return block; @@ -233,60 +206,4 @@ private void FixBlock(Block block, bool removeJmp = false) return null; } - - private Block SplitAndCreate(Block target, int index) - { - if (index < 0 || index >= target.isilInstructions.Count) - throw new ArgumentOutOfRangeException(nameof(index)); - - // Don't need to split... - if (index == 0) - return target; - - var newNode = new Block() { ID = idCounter++ }; - - // target split in two - // targetFirstPart -> targetSecondPart aka newNode - - // Take the instructions for the secondPart - var instructions = target.isilInstructions.GetRange(index, target.isilInstructions.Count - index); - target.isilInstructions.RemoveRange(index, target.isilInstructions.Count - index); - - // Add those to the newNode - newNode.isilInstructions.AddRange(instructions); - // Transfer control flow - newNode.BlockType = target.BlockType; - target.BlockType = BlockType.Fall; - - // Transfer successors - newNode.Successors = target.Successors; - if (target.Dirty) - newNode.Dirty = true; - target.Dirty = false; - target.Successors = []; - - // Correct the predecessors for all the successors - foreach (var successor in newNode.Successors) - { - for (int i = 0; i < successor.Predecessors.Count; i++) - { - if (successor.Predecessors[i].ID == target.ID) - successor.Predecessors[i] = newNode; - } - } - - // Add newNode and connect it - AddNode(newNode); - AddDirectedEdge(target, newNode); - - return newNode; - } - - private void AddDirectedEdge(Block from, Block to) - { - from.Successors.Add(to); - to.Predecessors.Add(from); - } - - protected void AddNode(Block block) => blockSet.Add(block); } diff --git a/Cpp2IL.Core/Graphs/Processors/CallProcessor.cs b/Cpp2IL.Core/Graphs/Processors/CallProcessor.cs index a3f3784f5..1ee04b8ec 100644 --- a/Cpp2IL.Core/Graphs/Processors/CallProcessor.cs +++ b/Cpp2IL.Core/Graphs/Processors/CallProcessor.cs @@ -7,11 +7,11 @@ namespace Cpp2IL.Core.Graphs.Processors; internal class CallProcessor : IBlockProcessor { - public void Process(MethodAnalysisContext methodAnalysisContext, Block block) + public void Process(MethodAnalysisContext methodAnalysisContext, Block block) { if (block.BlockType != BlockType.Call) return; - var callInstruction = block.isilInstructions[^1]; + var callInstruction = block.Instructions[^1]; if (callInstruction == null) return; if (callInstruction.OpCode != InstructionSetIndependentOpCode.Call) diff --git a/Cpp2IL.Core/Graphs/Processors/IBlockProcessor.cs b/Cpp2IL.Core/Graphs/Processors/IBlockProcessor.cs index ce46d76da..0c1c4abbb 100644 --- a/Cpp2IL.Core/Graphs/Processors/IBlockProcessor.cs +++ b/Cpp2IL.Core/Graphs/Processors/IBlockProcessor.cs @@ -1,8 +1,9 @@ +using Cpp2IL.Core.ISIL; using Cpp2IL.Core.Model.Contexts; namespace Cpp2IL.Core.Graphs.Processors; internal interface IBlockProcessor { - public void Process(MethodAnalysisContext methodAnalysisContext, Block block); + public void Process(MethodAnalysisContext methodAnalysisContext, Block block); } diff --git a/Cpp2IL.Core/Graphs/Processors/MetadataProcessor.cs b/Cpp2IL.Core/Graphs/Processors/MetadataProcessor.cs index 19ad5bdbb..505c92bae 100644 --- a/Cpp2IL.Core/Graphs/Processors/MetadataProcessor.cs +++ b/Cpp2IL.Core/Graphs/Processors/MetadataProcessor.cs @@ -8,9 +8,9 @@ namespace Cpp2IL.Core.Graphs.Processors; internal class MetadataProcessor : IBlockProcessor { - public void Process(MethodAnalysisContext methodAnalysisContext, Block block) + public void Process(MethodAnalysisContext methodAnalysisContext, Block block) { - foreach (var instruction in block.isilInstructions) + foreach (var instruction in block.Instructions) { // TODO: Check if it shows up in any other if (instruction.OpCode != InstructionSetIndependentOpCode.Move) @@ -33,6 +33,7 @@ public void Process(MethodAnalysisContext methodAnalysisContext, Block block) var metadataUsage = LibCpp2IlMain.GetTypeGlobalByAddress((ulong)memoryOp.Addend); if (metadataUsage != null && methodAnalysisContext.DeclaringType is not null) { + var typeAnalysisContext = metadataUsage.ToContext(methodAnalysisContext.DeclaringType!.DeclaringAssembly); if (typeAnalysisContext != null) instruction.Operands[1] = InstructionSetIndependentOperand.MakeTypeMetadataUsage(typeAnalysisContext); diff --git a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs index c99c948ab..aaa089148 100644 --- a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs +++ b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs @@ -156,8 +156,8 @@ public void Analyze() if (ConvertedIsil.Count == 0) return; //Nothing to do, empty function - ControlFlowGraph = new ISILControlFlowGraph(); - ControlFlowGraph.Build(ConvertedIsil); + + ControlFlowGraph = ISILControlFlowGraph.Build(ConvertedIsil); // Post step to convert metadata usage. Ldstr Opcodes etc. foreach (var block in ControlFlowGraph.Blocks) From 42c4024baafb83ba98d7e36a357f495b8c50a581 Mon Sep 17 00:00:00 2001 From: gompoc <91314780+gompoc@users.noreply.github.com> Date: Wed, 28 Aug 2024 13:27:08 +0100 Subject: [PATCH 2/5] Core.Graphs: Basic stack checks --- Cpp2IL.Core/Graphs/Analysis/StackAnalyzer.cs | 96 +++++++++++++++++++ .../InstructionSets/NewArmV8InstructionSet.cs | 2 +- .../InstructionSets/X86InstructionSet.cs | 42 ++++---- .../Model/Contexts/MethodAnalysisContext.cs | 7 +- .../ControlFlowGraphOutputFormat.cs | 16 +++- 5 files changed, 134 insertions(+), 29 deletions(-) create mode 100644 Cpp2IL.Core/Graphs/Analysis/StackAnalyzer.cs diff --git a/Cpp2IL.Core/Graphs/Analysis/StackAnalyzer.cs b/Cpp2IL.Core/Graphs/Analysis/StackAnalyzer.cs new file mode 100644 index 000000000..4db963029 --- /dev/null +++ b/Cpp2IL.Core/Graphs/Analysis/StackAnalyzer.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using LibCpp2IL; +using Cpp2IL.Core.ISIL; +using Cpp2IL.Core.Model.Contexts; +using System.Diagnostics; +namespace Cpp2IL.Core.Graphs.Analysis; + +public sealed class StackAnalyzer +{ + public HashSet> visited = []; + public Dictionary, int> inComingDelta = []; + public Dictionary, int> outGoingDelta = []; + + public static int unbalancedStackCount { get; private set; } = 0; + public static int balanacedStackCount { get; private set; } = 0; + + private StackAnalyzer() {} + + public static void Analyze(MethodAnalysisContext context) + { + try + { + var graph = context.ControlFlowGraph; + if (graph == null) + { + return; + } + var analyzer = new StackAnalyzer(); + analyzer.inComingDelta[graph.EntryBlock] = 0; + int archSize = LibCpp2IlMain.Binary!.is32Bit ? 4 : 8; + analyzer.TraverseGraph(graph.EntryBlock, archSize); + var outDelta = analyzer.outGoingDelta[graph.ExitBlock]; + if (outDelta != 0) + { + unbalancedStackCount++; + } + else + { + balanacedStackCount++; + } + } catch (Exception e) + { + unbalancedStackCount++; + } + } + + private void TraverseGraph(Block block, int archSize) + { + var blockDelta = inComingDelta[block]; + + if (block.BlockType == BlockType.Call && block.Successors.Count == 1 && block.Successors[0].BlockType == BlockType.Exit) + { + // Tail call / CallNoReturn + blockDelta = 0; + outGoingDelta[block] = blockDelta; + } else + { + foreach (var instruction in block.Instructions) + { + switch (instruction.OpCode.Mnemonic) + { + case IsilMnemonic.Push: + blockDelta -= archSize; + break; + case IsilMnemonic.Pop: + blockDelta += archSize; + break; + case IsilMnemonic.ShiftStack: + blockDelta += (int)((IsilImmediateOperand)instruction.Operands[0].Data).Value; + break; + } + } + outGoingDelta[block] = blockDelta; + } + + foreach (var succ in block.Successors) + { + if (!visited.Contains(succ)) + { + inComingDelta[succ] = blockDelta; + visited.Add(succ); + TraverseGraph(succ, archSize); + } else + { + var expectedDelta = inComingDelta[succ]; + + if (expectedDelta != blockDelta) + { + throw new Exception("Unbalanced stack"); + } + inComingDelta[succ] = blockDelta; + } + } + } +} diff --git a/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs b/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs index df171aefc..c8319f061 100644 --- a/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs +++ b/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs @@ -176,7 +176,7 @@ private void ConvertInstructionStatement(Arm64Instruction instruction, IsilBuild { //Unconditional branch to outside the method, treat as call (tail-call, specifically) followed by return builder.Call(instruction.Address, instruction.BranchTarget, GetArgumentOperandsForCall(context, instruction.BranchTarget).ToArray()); - builder.Return(instruction.Address, GetReturnRegisterForContext(context)); + //builder.Return(instruction.Address, GetReturnRegisterForContext(context)); } break; diff --git a/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs b/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs index 95cf26d7a..bd82f3f50 100644 --- a/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs +++ b/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs @@ -87,15 +87,15 @@ private void ConvertInstructionStatement(Instruction instruction, IsilBuilder bu builder.Move(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1)); break; case Mnemonic.Cbw: // AX := sign-extend AL - builder.Move(instruction.IP, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.AX)), + builder.Move(instruction.IP, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.AX)), InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.AL))); break; case Mnemonic.Cwde: // EAX := sign-extend AX - builder.Move(instruction.IP, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.EAX)), + builder.Move(instruction.IP, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.EAX)), InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.AX))); break; case Mnemonic.Cdqe: // RAX := sign-extend EAX - builder.Move(instruction.IP, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.RAX)), + builder.Move(instruction.IP, InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.RAX)), InstructionSetIndependentOperand.MakeRegister(X86Utils.GetRegisterName(Register.EAX))); break; // it's very unsafe if there's been a jump to the next instruction here before. @@ -216,14 +216,14 @@ private void ConvertInstructionStatement(Instruction instruction, IsilBuilder bu goto default; break; - + case Mnemonic.Divss: // Divide Scalar Single Precision Floating-Point Values. DEST[31:0] = DEST[31:0] / SRC[31:0] builder.Divide(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 0), ConvertOperand(instruction, 1)); break; case Mnemonic.Vdivss: // VEX Divide Scalar Single Precision Floating-Point Values. DEST[31:0] = SRC1[31:0] / SRC2[31:0] builder.Divide(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1), ConvertOperand(instruction, 2)); break; - + case Mnemonic.Ret: // TODO: Verify correctness of operation with Vectors. @@ -312,15 +312,15 @@ private void ConvertInstructionStatement(Instruction instruction, IsilBuilder bu { if (instruction.Op1Kind == OpKind.Memory) goto default; - + var imm = instruction.Immediate8; var src1 = X86Utils.GetRegisterName(instruction.Op0Register); var src2 = X86Utils.GetRegisterName(instruction.Op1Register); var dest = "XMM_TEMP"; //TEMP_DEST[31:0] := Select4(SRC1[127:0], imm8[1:0]); - builder.Move(instruction.IP, ConvertVector(dest, 0), ConvertVector(src1, imm & 0b11)); + builder.Move(instruction.IP, ConvertVector(dest, 0), ConvertVector(src1, imm & 0b11)); //TEMP_DEST[63:32] := Select4(SRC1[127:0], imm8[3:2]); - builder.Move(instruction.IP, ConvertVector(dest, 1), ConvertVector(src1, (imm >> 2) & 0b11)); + builder.Move(instruction.IP, ConvertVector(dest, 1), ConvertVector(src1, (imm >> 2) & 0b11)); //TEMP_DEST[95:64] := Select4(SRC2[127:0], imm8[5:4]); builder.Move(instruction.IP, ConvertVector(dest, 2), ConvertVector(src2, (imm >> 4) & 0b11)); //TEMP_DEST[127:96] := Select4(SRC2[127:0], imm8[7:6]); @@ -332,12 +332,12 @@ private void ConvertInstructionStatement(Instruction instruction, IsilBuilder bu static InstructionSetIndependentOperand ConvertVector(string reg, int imm) => InstructionSetIndependentOperand.MakeVectorElement(reg, IsilVectorRegisterElementOperand.VectorElementWidth.S, imm); } - + case Mnemonic.Unpcklps : // Unpack and Interleave Low Packed Single Precision Floating-Point Values { if (instruction.Op1Kind == OpKind.Memory) goto default; - + var src1 = X86Utils.GetRegisterName(instruction.Op0Register); var src2 = X86Utils.GetRegisterName(instruction.Op1Register); var dest = "XMM_TEMP"; @@ -351,7 +351,7 @@ static InstructionSetIndependentOperand ConvertVector(string reg, int imm) => static InstructionSetIndependentOperand ConvertVector(string reg, int imm) => InstructionSetIndependentOperand.MakeVectorElement(reg, IsilVectorRegisterElementOperand.VectorElementWidth.S, imm); } - + case Mnemonic.Call: // We don't try and resolve which method is being called, but we do need to know how many parameters it has // I would hope that all of these methods have the same number of arguments, else how can they be inlined? @@ -425,9 +425,9 @@ static InstructionSetIndependentOperand ConvertVector(string reg, int imm) => case Mnemonic.Ucomiss: // same, but unsigned builder.Compare(instruction.IP, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1)); break; - + case Mnemonic.Cmove: // move if condition - case Mnemonic.Cmovne: + case Mnemonic.Cmovne: case Mnemonic.Cmova: case Mnemonic.Cmovg: case Mnemonic.Cmovae: @@ -435,9 +435,9 @@ static InstructionSetIndependentOperand ConvertVector(string reg, int imm) => case Mnemonic.Cmovb: case Mnemonic.Cmovl: case Mnemonic.Cmovbe: - case Mnemonic.Cmovle: - case Mnemonic.Cmovs: - case Mnemonic.Cmovns: + case Mnemonic.Cmovle: + case Mnemonic.Cmovs: + case Mnemonic.Cmovns: switch (instruction.Mnemonic) { case Mnemonic.Cmove: // equals @@ -487,7 +487,7 @@ static InstructionSetIndependentOperand ConvertVector(string reg, int imm) => builder.Nop(instruction.IP + 1); // exit for IF break; } - + case Mnemonic.Cmpxchg: // compare and exchange { var accumulator = InstructionSetIndependentOperand.MakeRegister(instruction.Op1Register.GetSize() switch @@ -508,11 +508,11 @@ static InstructionSetIndependentOperand ConvertVector(string reg, int imm) => // ELSE // SET ZF = 0 builder.Move(instruction.IP + 1, accumulator, dest); // accumulator = dest - + builder.Nop(instruction.IP + 2); // exit for IF break; } - + case Mnemonic.Jmp: if (instruction.Op0Kind != OpKind.Register) { @@ -566,7 +566,7 @@ static InstructionSetIndependentOperand ConvertVector(string reg, int imm) => builder.JumpIfSign(instruction.IP, jumpTarget); break; } - + goto default; case Mnemonic.Jns: if (instruction.Op0Kind != OpKind.Register) @@ -576,7 +576,7 @@ static InstructionSetIndependentOperand ConvertVector(string reg, int imm) => builder.JumpIfNotSign(instruction.IP, jumpTarget); break; } - + goto default; case Mnemonic.Jg: case Mnemonic.Ja: diff --git a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs index aaa089148..aa78a4085 100644 --- a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs +++ b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs @@ -11,6 +11,7 @@ using LibCpp2IL.Metadata; using StableNameDotNet.Providers; using System.Linq; +using Cpp2IL.Core.Graphs.Analysis; namespace Cpp2IL.Core.Model.Contexts; @@ -80,7 +81,7 @@ public class MethodAnalysisContext : HasCustomAttributesAndName, IMethodInfoProv //TODO Support custom attributes on return types (v31 feature) public TypeAnalysisContext ReturnTypeContext => InjectedReturnType ?? DeclaringType!.DeclaringAssembly.ResolveIl2CppType(Definition!.RawReturnType!); - + protected Memory? rawMethodBody; @@ -156,7 +157,7 @@ public void Analyze() if (ConvertedIsil.Count == 0) return; //Nothing to do, empty function - + ControlFlowGraph = ISILControlFlowGraph.Build(ConvertedIsil); // Post step to convert metadata usage. Ldstr Opcodes etc. @@ -167,6 +168,8 @@ public void Analyze() converter.Process(this, block); } } + + StackAnalyzer.Analyze(this); } public void ReleaseAnalysisData() diff --git a/Cpp2IL.Plugin.ControlFlowGraph/ControlFlowGraphOutputFormat.cs b/Cpp2IL.Plugin.ControlFlowGraph/ControlFlowGraphOutputFormat.cs index ff397ce2a..55480c72a 100644 --- a/Cpp2IL.Plugin.ControlFlowGraph/ControlFlowGraphOutputFormat.cs +++ b/Cpp2IL.Plugin.ControlFlowGraph/ControlFlowGraphOutputFormat.cs @@ -2,12 +2,14 @@ using Cpp2IL.Core.Model.Contexts; using Cpp2IL.Core.Logging; using Cpp2IL.Core.Utils; +using Cpp2IL.Core.ISIL; using Cpp2IL.Core.Extensions; using System.Text; using Cpp2IL.Core.Graphs; using DotNetGraph.Core; using DotNetGraph.Extensions; using DotNetGraph.Compilation; +using Cpp2IL.Core.Graphs.Analysis; namespace Cpp2IL.Plugin.ControlFlowGraph; @@ -61,6 +63,8 @@ public override void DoOutput(ApplicationAnalysisContext context, string outputR } }); } + Logger.InfoNewline($"StackAnalyzer unbalanced: {StackAnalyzer.unbalancedStackCount}"); + Logger.InfoNewline($"StackAnalyzer balanced: {StackAnalyzer.balanacedStackCount}"); } private string GenerateGraphTitle(MethodAnalysisContext context) @@ -82,17 +86,19 @@ public DotGraph GenerateGraph(ISILControlFlowGraph graph, MethodAnalysisContext .Directed() .WithLabel(GenerateGraphTitle(method)); - var nodeCache = new Dictionary(); + var nodeCache = new Dictionary, DotNode>(); var edgeCache = new List(); - DotNode GetOrAddNode(int id) + uint idCounter = 0; + DotNode GetOrAddNode(Block id) { + if (nodeCache.TryGetValue(id, out var node)) { return node; } - var newNode = new DotNode().WithIdentifier(id.ToString()); + var newNode = new DotNode().WithIdentifier(idCounter++.ToString()); directedGraph.Add(newNode); nodeCache[id] = newNode; return newNode; @@ -116,7 +122,7 @@ DotEdge GetOrAddEdge(DotNode from, DotNode to) foreach (var block in graph.Blocks) { - var node = GetOrAddNode(block.ID); + var node = GetOrAddNode(block); if (block.BlockType == BlockType.Entry) { node.WithColor("green"); @@ -135,7 +141,7 @@ DotEdge GetOrAddEdge(DotNode from, DotNode to) foreach (var succ in block.Successors) { - var target = GetOrAddNode(succ.ID); + var target = GetOrAddNode(succ); GetOrAddEdge(node, target); } } From f2ee54a0fe7283b505386a938e56edb2b092c1f9 Mon Sep 17 00:00:00 2001 From: gompoc <91314780+gompoc@users.noreply.github.com> Date: Fri, 13 Sep 2024 13:59:42 +0100 Subject: [PATCH 3/5] Core.Graphs: Stack checks --- .../Graphs/Analysis/Stack/StackAnalyzer.cs | 139 ++++++++++++++++++ .../Graphs/Analysis/Stack/StackEntry.cs | 31 ++++ Cpp2IL.Core/Graphs/Analysis/StackAnalyzer.cs | 96 ------------ .../Model/Contexts/MethodAnalysisContext.cs | 4 + .../ControlFlowGraphOutputFormat.cs | 2 +- 5 files changed, 175 insertions(+), 97 deletions(-) create mode 100644 Cpp2IL.Core/Graphs/Analysis/Stack/StackAnalyzer.cs create mode 100644 Cpp2IL.Core/Graphs/Analysis/Stack/StackEntry.cs delete mode 100644 Cpp2IL.Core/Graphs/Analysis/StackAnalyzer.cs diff --git a/Cpp2IL.Core/Graphs/Analysis/Stack/StackAnalyzer.cs b/Cpp2IL.Core/Graphs/Analysis/Stack/StackAnalyzer.cs new file mode 100644 index 000000000..09328a52e --- /dev/null +++ b/Cpp2IL.Core/Graphs/Analysis/Stack/StackAnalyzer.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using LibCpp2IL; +using Cpp2IL.Core.ISIL; +using Cpp2IL.Core.Model.Contexts; +using System.Linq; + +namespace Cpp2IL.Core.Graphs.Analysis.Stack; + +public sealed class StackAnalyzer +{ + private HashSet> visited = []; + + // TODO: Should stack state be per instruction or per block? + private Dictionary, StackEntry> inComingDelta = []; + private Dictionary, StackEntry> outGoingDelta = []; + + // debug + public static int unbalancedStackCount { get; private set; } = 0; + public static int balanacedStackCount { get; private set; } = 0; + + private StackAnalyzer() { } + + public static void Analyze(MethodAnalysisContext context) + { + try + { + var graph = context.ControlFlowGraph; + if (graph == null) + { + return; + } + var analyzer = new StackAnalyzer(); + analyzer.inComingDelta[graph.EntryBlock] = new StackEntry(); + var archSize = LibCpp2IlMain.Binary!.is32Bit ? 4 : 8; + analyzer.TraverseGraph(graph.EntryBlock, archSize); + var outDelta = analyzer.outGoingDelta[graph.ExitBlock]; + if (outDelta.StackState.Count != 0) + { + unbalancedStackCount++; + } + else + { + balanacedStackCount++; + foreach(var block in graph.Blocks) + { + // TODO: Replace push instructions with a move Stack 0x20, reg1 or whatever + // push instructions can be nopped? Same with shiftstack instructions? + if (block.BlockType == BlockType.Call) + { + var callInstruction = block.Instructions[^1]; + + var stackState = analyzer.outGoingDelta[block].StackState; + + + var stackParams = callInstruction.Operands.Where(op => op.Type == InstructionSetIndependentOperand.OperandType.StackOffset); + // TODO: translate stack offsets relative to call instruction to stack offsets relative to the base of the stack for the function + } + } + + } + } + catch (Exception e) + { + unbalancedStackCount++; + } + } + + private void TraverseGraph(Block block, int archSize) + { + var blockDelta = inComingDelta[block].Clone(); + + // TODO: Handle interrupt blocks, should we just remove them? + if (block.BlockType == BlockType.Call && block.Successors.Count == 1 && block.Successors[0].BlockType == BlockType.Exit) + { + // Tail call / CallNoReturn = Flush stack? + blockDelta.StackState.Clear(); + outGoingDelta[block] = blockDelta; + } + else + { + foreach (var instruction in block.Instructions) + { + switch (instruction.OpCode.Mnemonic) + { + case IsilMnemonic.Push: + blockDelta.PushEntry("push"); + break; + case IsilMnemonic.Pop: + blockDelta.PopEntry(); + break; + case IsilMnemonic.ShiftStack: + var value = (int)((IsilImmediateOperand)instruction.Operands[0].Data).Value; + if (value % archSize != 0) + { + throw new Exception("Unaligned stack shift"); + } else + { + for (int i = 0; i < Math.Abs(value / archSize); i++) + { + if (value < 0) + { + blockDelta.PushEntry("allocated space"); + } + else + { + blockDelta.PopEntry(); + } + } + } + + break; + } + } + outGoingDelta[block] = blockDelta; + } + + foreach (var succ in block.Successors) + { + if (!visited.Contains(succ)) + { + inComingDelta[succ] = blockDelta; + visited.Add(succ); + TraverseGraph(succ, archSize); + } + else + { + var expectedDelta = inComingDelta[succ]; + + if (expectedDelta != blockDelta) + { + // TODO: Investigate Guid\.ctor_Byte[].dot, stack appears to be well formed but results in unbalanced stack somehow + throw new Exception("Unbalanced stack"); + } + inComingDelta[succ] = blockDelta; + } + } + } +} diff --git a/Cpp2IL.Core/Graphs/Analysis/Stack/StackEntry.cs b/Cpp2IL.Core/Graphs/Analysis/Stack/StackEntry.cs new file mode 100644 index 000000000..ce08b91fe --- /dev/null +++ b/Cpp2IL.Core/Graphs/Analysis/Stack/StackEntry.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Cpp2IL.Core.Graphs.Analysis.Stack; + +// Avert your eyes... I'm just trying to get this to work +internal sealed class StackEntry +{ + public Stack StackState = []; + + public int Size => StackState.Count; + + public void PushEntry(string value) => StackState.Push(value); + + public string PopEntry() => StackState.Pop(); + + public static StackEntry Copy(StackEntry other) + { + var newEntry = new StackEntry(); + + foreach (var entry in other.StackState.Reverse()) + newEntry.PushEntry(entry); + + return newEntry; + } + + public StackEntry Clone() + { + return Copy(this); + } +} diff --git a/Cpp2IL.Core/Graphs/Analysis/StackAnalyzer.cs b/Cpp2IL.Core/Graphs/Analysis/StackAnalyzer.cs deleted file mode 100644 index 4db963029..000000000 --- a/Cpp2IL.Core/Graphs/Analysis/StackAnalyzer.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Collections.Generic; -using LibCpp2IL; -using Cpp2IL.Core.ISIL; -using Cpp2IL.Core.Model.Contexts; -using System.Diagnostics; -namespace Cpp2IL.Core.Graphs.Analysis; - -public sealed class StackAnalyzer -{ - public HashSet> visited = []; - public Dictionary, int> inComingDelta = []; - public Dictionary, int> outGoingDelta = []; - - public static int unbalancedStackCount { get; private set; } = 0; - public static int balanacedStackCount { get; private set; } = 0; - - private StackAnalyzer() {} - - public static void Analyze(MethodAnalysisContext context) - { - try - { - var graph = context.ControlFlowGraph; - if (graph == null) - { - return; - } - var analyzer = new StackAnalyzer(); - analyzer.inComingDelta[graph.EntryBlock] = 0; - int archSize = LibCpp2IlMain.Binary!.is32Bit ? 4 : 8; - analyzer.TraverseGraph(graph.EntryBlock, archSize); - var outDelta = analyzer.outGoingDelta[graph.ExitBlock]; - if (outDelta != 0) - { - unbalancedStackCount++; - } - else - { - balanacedStackCount++; - } - } catch (Exception e) - { - unbalancedStackCount++; - } - } - - private void TraverseGraph(Block block, int archSize) - { - var blockDelta = inComingDelta[block]; - - if (block.BlockType == BlockType.Call && block.Successors.Count == 1 && block.Successors[0].BlockType == BlockType.Exit) - { - // Tail call / CallNoReturn - blockDelta = 0; - outGoingDelta[block] = blockDelta; - } else - { - foreach (var instruction in block.Instructions) - { - switch (instruction.OpCode.Mnemonic) - { - case IsilMnemonic.Push: - blockDelta -= archSize; - break; - case IsilMnemonic.Pop: - blockDelta += archSize; - break; - case IsilMnemonic.ShiftStack: - blockDelta += (int)((IsilImmediateOperand)instruction.Operands[0].Data).Value; - break; - } - } - outGoingDelta[block] = blockDelta; - } - - foreach (var succ in block.Successors) - { - if (!visited.Contains(succ)) - { - inComingDelta[succ] = blockDelta; - visited.Add(succ); - TraverseGraph(succ, archSize); - } else - { - var expectedDelta = inComingDelta[succ]; - - if (expectedDelta != blockDelta) - { - throw new Exception("Unbalanced stack"); - } - inComingDelta[succ] = blockDelta; - } - } - } -} diff --git a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs index aa78a4085..cb4d388c7 100644 --- a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs +++ b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs @@ -10,9 +10,13 @@ using LibCpp2IL; using LibCpp2IL.Metadata; using StableNameDotNet.Providers; + using System.Linq; using Cpp2IL.Core.Graphs.Analysis; +using Cpp2IL.Core.Graphs.Analysis.Stack; + + namespace Cpp2IL.Core.Model.Contexts; /// diff --git a/Cpp2IL.Plugin.ControlFlowGraph/ControlFlowGraphOutputFormat.cs b/Cpp2IL.Plugin.ControlFlowGraph/ControlFlowGraphOutputFormat.cs index 55480c72a..b44a06686 100644 --- a/Cpp2IL.Plugin.ControlFlowGraph/ControlFlowGraphOutputFormat.cs +++ b/Cpp2IL.Plugin.ControlFlowGraph/ControlFlowGraphOutputFormat.cs @@ -9,7 +9,7 @@ using DotNetGraph.Core; using DotNetGraph.Extensions; using DotNetGraph.Compilation; -using Cpp2IL.Core.Graphs.Analysis; +using Cpp2IL.Core.Graphs.Analysis.Stack; namespace Cpp2IL.Plugin.ControlFlowGraph; From 68147af68b30604c5b4d00599b46430c2bee3569 Mon Sep 17 00:00:00 2001 From: gompoc <91314780+gompoc@users.noreply.github.com> Date: Thu, 28 Nov 2024 20:09:12 +0000 Subject: [PATCH 4/5] Update StackAnalyzer.cs --- .../Graphs/Analysis/Stack/StackAnalyzer.cs | 71 ++++++++++++++----- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/Cpp2IL.Core/Graphs/Analysis/Stack/StackAnalyzer.cs b/Cpp2IL.Core/Graphs/Analysis/Stack/StackAnalyzer.cs index 09328a52e..9b85c8391 100644 --- a/Cpp2IL.Core/Graphs/Analysis/Stack/StackAnalyzer.cs +++ b/Cpp2IL.Core/Graphs/Analysis/Stack/StackAnalyzer.cs @@ -11,10 +11,12 @@ public sealed class StackAnalyzer { private HashSet> visited = []; - // TODO: Should stack state be per instruction or per block? + // This is overkill and slow but it works for now private Dictionary, StackEntry> inComingDelta = []; private Dictionary, StackEntry> outGoingDelta = []; + private Dictionary instructionsStackState = []; + // debug public static int unbalancedStackCount { get; private set; } = 0; public static int balanacedStackCount { get; private set; } = 0; @@ -41,22 +43,51 @@ public static void Analyze(MethodAnalysisContext context) } else { - balanacedStackCount++; + foreach(var block in graph.Blocks) { - // TODO: Replace push instructions with a move Stack 0x20, reg1 or whatever - // push instructions can be nopped? Same with shiftstack instructions? + foreach (var instruction in block.Instructions) + { + var currentPos = (analyzer.instructionsStackState[instruction].StackState.Count) * archSize; + if (instruction.OpCode.Mnemonic == IsilMnemonic.Push) + { + instruction.OpCode = InstructionSetIndependentOpCode.Move; + instruction.Operands = [InstructionSetIndependentOperand.MakeStack(currentPos), instruction.Operands[1]]; + } + else if (instruction.OpCode.Mnemonic == IsilMnemonic.Pop) + { + + instruction.OpCode = InstructionSetIndependentOpCode.Move; + instruction.Operands = [instruction.Operands[0], InstructionSetIndependentOperand.MakeStack(currentPos)]; + } + else if (instruction.OpCode.Mnemonic == IsilMnemonic.ShiftStack) + { + instruction.OpCode = InstructionSetIndependentOpCode.Nop; + instruction.Operands = []; + } + } if (block.BlockType == BlockType.Call) { var callInstruction = block.Instructions[^1]; - var stackState = analyzer.outGoingDelta[block].StackState; - + var stackState = analyzer.instructionsStackState[callInstruction].StackState; + var stackSize = stackState.Count * archSize; + for (int i = 0; i < callInstruction.Operands.Length; i++) + { + var op = callInstruction.Operands[i]; + if (op.Type == InstructionSetIndependentOperand.OperandType.StackOffset) + { + + var actual = stackSize - ((IsilStackOperand)op.Data).Offset; + callInstruction.Operands[i] = InstructionSetIndependentOperand.MakeStack(actual); + } + } - var stackParams = callInstruction.Operands.Where(op => op.Type == InstructionSetIndependentOperand.OperandType.StackOffset); - // TODO: translate stack offsets relative to call instruction to stack offsets relative to the base of the stack for the function + // Filter out stack operands with an offset < 0, we've overestimated how many actual args this function has + callInstruction.Operands = callInstruction.Operands.Where(op => op.Type != InstructionSetIndependentOperand.OperandType.StackOffset || ((IsilStackOperand)op.Data).Offset > stackSize).ToArray(); } } + balanacedStackCount++; } } @@ -70,24 +101,30 @@ private void TraverseGraph(Block block, in { var blockDelta = inComingDelta[block].Clone(); - // TODO: Handle interrupt blocks, should we just remove them? + // TODO: Handle interrupt blocks: Call -> Interrupt -> Exit if (block.BlockType == BlockType.Call && block.Successors.Count == 1 && block.Successors[0].BlockType == BlockType.Exit) { + // still need to calculate it for instructions // Tail call / CallNoReturn = Flush stack? blockDelta.StackState.Clear(); outGoingDelta[block] = blockDelta; } else { + var previous = blockDelta; foreach (var instruction in block.Instructions) { + instructionsStackState[instruction] = previous; + switch (instruction.OpCode.Mnemonic) { case IsilMnemonic.Push: - blockDelta.PushEntry("push"); + previous = previous.Clone(); + previous.PushEntry("push"); break; case IsilMnemonic.Pop: - blockDelta.PopEntry(); + previous = previous.Clone(); + previous.PopEntry(); break; case IsilMnemonic.ShiftStack: var value = (int)((IsilImmediateOperand)instruction.Operands[0].Data).Value; @@ -96,22 +133,23 @@ private void TraverseGraph(Block block, in throw new Exception("Unaligned stack shift"); } else { + previous = previous.Clone(); for (int i = 0; i < Math.Abs(value / archSize); i++) { if (value < 0) { - blockDelta.PushEntry("allocated space"); + previous.PushEntry("allocated space"); } else { - blockDelta.PopEntry(); + previous.PopEntry(); } } } - break; } } + blockDelta = previous; outGoingDelta[block] = blockDelta; } @@ -127,9 +165,10 @@ private void TraverseGraph(Block block, in { var expectedDelta = inComingDelta[succ]; - if (expectedDelta != blockDelta) + if (expectedDelta.StackState.Count != blockDelta.StackState.Count) { - // TODO: Investigate Guid\.ctor_Byte[].dot, stack appears to be well formed but results in unbalanced stack somehow + // TODO: Investigate SystemGuid::.ctor(Byte[]), stack appears to be well formed but results in unbalanced stack somehow + // TODO: Investigate System.WeakReference::get_Target(), has some wack stack manipulation throw new Exception("Unbalanced stack"); } inComingDelta[succ] = blockDelta; From a2531a7dde61b94eca1352cffa087bcfcf9f217a Mon Sep 17 00:00:00 2001 From: gompoc <91314780+gompoc@users.noreply.github.com> Date: Sun, 15 Dec 2024 20:34:34 +0000 Subject: [PATCH 5/5] Cpp2IL.Core: Stack analysis & remove unused code --- .../NodeConditionCalculationException.cs | 5 -- .../Graphs/Analysis/Stack/StackAnalyzer.cs | 61 +++++++++++-------- .../ISIL/InstructionSetIndependentOpCode.cs | 2 - Cpp2IL.Core/ISIL/IsilBuilder.cs | 5 -- Cpp2IL.Core/ISIL/IsilMnemonic.cs | 2 - .../InstructionSets/X86InstructionSet.cs | 2 +- .../Model/Contexts/MethodAnalysisContext.cs | 3 +- 7 files changed, 39 insertions(+), 41 deletions(-) delete mode 100644 Cpp2IL.Core/Exceptions/NodeConditionCalculationException.cs diff --git a/Cpp2IL.Core/Exceptions/NodeConditionCalculationException.cs b/Cpp2IL.Core/Exceptions/NodeConditionCalculationException.cs deleted file mode 100644 index d7c278bb2..000000000 --- a/Cpp2IL.Core/Exceptions/NodeConditionCalculationException.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System; - -namespace Cpp2IL.Core.Exceptions; - -public class NodeConditionCalculationException(string message) : Exception(message); diff --git a/Cpp2IL.Core/Graphs/Analysis/Stack/StackAnalyzer.cs b/Cpp2IL.Core/Graphs/Analysis/Stack/StackAnalyzer.cs index 9b85c8391..c10cda134 100644 --- a/Cpp2IL.Core/Graphs/Analysis/Stack/StackAnalyzer.cs +++ b/Cpp2IL.Core/Graphs/Analysis/Stack/StackAnalyzer.cs @@ -1,17 +1,25 @@ using System; +using System.Linq; using System.Collections.Generic; using LibCpp2IL; using Cpp2IL.Core.ISIL; using Cpp2IL.Core.Model.Contexts; -using System.Linq; namespace Cpp2IL.Core.Graphs.Analysis.Stack; +/* The whole purpose of this class is to try analyze the stack state of a method. + * With this information we should be able to determine the offset of the stack + * for instructions and blocks and correct "move stack(0xXX) , reg10" instructions + * without a correct stack offset. Also NOP shift stack instructions. + */ public sealed class StackAnalyzer { private HashSet> visited = []; - // This is overkill and slow but it works for now + // Stack offset for each block we have. To my knowledge this should be consistent. + // If it's not then its a problem (should be a very small % of cases) + // Most of these mismatches currently are because of switch/exception catchers + // which are not implemented yet. private Dictionary, StackEntry> inComingDelta = []; private Dictionary, StackEntry> outGoingDelta = []; @@ -23,14 +31,14 @@ public sealed class StackAnalyzer private StackAnalyzer() { } - public static void Analyze(MethodAnalysisContext context) + public static bool Analyze(MethodAnalysisContext context) { try { var graph = context.ControlFlowGraph; if (graph == null) { - return; + return false; } var analyzer = new StackAnalyzer(); analyzer.inComingDelta[graph.EntryBlock] = new StackEntry(); @@ -39,32 +47,38 @@ public static void Analyze(MethodAnalysisContext context) var outDelta = analyzer.outGoingDelta[graph.ExitBlock]; if (outDelta.StackState.Count != 0) { + // This method ends with a non-empty stack, let's just bail early for now unbalancedStackCount++; + return false; } else { - foreach(var block in graph.Blocks) { + InstructionSetIndependentInstruction? previousInstruction = null; foreach (var instruction in block.Instructions) { var currentPos = (analyzer.instructionsStackState[instruction].StackState.Count) * archSize; - if (instruction.OpCode.Mnemonic == IsilMnemonic.Push) - { - instruction.OpCode = InstructionSetIndependentOpCode.Move; - instruction.Operands = [InstructionSetIndependentOperand.MakeStack(currentPos), instruction.Operands[1]]; - } - else if (instruction.OpCode.Mnemonic == IsilMnemonic.Pop) - { - instruction.OpCode = InstructionSetIndependentOpCode.Move; - instruction.Operands = [instruction.Operands[0], InstructionSetIndependentOperand.MakeStack(currentPos)]; - } - else if (instruction.OpCode.Mnemonic == IsilMnemonic.ShiftStack) + /* + * Push/Pop + * builder.ShiftStack(instruction.IP, -operandSize); + * builder.Move(instruction.IP, InstructionSetIndependentOperand.MakeStack(0), ConvertOperand(instruction, 0)); + */ + if (instruction.OpCode.Mnemonic == IsilMnemonic.ShiftStack) { + // NOP the shift stack instruction instruction.OpCode = InstructionSetIndependentOpCode.Nop; instruction.Operands = []; - } + // Correct stack offset for previous move instruction if it matches (push/pop combo) + if (previousInstruction != null && + previousInstruction.OpCode == InstructionSetIndependentOpCode.Move && + previousInstruction.Operands is [InstructionSetIndependentOperand { Type: InstructionSetIndependentOperand.OperandType.StackOffset, Data: IsilStackOperand { Offset: 0 } }, InstructionSetIndependentOperand op2]) + { + previousInstruction.Operands = [InstructionSetIndependentOperand.MakeStack(currentPos), op2]; + } + } + previousInstruction = instruction; } if (block.BlockType == BlockType.Call) { @@ -88,15 +102,20 @@ public static void Analyze(MethodAnalysisContext context) } } balanacedStackCount++; + return true; } } catch (Exception e) { unbalancedStackCount++; + return false; } } + + + // Traverse the graph and calculate the stack state for each block and instruction private void TraverseGraph(Block block, int archSize) { var blockDelta = inComingDelta[block].Clone(); @@ -118,14 +137,6 @@ private void TraverseGraph(Block block, in switch (instruction.OpCode.Mnemonic) { - case IsilMnemonic.Push: - previous = previous.Clone(); - previous.PushEntry("push"); - break; - case IsilMnemonic.Pop: - previous = previous.Clone(); - previous.PopEntry(); - break; case IsilMnemonic.ShiftStack: var value = (int)((IsilImmediateOperand)instruction.Operands[0].Data).Value; if (value % archSize != 0) diff --git a/Cpp2IL.Core/ISIL/InstructionSetIndependentOpCode.cs b/Cpp2IL.Core/ISIL/InstructionSetIndependentOpCode.cs index de75395cc..4622ff505 100644 --- a/Cpp2IL.Core/ISIL/InstructionSetIndependentOpCode.cs +++ b/Cpp2IL.Core/ISIL/InstructionSetIndependentOpCode.cs @@ -31,8 +31,6 @@ public class InstructionSetIndependentOpCode //public static readonly InstructionSetIndependentOpCode CompareLessThanOrEqual = new(IsilMnemonic.CompareLessThanOrEqual, 2, InstructionSetIndependentOperand.OperandType.Any, InstructionSetIndependentOperand.OperandType.Any); //public static readonly InstructionSetIndependentOpCode CompareGreaterThanOrEqual = new(IsilMnemonic.CompareGreaterThanOrEqual, 2, InstructionSetIndependentOperand.OperandType.Any, InstructionSetIndependentOperand.OperandType.Any); public static readonly InstructionSetIndependentOpCode ShiftStack = new(IsilMnemonic.ShiftStack, 1, InstructionSetIndependentOperand.OperandType.Immediate); - public static readonly InstructionSetIndependentOpCode Push = new(IsilMnemonic.Push, 2, InstructionSetIndependentOperand.OperandType.Register, InstructionSetIndependentOperand.OperandType.Any); - public static readonly InstructionSetIndependentOpCode Pop = new(IsilMnemonic.Pop, 2, InstructionSetIndependentOperand.OperandType.Any, InstructionSetIndependentOperand.OperandType.Register); public static readonly InstructionSetIndependentOpCode Return = new(IsilMnemonic.Return, 1, InstructionSetIndependentOperand.OperandType.NotStack); public static readonly InstructionSetIndependentOpCode Goto = new(IsilMnemonic.Goto, 1, InstructionSetIndependentOperand.OperandType.Instruction); diff --git a/Cpp2IL.Core/ISIL/IsilBuilder.cs b/Cpp2IL.Core/ISIL/IsilBuilder.cs index ca94f17f7..352c03371 100644 --- a/Cpp2IL.Core/ISIL/IsilBuilder.cs +++ b/Cpp2IL.Core/ISIL/IsilBuilder.cs @@ -68,12 +68,7 @@ public void FixJumps() public void LoadAddress(ulong instructionAddress, InstructionSetIndependentOperand dest, InstructionSetIndependentOperand src) => AddInstruction(new(InstructionSetIndependentOpCode.LoadAddress, instructionAddress, IsilFlowControl.Continue, dest, src)); public void ShiftStack(ulong instructionAddress, int amount) => AddInstruction(new(InstructionSetIndependentOpCode.ShiftStack, instructionAddress, IsilFlowControl.Continue, InstructionSetIndependentOperand.MakeImmediate(amount))); - - public void Push(ulong instructionAddress, InstructionSetIndependentOperand stackPointerRegister, InstructionSetIndependentOperand operand) => AddInstruction(new(InstructionSetIndependentOpCode.Push, instructionAddress, IsilFlowControl.Continue, stackPointerRegister, operand)); - public void Pop(ulong instructionAddress, InstructionSetIndependentOperand stackPointerRegister, InstructionSetIndependentOperand operand) => AddInstruction(new(InstructionSetIndependentOpCode.Pop, instructionAddress, IsilFlowControl.Continue, operand, stackPointerRegister)); - public void Exchange(ulong instructionAddress, InstructionSetIndependentOperand place1, InstructionSetIndependentOperand place2) => AddInstruction(new(InstructionSetIndependentOpCode.Exchange, instructionAddress, IsilFlowControl.Continue, place1, place2)); - public void Subtract(ulong instructionAddress, InstructionSetIndependentOperand dest, InstructionSetIndependentOperand left, InstructionSetIndependentOperand right) => AddInstruction(new(InstructionSetIndependentOpCode.Subtract, instructionAddress, IsilFlowControl.Continue, dest, left, right)); public void Add(ulong instructionAddress, InstructionSetIndependentOperand dest, InstructionSetIndependentOperand left, InstructionSetIndependentOperand right) => AddInstruction(new(InstructionSetIndependentOpCode.Add, instructionAddress, IsilFlowControl.Continue, dest, left, right)); diff --git a/Cpp2IL.Core/ISIL/IsilMnemonic.cs b/Cpp2IL.Core/ISIL/IsilMnemonic.cs index 008025c64..e3e240247 100644 --- a/Cpp2IL.Core/ISIL/IsilMnemonic.cs +++ b/Cpp2IL.Core/ISIL/IsilMnemonic.cs @@ -20,8 +20,6 @@ public enum IsilMnemonic Neg, Compare, ShiftStack, - Push, - Pop, Return, Goto, JumpIfEqual, diff --git a/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs b/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs index bd82f3f50..395ccfec7 100644 --- a/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs +++ b/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs @@ -240,8 +240,8 @@ private void ConvertInstructionStatement(Instruction instruction, IsilBuilder bu break; case Mnemonic.Push: operandSize = instruction.Op0Kind == OpKind.Register ? instruction.Op0Register.GetSize() : instruction.MemorySize.GetSize(); - builder.ShiftStack(instruction.IP, -operandSize); builder.Move(instruction.IP, InstructionSetIndependentOperand.MakeStack(0), ConvertOperand(instruction, 0)); + builder.ShiftStack(instruction.IP, -operandSize); break; case Mnemonic.Pop: operandSize = instruction.Op0Kind == OpKind.Register ? instruction.Op0Register.GetSize() : instruction.MemorySize.GetSize(); diff --git a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs index cb4d388c7..0a0ce7a50 100644 --- a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs +++ b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs @@ -173,7 +173,8 @@ public void Analyze() } } - StackAnalyzer.Analyze(this); + if (!StackAnalyzer.Analyze(this)) + return; } public void ReleaseAnalysisData()