public static (Block[] Graph, Block Root) DecodeSubroutine( TranslatorCache cache, CpuThreadState state, MemoryManager memory, long start) { Dictionary <long, Block> visited = new Dictionary <long, Block>(); Dictionary <long, Block> visitedEnd = new Dictionary <long, Block>(); Queue <Block> blocks = new Queue <Block>(); Block Enqueue(long position) { if (!visited.TryGetValue(position, out Block output)) { output = new Block(position); blocks.Enqueue(output); visited.Add(position, output); } return(output); } Block root = Enqueue(start); while (blocks.Count > 0) { Block current = blocks.Dequeue(); FillBlock(state, memory, current); //Set child blocks. "Branch" is the block the branch instruction //points to (when taken), "Next" is the block at the next address, //executed when the branch is not taken. For Unconditional Branches //(except BL/BLR that are sub calls) or end of executable, Next is null. if (current.OpCodes.Count > 0) { bool hasCachedSub = false; OpCode64 lastOp = current.GetLastOp(); if (lastOp is OpCodeBImm64 op) { if (op.Emitter == InstEmit.Bl) { hasCachedSub = cache.HasSubroutine(op.Imm); } else { current.Branch = Enqueue(op.Imm); } } if (!((lastOp is OpCodeBImmAl64) || (lastOp is OpCodeBReg64)) || hasCachedSub) { current.Next = Enqueue(current.EndPosition); } } //If we have on the graph two blocks with the same end position, //then we need to split the bigger block and have two small blocks, //the end position of the bigger "Current" block should then be == to //the position of the "Smaller" block. while (visitedEnd.TryGetValue(current.EndPosition, out Block smaller)) { if (current.Position > smaller.Position) { Block temp = smaller; smaller = current; current = temp; } current.EndPosition = smaller.Position; current.Next = smaller; current.Branch = null; current.OpCodes.RemoveRange( current.OpCodes.Count - smaller.OpCodes.Count, smaller.OpCodes.Count); visitedEnd[smaller.EndPosition] = smaller; } visitedEnd.Add(current.EndPosition, current); } //Make and sort Graph blocks array by position. Block[] graph = new Block[visited.Count]; while (visited.Count > 0) { ulong firstPos = ulong.MaxValue; foreach (Block block in visited.Values) { if (firstPos > (ulong)block.Position) { firstPos = (ulong)block.Position; } } Block current = visited[(long)firstPos]; do { graph[graph.Length - visited.Count] = current; visited.Remove(current.Position); current = current.Next; }while (current != null); } return(graph, root); }
public static Block DecodeSubroutine( TranslatorCache cache, MemoryManager memory, long start, ExecutionMode mode) { Dictionary <long, Block> visited = new Dictionary <long, Block>(); Dictionary <long, Block> visitedEnd = new Dictionary <long, Block>(); Queue <Block> blocks = new Queue <Block>(); Block Enqueue(long position) { if (!visited.TryGetValue(position, out Block output)) { output = new Block(position); blocks.Enqueue(output); visited.Add(position, output); } return(output); } Block entry = Enqueue(start); while (blocks.Count > 0) { Block current = blocks.Dequeue(); FillBlock(memory, mode, current); //Set child blocks. "Branch" is the block the branch instruction //points to (when taken), "Next" is the block at the next address, //executed when the branch is not taken. For Unconditional Branches //(except BL/BLR that are sub calls) or end of executable, Next is null. if (current.OpCodes.Count > 0) { bool hasCachedSub = false; OpCode64 lastOp = current.GetLastOp(); if (lastOp is IOpCodeBImm op) { if (op.Emitter == InstEmit.Bl) { hasCachedSub = cache.HasSubroutine(op.Imm); } else { current.Branch = Enqueue(op.Imm); } } if (!IsUnconditionalBranch(lastOp) || hasCachedSub) { current.Next = Enqueue(current.EndPosition); } } //If we have on the graph two blocks with the same end position, //then we need to split the bigger block and have two small blocks, //the end position of the bigger "Current" block should then be == to //the position of the "Smaller" block. while (visitedEnd.TryGetValue(current.EndPosition, out Block smaller)) { if (current.Position > smaller.Position) { Block temp = smaller; smaller = current; current = temp; } current.EndPosition = smaller.Position; current.Next = smaller; current.Branch = null; current.OpCodes.RemoveRange( current.OpCodes.Count - smaller.OpCodes.Count, smaller.OpCodes.Count); visitedEnd[smaller.EndPosition] = smaller; } visitedEnd.Add(current.EndPosition, current); } return(entry); }