示例#1
0
        public void Graph_with_two_returning_branches_and_empty_block_succeeds()
        {
            var graphBuilder = new BasicBlockGraphBuilder();
            var firstBuilder = graphBuilder.GetInitialBlockBuilder();

            var leftBuilder = firstBuilder.CreateSuccessorBlock();

            leftBuilder.AppendInstruction(Opcode.Return, 0, 0, 0);
            var rightBuilder = firstBuilder.CreateBranch(7);

            rightBuilder.AppendInstruction(Opcode.Return, 0, 0, 0);

            // This block does not return nor is referenced, therefore it should be dropped
            var finalBuilder = leftBuilder.CreateSuccessorBlock();

            finalBuilder.AppendInstruction(Opcode.CopyValue, 1, 2, 3);

            var graph = graphBuilder.Build();

            Assert.That(graph.BasicBlocks, Has.Exactly(3).Items);
            Assert.That(graph.BasicBlocks[0].Instructions, Has.Exactly(1).Items);
            Assert.That(graph.BasicBlocks[0].Instructions[0].Operation, Is.EqualTo(Opcode.BranchIf));
            Assert.That(graph.BasicBlocks[0].Instructions[0].Left, Is.EqualTo(7));
            Assert.That(graph.BasicBlocks[0].DefaultSuccessor, Is.EqualTo(1));
            Assert.That(graph.BasicBlocks[0].AlternativeSuccessor, Is.EqualTo(2));

            Assert.That(graph.BasicBlocks[1].Instructions, Has.Exactly(1).Items);
            Assert.That(graph.BasicBlocks[1].DefaultSuccessor, Is.EqualTo(-1));
            CollectionAssert.AreEqual(new[] { 0 }, graph.BasicBlocks[1].Predecessors);

            Assert.That(graph.BasicBlocks[2].Instructions, Has.Exactly(1).Items);
            Assert.That(graph.BasicBlocks[2].DefaultSuccessor, Is.EqualTo(-1));
            CollectionAssert.AreEqual(new[] { 0 }, graph.BasicBlocks[2].Predecessors);
        }
示例#2
0
        public void DisassembleBody_three_basic_blocks_with_branch_2()
        {
            var graphBuilder = new BasicBlockGraphBuilder();
            var firstBuilder = graphBuilder.GetInitialBlockBuilder();

            // In this test case, the return succeeds the initial block...
            var returnBuilder = firstBuilder.CreateBranch(1);

            returnBuilder.AppendInstruction(Opcode.Return, 0, 0, 0);
            var loopBuilder = firstBuilder.CreateSuccessorBlock();

            loopBuilder.AppendInstruction(Opcode.Nop, 0, 0, 0);
            loopBuilder.SetSuccessor(0);

            var method = new CompiledMethod("Test::Method")
            {
                Body = graphBuilder.Build()
            };

            // ...and both successors are displayed.
            const string expected = "BB_0:\n    BranchIf #1 ==> BB_1\n    ==> BB_2\n\n" +
                                    "BB_1:\n    Return #0\n\n" +
                                    "BB_2:\n    Nop\n    ==> BB_0\n\n";

            var builder = new StringBuilder();

            MethodDisassembler.DisassembleBody(method, builder);
            Assert.That(builder.ToString().Replace("\r\n", "\n"), Is.EqualTo(expected));
        }
