public void GetBuilderByBlockIndex_returns_existent_builders() { var graphBuilder = new BasicBlockGraphBuilder(); var first = graphBuilder.GetInitialBlockBuilder(); // Out-of-bounds indices are not tolerated Assert.That(() => graphBuilder.GetBuilderByBlockIndex(1), Throws.InstanceOf <ArgumentOutOfRangeException>()); var second = graphBuilder.GetNewBasicBlock(); Assert.That(graphBuilder.GetBuilderByBlockIndex(0), Is.SameAs(first)); Assert.That(graphBuilder.GetBuilderByBlockIndex(1), Is.SameAs(second)); }
public void Unreachable_blocks_are_dropped() { var graphBuilder = new BasicBlockGraphBuilder(); // The first and the last block are alive, the two in the middle can be dropped var firstBuilder = graphBuilder.GetInitialBlockBuilder(); graphBuilder.GetNewBasicBlock().CreateSuccessorBlock(); firstBuilder.CreateSuccessorBlock().AppendInstruction(Opcode.Return, 0, 0, 0); var graph = graphBuilder.Build(); Assert.That(graph.BasicBlocks, Has.Exactly(2).Items); Assert.That(graph.BasicBlocks[0].Instructions, Is.Empty); Assert.That(graph.BasicBlocks[0].DefaultSuccessor, Is.EqualTo(1)); Assert.That(graph.BasicBlocks[0].AlternativeSuccessor, Is.EqualTo(-1)); Assert.That(graph.BasicBlocks[1].Instructions, Has.Exactly(1).Items); Assert.That(graph.BasicBlocks[1].Instructions[0].Operation, Is.EqualTo(Opcode.Return)); Assert.That(graph.BasicBlocks[1].DefaultSuccessor, Is.EqualTo(-1)); CollectionAssert.AreEqual(new[] { 0 }, graph.BasicBlocks[1].Predecessors); }
/// <summary> /// Creates a <see cref="CompiledMethod"/> out of the given IR disassembly, /// which must follow the <see cref="MethodDisassembler"/> output very closely. /// </summary> public static CompiledMethod Assemble(string source, string fullName) { var method = new CompiledMethod(fullName); var graphBuilder = new BasicBlockGraphBuilder(); BasicBlockBuilder?currentBlockBuilder = null; var calledMethodIndices = new Dictionary <string, int>(); // Since this class is for testing purposes only, we use brittle and unperformant string splits var lines = source.Replace("\r\n", "\n").Split('\n'); foreach (var rawLine in lines) { var currentLine = rawLine.Trim(); if (currentLine.Length == 0) { // Empty (or whitespace) line - skip } else if (currentLine.StartsWith("; #")) { // A variable definition (hopefully) - add a local ParseLocal(currentLine, method); } else if (currentLine.StartsWith("BB_")) { // Starting a new basic block // First finalize the current basic block, if needed, with a fallthrough if (currentBlockBuilder != null && !currentBlockBuilder.HasDefinedExitBehavior) { currentBlockBuilder.SetSuccessor(currentBlockBuilder.Index + 1); } // The line is of form "BB_nnn:" so we have to chop bits off both ends var blockIndex = int.Parse(currentLine.AsSpan(3, currentLine.Length - 4)); currentBlockBuilder = graphBuilder.GetNewBasicBlock(); Assert.That(blockIndex, Is.EqualTo(currentBlockBuilder.Index), "Blocks must be specified in order."); } else if (currentLine.StartsWith("==>")) { // Explicitly set the successor block // The line is of form "==> BB_nnn" var blockIndex = int.Parse(currentLine.AsSpan(7)); Assert.That(currentBlockBuilder, Is.Not.Null, "No basic block has been started."); currentBlockBuilder !.SetSuccessor(blockIndex); } else if (currentLine.StartsWith("PHI")) { Assert.That(currentBlockBuilder, Is.Not.Null, "No basic block has been started."); // Phi: read the unknown number of operands - the last one is the destination var phiParts = currentLine.Split(' ', '(', ',', ')'); var phiBuilder = ImmutableList <int> .Empty.ToBuilder(); foreach (var operand in phiParts) { if (operand.StartsWith("#")) { phiBuilder.Add(int.Parse(operand.AsSpan(1))); } } var dest = phiBuilder[phiBuilder.Count - 1]; phiBuilder.RemoveAt(phiBuilder.Count - 1); currentBlockBuilder !.AddPhi(dest, phiBuilder.ToImmutable()); } else { Assert.That(currentBlockBuilder, Is.Not.Null, "No basic block has been started."); ParseInstruction(currentLine, method, currentBlockBuilder !, calledMethodIndices); } } method.Body = graphBuilder.Build(); return(method); }