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); }
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)); }
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)); }
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); }
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); }
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); }
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); }
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); }
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); }
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); }
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)); }
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)); }
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); }
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)); }
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)); }
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); }
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); }
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)); }
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)); }
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)); }
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); }
public void Empty_graph_fails() { var graphBuilder = new BasicBlockGraphBuilder(); Assert.That(() => graphBuilder.Build(), Throws.InvalidOperationException); }
/// <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); }