示例#3
0
        public void DisassembleBody_single_basic_block_with_several_instructions()
        {
            // TODO: Add new instructions to this test

            var graphBuilder = new BasicBlockGraphBuilder();
            var blockBuilder = graphBuilder.GetInitialBlockBuilder();

            blockBuilder.AppendInstruction(Opcode.Load, unchecked ((ulong)-17), 0, 0);
            blockBuilder.AppendInstruction(Opcode.Load, 1, 0, 1);
            blockBuilder.AppendInstruction(Opcode.CopyValue, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.Add, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.Subtract, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.Multiply, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.Divide, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.Modulo, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.ArithmeticNegate, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.ShiftLeft, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.ShiftRight, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.BitwiseAnd, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.BitwiseNot, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.BitwiseOr, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.BitwiseXor, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.Less, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.LessOrEqual, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.Equal, 1, 0, 2);
            blockBuilder.AppendInstruction(Opcode.Return, 2, 0, 0);
            var method = new CompiledMethod("Test::Method")
            {
                Body = graphBuilder.Build()
            };

            method.AddLocal(SimpleType.Int32, LocalFlags.None);
            method.AddLocal(SimpleType.Bool, LocalFlags.None);

            const string expected = "BB_0:\n" +
                                    "    Load -17 -> #0\n" +
                                    "    Load true -> #1\n" +
                                    "    CopyValue #1 -> #2\n" +
                                    "    Add #1 + #0 -> #2\n" +
                                    "    Subtract #1 - #0 -> #2\n" +
                                    "    Multiply #1 * #0 -> #2\n" +
                                    "    Divide #1 / #0 -> #2\n" +
                                    "    Modulo #1 % #0 -> #2\n" +
                                    "    ArithmeticNegate #1 -> #2\n" +
                                    "    ShiftLeft #1 << #0 -> #2\n" +
                                    "    ShiftRight #1 >> #0 -> #2\n" +
                                    "    BitwiseAnd #1 & #0 -> #2\n" +
                                    "    BitwiseNot #1 -> #2\n" +
                                    "    BitwiseOr #1 | #0 -> #2\n" +
                                    "    BitwiseXor #1 ^ #0 -> #2\n" +
                                    "    Less #1 < #0 -> #2\n" +
                                    "    LessOrEqual #1 <= #0 -> #2\n" +
                                    "    Equal #1 == #0 -> #2\n" +
                                    "    Return #2\n\n";

            var builder = new StringBuilder();

            MethodDisassembler.DisassembleBody(method, builder);
            Assert.That(builder.ToString().Replace("\r\n", "\n"), Is.EqualTo(expected));
        }
示例#4
0
        public void Graph_with_two_consecutive_basic_blocks_succeeds()
        {
            var graphBuilder  = new BasicBlockGraphBuilder();
            var firstBuilder  = graphBuilder.GetInitialBlockBuilder();
            var secondBuilder = firstBuilder.CreateSuccessorBlock();

            secondBuilder.AppendInstruction(Opcode.Return, 0, 0, 0);

            Assert.That(firstBuilder.Index, Is.EqualTo(0));
            Assert.That(secondBuilder.Index, Is.EqualTo(1));

            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[0].Predecessors, Is.Empty);

            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));
            Assert.That(graph.BasicBlocks[1].AlternativeSuccessor, Is.EqualTo(-1));
            CollectionAssert.AreEqual(new[] { 0 }, graph.BasicBlocks[1].Predecessors);
        }
示例#5
0
        public void Graph_with_out_of_bounds_default_successor_fails(int successor)
        {
            var graphBuilder = new BasicBlockGraphBuilder();
            var blockBuilder = graphBuilder.GetInitialBlockBuilder();

            blockBuilder.SetSuccessor(successor);

            Assert.That(() => graphBuilder.Build(), Throws.InvalidOperationException);
        }
示例#6
0
        public void Branch_without_default_successor_is_invalid()
        {
            var graphBuilder = new BasicBlockGraphBuilder();
            var blockBuilder = graphBuilder.GetInitialBlockBuilder();

            blockBuilder.CreateBranch(0).AppendInstruction(Opcode.Return, 0, 0, 0);

            Assert.That(() => graphBuilder.Build(), Throws.InvalidOperationException);
        }
示例#7
0
        private CompiledMethod?InternalCompile()
        {
            Debug.Assert(_syntaxTree != null);
            Debug.Assert(_syntaxTree.Block != null);
            Debug.Assert(_sourceFilename != null);
            Debug.Assert(_declaration != null);

            _methodInProgress = new CompiledMethod(_declaration.FullName);

            // Create locals for the parameters
            _variableMap.PushScope();
            for (var i = 0; i < _declaration.ParameterTypes.Count; i++)
            {
                var paramSyntax = _syntaxTree.Parameters[i];
                var paramIndex  = _methodInProgress.AddLocal(_declaration.ParameterTypes[i], LocalFlags.Parameter);

                if (!_variableMap.TryAddVariable(paramSyntax.Name, paramIndex))
                {
                    _diagnostics.Add(DiagnosticCode.VariableAlreadyDefined, paramSyntax.Position, paramSyntax.Name);
                    return(null);
                }
            }

            // Compile the body
            var graphBuilder = new BasicBlockGraphBuilder();

            if (!TryCompileBlock(_syntaxTree.Block, graphBuilder.GetInitialBlockBuilder(),
                                 out var finalBlockBuilder, out var returnGuaranteed))
            {
                return(null);
            }

            // ReSharper nullability checker cannot see that the fields weren't changed in TryCompileBlock
            Debug.Assert(_declaration != null);
            Debug.Assert(_methodInProgress != null);

            if (!returnGuaranteed)
            {
                // Void methods may return implicitly
                // Others should fail
                if (_declaration.ReturnType.Equals(SimpleType.Void))
                {
                    var voidIndex = _methodInProgress.AddLocal(SimpleType.Void, LocalFlags.None);
                    finalBlockBuilder.AppendInstruction(Opcode.Return, voidIndex, 0, 0);
                }
                else
                {
                    Debug.Assert(_syntaxTree != null);
                    _diagnostics.Add(DiagnosticCode.ReturnNotGuaranteed, _syntaxTree.Position, _syntaxTree.Name);
                    return(null);
                }
            }

            _methodInProgress.Body = graphBuilder.Build();
            return(_methodInProgress);
        }
