Restore one-pass LIR->IR conversion.

Bug: b/225838009
Change-Id: Iebcd0b7fb54c97af3484576e0fd2252a6bff82ed
diff --git a/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java b/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
index 9908098..77533c9 100644
--- a/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
@@ -71,7 +71,16 @@
       while (it.hasNext()) {
         Instruction instruction = it.next();
         builder.setCurrentPosition(instruction.getPosition());
-        instruction.buildLIR(builder);
+        if (instruction.isGoto()) {
+          BasicBlock nextBlock = blockIt.peekNext();
+          if (instruction.asGoto().getTarget() == nextBlock) {
+            builder.addFallthrough();
+          } else {
+            instruction.buildLIR(builder);
+          }
+        } else {
+          instruction.buildLIR(builder);
+        }
       }
     }
     return builder.build();
diff --git a/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
index efccdb9..a726649 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
@@ -48,7 +48,6 @@
 
   public static IRCode translate(ProgramMethod method, LIRCode lirCode, AppView<?> appView) {
     Parser parser = new Parser(lirCode, method.getReference(), appView);
-    parser.computeControlFlowGraph();
     parser.parseArguments(method);
     lirCode.forEach(view -> view.accept(parser));
     return parser.getIRCode(method);
@@ -93,12 +92,13 @@
     }
 
     private void ensureCurrentBlock() {
-      BasicBlock nextBlock = blocks.get(nextInstructionIndex);
-      if (nextBlock != null) {
-        assert currentBlock != nextBlock;
-        currentBlock = nextBlock;
+      // Control instructions must close the block, thus the current block is null iff the
+      // instruction denotes a new block.
+      if (currentBlock == null) {
+        currentBlock = blocks.computeIfAbsent(nextInstructionIndex, k -> new BasicBlock());
+      } else {
+        assert !blocks.containsKey(nextInstructionIndex);
       }
-      assert currentBlock != null;
     }
 
     private void ensureCurrentPosition() {
@@ -116,34 +116,8 @@
               : null;
     }
 
-    public void computeControlFlowGraph() {
-      // TODO(b/225838009): Since each basic block has a terminal instruction we can compute block
-      //  structure lazily while iterating the instructions and avoid this additional pass.
-      // Always create an entry block as it cannot be targeted from code.
-      blocks.put(ENTRY_BLOCK_INDEX, createBlock());
-      for (LIRInstructionView view : code) {
-        int opcode = view.getOpcode();
-        if (LIROpcodes.isControlFlowInstruction(opcode)) {
-          // TODO(b/225838009): Deal with multi-target instructions.
-          int target = view.getNextBlockOperand();
-          blocks.computeIfAbsent(target, k -> createBlock());
-          if (LIROpcodes.isControlFlowInstructionWithFallthrough(opcode)) {
-            blocks.computeIfAbsent(view.getInstructionIndex() + 1, k -> createBlock());
-          }
-        }
-      }
-    }
-
-    public BasicBlock createBlock() {
-      BasicBlock block = new BasicBlock();
-      block.setNumber(basicBlockNumberGenerator.next());
-      // The LIR is in SSA with accurate phis. The blocks are thus filled by construction.
-      block.setFilled();
-      return block;
-    }
-
     public void parseArguments(ProgramMethod method) {
-      currentBlock = blocks.get(ENTRY_BLOCK_INDEX);
+      currentBlock = getBasicBlock(ENTRY_BLOCK_INDEX);
       boolean hasReceiverArgument = !method.getDefinition().isStatic();
       assert code.getArgumentCount()
           == method.getParameters().size() + (hasReceiverArgument ? 1 : 0);
@@ -159,13 +133,13 @@
     }
 
     public IRCode getIRCode(ProgramMethod method) {
-      // TODO(b/225838009): Support control flow.
-      currentBlock.setFilled();
       LinkedList<BasicBlock> blockList = new LinkedList<>();
       IntList blockIndices = new IntArrayList(blocks.keySet());
       blockIndices.sort(Integer::compare);
       for (int i = 0; i < blockIndices.size(); i++) {
-        blockList.add(blocks.get(blockIndices.getInt(i)));
+        BasicBlock block = blocks.get(blockIndices.getInt(i));
+        block.setFilled();
+        blockList.add(block);
       }
       return new IRCode(
           appView.options(),
@@ -180,9 +154,13 @@
     }
 
     public BasicBlock getBasicBlock(int instructionIndex) {
-      BasicBlock block = blocks.get(instructionIndex);
-      assert block != null;
-      return block;
+      return blocks.computeIfAbsent(
+          instructionIndex,
+          k -> {
+            BasicBlock block = new BasicBlock();
+            block.setNumber(basicBlockNumberGenerator.next());
+            return block;
+          });
     }
 
     public Value getValue(int index) {
@@ -301,6 +279,12 @@
     }
 
     @Override
+    public void onFallthrough() {
+      int nextBlockIndex = peekNextInstructionIndex() + 1;
+      onGoto(nextBlockIndex);
+    }
+
+    @Override
     public void onGoto(int blockIndex) {
       BasicBlock targetBlock = getBasicBlock(blockIndex);
       addInstruction(new Goto());
@@ -341,6 +325,7 @@
     @Override
     public void onReturnVoid() {
       addInstruction(new Return());
+      closeCurrentBlock();
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java b/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
index 409f02e..927d3bf 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
@@ -241,6 +241,10 @@
     return addNoOperandInstruction(LIROpcodes.DEBUGPOS);
   }
 
+  public void addFallthrough() {
+    addNoOperandInstruction(LIROpcodes.FALLTHROUGH);
+  }
+
   public LIRBuilder<V, B> addGoto(B target) {
     int targetIndex = getBlockIndex(target);
     int operandSize = blockIndexSize(targetIndex);
diff --git a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java b/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
index 7e0c520..5e9235f 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
@@ -14,26 +14,7 @@
 
   static boolean isOneByteInstruction(int opcode) {
     assert opcode >= ACONST_NULL;
-    return opcode <= DCONST_1 || opcode == RETURN || opcode == DEBUGPOS;
-  }
-
-  static boolean isControlFlowInstruction(int opcode) {
-    return opcode == GOTO || isControlFlowInstructionWithFallthrough(opcode);
-  }
-
-  static boolean isControlFlowInstructionWithFallthrough(int opcode) {
-    switch (opcode) {
-      case IFEQ:
-      case IFNE:
-      case IFLT:
-      case IFGE:
-      case IFGT:
-      case IFLE:
-        // TODO(b/225838009): put in the rest!
-        return true;
-      default:
-        return false;
-    }
+    return opcode <= DCONST_1 || opcode == RETURN || opcode == DEBUGPOS || opcode == FALLTHROUGH;
   }
 
   // Instructions maintaining the same opcode as defined in CF.
@@ -203,6 +184,7 @@
   int INVOKEDIRECT = 204;
   int DEBUGPOS = 205;
   int PHI = 206;
+  int FALLTHROUGH = 207;
 
   static String toString(int opcode) {
     switch (opcode) {
@@ -511,6 +493,8 @@
         return "DEBUGPOS";
       case PHI:
         return "PHI";
+      case FALLTHROUGH:
+        return "FALLTHROUGH";
 
       default:
         throw new Unreachable("Unexpected LIR opcode: " + opcode);
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
index 8f32cf5..ed822c9 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
@@ -40,6 +40,8 @@
 
   public void onGoto(int blockIndex) {}
 
+  public void onFallthrough() {}
+
   public void onInvokeMethodInstruction(DexMethod method, IntList arguments) {}
 
   public void onInvokeDirect(DexMethod method, IntList arguments) {
@@ -144,6 +146,11 @@
           onPhi(type, operands);
           break;
         }
+      case LIROpcodes.FALLTHROUGH:
+        {
+          onFallthrough();
+          break;
+        }
       default:
         throw new Unimplemented("No dispatch for opcode " + LIROpcodes.toString(view.getOpcode()));
     }