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);
        }
Example #3
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);
        }