示例#8
0
        public void Branch_without_alternative_successor_is_invalid()
        {
            var graphBuilder = new BasicBlockGraphBuilder();
            var blockBuilder = graphBuilder.GetInitialBlockBuilder();

            blockBuilder.AppendInstruction(Opcode.BranchIf, 0, 0, 0);
            blockBuilder.SetSuccessor(0);

            Assert.That(() => graphBuilder.Build(), Throws.InvalidOperationException);
        }
示例#9
0
        public void Graph_with_non_returning_branch_fails()
        {
            var graphBuilder = new BasicBlockGraphBuilder();
            var firstBuilder = graphBuilder.GetInitialBlockBuilder();

            var leftBuilder = firstBuilder.CreateSuccessorBlock();

            leftBuilder.AppendInstruction(Opcode.Return, 0, 0, 0);

            firstBuilder.CreateBranch(7); // This branch has no exit behavior

            Assert.That(() => graphBuilder.Build(), Throws.InvalidOperationException);
        }
示例#10
0
        public void Graph_with_single_infinite_loop_succeeds()
        {
            var graphBuilder = new BasicBlockGraphBuilder();
            var blockBuilder = graphBuilder.GetInitialBlockBuilder();

            blockBuilder.SetSuccessor(0);

            var graph = graphBuilder.Build();

            Assert.That(graph.BasicBlocks, Has.Exactly(1).Items);
            Assert.That(graph.BasicBlocks[0].Instructions, Is.Empty);
            Assert.That(graph.BasicBlocks[0].DefaultSuccessor, Is.EqualTo(0));
            Assert.That(graph.BasicBlocks[0].AlternativeSuccessor, Is.EqualTo(-1));
            CollectionAssert.AreEqual(new[] { 0 }, graph.BasicBlocks[0].Predecessors);
        }
示例#11
0
        public void DisassembleBody_single_basic_block_with_infinite_loop()
        {
            var graphBuilder = new BasicBlockGraphBuilder();

            graphBuilder.GetInitialBlockBuilder().SetSuccessor(0);
            var method = new CompiledMethod("Test::Method")
            {
                Body = graphBuilder.Build()
            };

            const string expected = "BB_0:\n    ==> BB_0\n\n";

            var builder = new StringBuilder();

            MethodDisassembler.DisassembleBody(method, builder);
            Assert.That(builder.ToString().Replace("\r\n", "\n"), Is.EqualTo(expected));
        }
示例#12
0
        public void DisassembleBody_single_basic_block_with_only_return()
        {
            var graphBuilder = new BasicBlockGraphBuilder();

            graphBuilder.GetInitialBlockBuilder().AppendInstruction(Opcode.Return, 2, 0, 0);
            var method = new CompiledMethod("Test::Method")
            {
                Body = graphBuilder.Build()
            };

            const string expected = "BB_0:\n    Return #2\n\n";

            var builder = new StringBuilder();

            MethodDisassembler.DisassembleBody(method, builder);
            Assert.That(builder.ToString().Replace("\r\n", "\n"), Is.EqualTo(expected));
        }
示例#13
0
        public void Graph_with_single_basic_block_succeeds()
        {
            var graphBuilder = new BasicBlockGraphBuilder();
            var blockBuilder = graphBuilder.GetInitialBlockBuilder();

            blockBuilder.AppendInstruction(Opcode.Return, 0, 0, 0);

            Assert.That(blockBuilder.Index, Is.EqualTo(0));

            var graph = graphBuilder.Build();

            Assert.That(graph.BasicBlocks, Has.Exactly(1).Items);
            Assert.That(graph.BasicBlocks[0].Instructions, Has.Exactly(1).Items);
            Assert.That(graph.BasicBlocks[0].Instructions[0].Operation, Is.EqualTo(Opcode.Return));
            Assert.That(graph.BasicBlocks[0].DefaultSuccessor, Is.EqualTo(-1));
            Assert.That(graph.BasicBlocks[0].AlternativeSuccessor, Is.EqualTo(-1));
            Assert.That(graph.BasicBlocks[0].Predecessors, Is.Empty);
        }
示例#14
0
        public void DisassembleBody_two_basic_blocks_making_infinite_loop()
        {
            var graphBuilder = new BasicBlockGraphBuilder();

            graphBuilder.GetInitialBlockBuilder().CreateSuccessorBlock().SetSuccessor(0);
            var method = new CompiledMethod("Test::Method")
            {
                Body = graphBuilder.Build()
            };

            // Fallthrough from BB_0 to BB_1 is not explicitly displayed
            const string expected = "BB_0:\n\nBB_1:\n    ==> BB_0\n\n";

            var builder = new StringBuilder();

            MethodDisassembler.DisassembleBody(method, builder);
            Assert.That(builder.ToString().Replace("\r\n", "\n"), Is.EqualTo(expected));
        }
示例#15
0
        public void DisassembleBody_handles_null_block()
        {
            var graphBuilder        = new BasicBlockGraphBuilder();
            var initialBlockBuilder = graphBuilder.GetInitialBlockBuilder();

            initialBlockBuilder.AppendInstruction(Opcode.Return, 0, 0, 0);
            initialBlockBuilder.CreateSuccessorBlock(); // No reference will be made and this will become null in Build()
            var method = new CompiledMethod("Test::Method")
            {
                Body = graphBuilder.Build()
            };

            const string expected = "BB_0:\n    Return #0\n\n";

            var builder = new StringBuilder();

            MethodDisassembler.DisassembleBody(method, builder);
            Assert.That(builder.ToString().Replace("\r\n", "\n"), Is.EqualTo(expected));
        }
示例#16
0
        public void Phis_are_correctly_added()
        {
            var graphBuilder = new BasicBlockGraphBuilder();
            var blockBuilder = graphBuilder.GetInitialBlockBuilder();

            blockBuilder.AddPhi(4, ImmutableList <int> .Empty.AddRange(new[] { 17, 42 }));
            blockBuilder.AddPhi(3, ImmutableList <int> .Empty.AddRange(new[] { 6, 4 }));
            blockBuilder.AppendInstruction(Opcode.Return, 3, 0, 0);

            var graph = graphBuilder.Build();

            Assert.That(graph.BasicBlocks, Has.Exactly(1).Items);
            Assert.That(graph.BasicBlocks[0].Instructions, Has.Exactly(1).Items);
            Assert.That(graph.BasicBlocks[0].Phis, Has.Exactly(2).Items);
            Assert.That(graph.BasicBlocks[0].Phis[0].Destination, Is.EqualTo(4));
            CollectionAssert.AreEqual(new[] { 17, 42 }, graph.BasicBlocks[0].Phis[0].Operands);
            Assert.That(graph.BasicBlocks[0].Phis[1].Destination, Is.EqualTo(3));
            CollectionAssert.AreEqual(new[] { 6, 4 }, graph.BasicBlocks[0].Phis[1].Operands);
        }
示例#17
0
        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);
        }
示例#18
0
        public void Disassemble_writes_both_values_and_basic_blocks()
        {
            var graphBuilder = new BasicBlockGraphBuilder();

            graphBuilder.GetInitialBlockBuilder().AppendInstruction(Opcode.Return, 0, 0, 0);
            var method = new CompiledMethod("Test::Method")
            {
                Body = graphBuilder.Build()
            };

            method.AddLocal(SimpleType.Int32, LocalFlags.None);
            method.AddLocal(SimpleType.Bool, LocalFlags.Parameter);

            const string expected = "; #0   int32\n" +
                                    "; #1   bool param\n" +
                                    "BB_0:\n" +
                                    "    Return #0\n\n";

            var builder = new StringBuilder();

            MethodDisassembler.Disassemble(method, builder);
            Assert.That(builder.ToString().Replace("\r\n", "\n"), Is.EqualTo(expected));
        }
示例#19
0
        public void DisassembleBody_single_basic_block_with_phis()
        {
            var graphBuilder = new BasicBlockGraphBuilder();
            var blockBuilder = graphBuilder.GetInitialBlockBuilder();

            blockBuilder.AddPhi(7, ImmutableList <int> .Empty.Add(1));
            blockBuilder.AddPhi(12, ImmutableList <int> .Empty.AddRange(new[] { 8, 6, 4 }));
            blockBuilder.AppendInstruction(Opcode.Return, 2, 0, 0);
            var method = new CompiledMethod("Test::Method")
            {
                Body = graphBuilder.Build()
            };

            const string expected = "BB_0:\n" +
                                    "    PHI (#1) -> #7\n" +
                                    "    PHI (#8, #6, #4) -> #12\n" +
                                    "    Return #2\n\n";

            var builder = new StringBuilder();

            MethodDisassembler.DisassembleBody(method, builder);
            Assert.That(builder.ToString().Replace("\r\n", "\n"), Is.EqualTo(expected));
        }
示例#20
0
        public void DisassembleBody_single_basic_block_with_call(MethodCallType callType, string expectedSuffix)
        {
            var graphBuilder = new BasicBlockGraphBuilder();
            var blockBuilder = graphBuilder.GetInitialBlockBuilder();

            blockBuilder.AppendInstruction(Opcode.Call, 0, 0, 1);
            blockBuilder.AppendInstruction(Opcode.Return, 2, 0, 0);
            var method = new CompiledMethod("Test::Method")
            {
                Body = graphBuilder.Build()
            };

            method.AddCallInfo(17, new[] { 3, 6, 9, 12 }, "Test::Callee", callType);

            string expected = "BB_0:\n" +
                              "    Call Test::Callee(#3, #6, #9, #12)" + expectedSuffix + " -> #1\n" +
                              "    Return #2\n\n";

            var builder = new StringBuilder();

            MethodDisassembler.DisassembleBody(method, builder);
            Assert.That(builder.ToString().Replace("\r\n", "\n"), Is.EqualTo(expected));
        }
示例#21
0
        public void Graph_with_branch_and_merge_succeeds()
        {
            var graphBuilder = new BasicBlockGraphBuilder();
            var firstBuilder = graphBuilder.GetInitialBlockBuilder();

            var leftBuilder  = firstBuilder.CreateSuccessorBlock();
            var rightBuilder = firstBuilder.CreateBranch(7);

            var finalBlock = leftBuilder.CreateSuccessorBlock();

            rightBuilder.SetSuccessor(leftBuilder.DefaultSuccessor);
            finalBlock.AppendInstruction(Opcode.Return, 0, 0, 0);

            var graph = graphBuilder.Build();

            Assert.That(graph.BasicBlocks, Has.Exactly(4).Items);
            Assert.That(graph.BasicBlocks[0].Instructions, Has.Exactly(1).Items);
            Assert.That(graph.BasicBlocks[0].Instructions[0].Operation, Is.EqualTo(Opcode.BranchIf));
            Assert.That(graph.BasicBlocks[0].Instructions[0].Left, Is.EqualTo(7));
            Assert.That(graph.BasicBlocks[0].DefaultSuccessor, Is.EqualTo(1));
            Assert.That(graph.BasicBlocks[0].AlternativeSuccessor, Is.EqualTo(2));

            Assert.That(graph.BasicBlocks[1].Instructions, Is.Empty);
            Assert.That(graph.BasicBlocks[1].DefaultSuccessor, Is.EqualTo(3));
            CollectionAssert.AreEqual(new[] { 0 }, graph.BasicBlocks[1].Predecessors);

            Assert.That(graph.BasicBlocks[2].Instructions, Is.Empty);
            Assert.That(graph.BasicBlocks[2].DefaultSuccessor, Is.EqualTo(3));
            CollectionAssert.AreEqual(new[] { 0 }, graph.BasicBlocks[2].Predecessors);

            Assert.That(graph.BasicBlocks[3].Instructions, Has.Exactly(1).Items);
            Assert.That(graph.BasicBlocks[3].Instructions[0].Operation, Is.EqualTo(Opcode.Return));
            Assert.That(graph.BasicBlocks[3].DefaultSuccessor, Is.EqualTo(-1));
            Assert.That(graph.BasicBlocks[3].AlternativeSuccessor, Is.EqualTo(-1));
            CollectionAssert.AreEqual(new[] { 1, 2 }, graph.BasicBlocks[3].Predecessors);
        }
示例#22
0
        public void Empty_graph_fails()
        {
            var graphBuilder = new BasicBlockGraphBuilder();

            Assert.That(() => graphBuilder.Build(), Throws.InvalidOperationException);
        }
示例#23
0
        /// <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);
        }