Move LIR/IR conversion into IRConverter.

Bug: b/225838009
Change-Id: I390238f99425a4e966fe3e214adf916ad3fd85d9
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index ff70fed..25d775a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -28,6 +28,7 @@
 import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LIRBuilder;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.List;
 
@@ -215,6 +216,11 @@
     return LibraryMethodReadSetModeling.getModeledReadSetOrUnknown(appView, this);
   }
 
+  @Override
+  public void buildLIR(LIRBuilder<Value> builder) {
+    builder.addInvokeDirect(getInvokedMethod(), arguments());
+  }
+
   public static class Builder extends InvokeMethod.Builder<Builder, InvokeDirect> {
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 33f3f28..1dc485b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -81,6 +81,9 @@
 import com.android.tools.r8.ir.optimize.outliner.Outliner;
 import com.android.tools.r8.ir.optimize.string.StringBuilderAppendOptimizer;
 import com.android.tools.r8.ir.optimize.string.StringOptimizer;
+import com.android.tools.r8.lightir.IR2LIRConverter;
+import com.android.tools.r8.lightir.LIR2IRBuilder;
+import com.android.tools.r8.lightir.LIRCode;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.naming.IdentifierNameStringMarker;
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
@@ -1611,6 +1614,9 @@
       OptimizationFeedback feedback,
       BytecodeMetadataProvider bytecodeMetadataProvider,
       Timing timing) {
+    if (options.testing.roundtripThroughLIR) {
+      code = roundtripThroughLIR(code, feedback, bytecodeMetadataProvider, timing);
+    }
     if (options.isGeneratingClassFiles()) {
       finalizeToCf(code, feedback, bytecodeMetadataProvider, timing);
     } else {
@@ -1619,6 +1625,16 @@
     }
   }
 
+  private IRCode roundtripThroughLIR(
+      IRCode code,
+      OptimizationFeedback feedback,
+      BytecodeMetadataProvider bytecodeMetadataProvider,
+      Timing timing) {
+    LIRCode lirCode = IR2LIRConverter.translate(code);
+    IRCode irCode = LIR2IRBuilder.translate(code.context(), lirCode, appView);
+    return irCode;
+  }
+
   private void finalizeToCf(
       IRCode code,
       OptimizationFeedback feedback,
diff --git a/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java b/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
new file mode 100644
index 0000000..f4a2e5f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.lightir;
+
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.Value;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+
+public class IR2LIRConverter {
+
+  public static LIRCode translate(IRCode irCode) {
+    Reference2IntMap<Value> values = new Reference2IntOpenHashMap<>();
+    int index = 0;
+    for (Instruction instruction : irCode.instructions()) {
+      if (instruction.hasOutValue()) {
+        values.put(instruction.outValue(), index);
+      }
+      index++;
+    }
+    LIRBuilder<Value> builder =
+        new LIRBuilder<Value>(values::getInt).setMetadata(irCode.metadata());
+    BasicBlockIterator blockIt = irCode.listIterator();
+    while (blockIt.hasNext()) {
+      BasicBlock block = blockIt.next();
+      // TODO(b/225838009): Support control flow.
+      assert !block.hasPhis();
+      InstructionIterator it = block.iterator();
+      while (it.hasNext()) {
+        Instruction instruction = it.next();
+        instruction.buildLIR(builder);
+      }
+    }
+    return builder.build();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIR2IRBuilder.java b/src/main/java/com/android/tools/r8/lightir/LIR2IRBuilder.java
index 76f1040..3fb39c8 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIR2IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIR2IRBuilder.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.ir.code.ConstString;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.InvokeVirtual;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
@@ -31,13 +32,7 @@
 
 public class LIR2IRBuilder {
 
-  private final AppView<?> appView;
-
-  public LIR2IRBuilder(AppView<?> appView) {
-    this.appView = appView;
-  }
-
-  public IRCode translate(ProgramMethod method, LIRCode lirCode) {
+  public static IRCode translate(ProgramMethod method, LIRCode lirCode, AppView<?> appView) {
     Parser parser = new Parser(lirCode, appView);
     parser.parseArguments(method);
     lirCode.forEach(view -> view.accept(parser));
@@ -170,6 +165,15 @@
     }
 
     @Override
+    public void onInvokeDirect(DexMethod target, IntList arguments) {
+      // TODO(b/225838009): Maintain is-interface bit.
+      Value dest = getInvokeInstructionOutputValue(target);
+      List<Value> ssaArgumentValues = getSsaValues(arguments);
+      InvokeDirect instruction = new InvokeDirect(target, dest, ssaArgumentValues);
+      addInstruction(instruction);
+    }
+
+    @Override
     public void onInvokeVirtual(DexMethod target, IntList arguments) {
       // TODO(b/225838009): Maintain is-interface bit.
       Value dest = getInvokeInstructionOutputValue(target);
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 06e642b..7d3505c 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
@@ -115,7 +115,7 @@
     return writeConstantReferencingInstruction(LIROpcodes.GETSTATIC, field);
   }
 
-  public LIRBuilder<V> addInvokeVirtual(DexMethod method, List<V> arguments) {
+  public LIRBuilder<V> addInvokeInstruction(int opcode, DexMethod method, List<V> arguments) {
     instructionCount++;
     int argumentOprandSize = constantIndexSize(method);
     int[] argumentIndexes = new int[arguments.size()];
@@ -125,7 +125,7 @@
       argumentIndexes[i++] = argumentIndex;
       argumentOprandSize += valueIndexSize(argumentIndex);
     }
-    writer.writeInstruction(LIROpcodes.INVOKEVIRTUAL, argumentOprandSize);
+    writer.writeInstruction(opcode, argumentOprandSize);
     writeConstantIndex(method);
     for (int argumentIndex : argumentIndexes) {
       writeValueIndex(argumentIndex);
@@ -133,6 +133,14 @@
     return this;
   }
 
+  public LIRBuilder<V> addInvokeDirect(DexMethod method, List<V> arguments) {
+    return addInvokeInstruction(LIROpcodes.INVOKEDIRECT, method, arguments);
+  }
+
+  public LIRBuilder<V> addInvokeVirtual(DexMethod method, List<V> arguments) {
+    return addInvokeInstruction(LIROpcodes.INVOKEVIRTUAL, method, arguments);
+  }
+
   public LIRBuilder<V> addReturn(V value) {
     return this;
   }
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 2141611..6d906ed 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
@@ -181,6 +181,7 @@
   int LCONST = 201;
   int FCONST = 202;
   int DCONST = 203;
+  int INVOKEDIRECT = 204;
 
   static String toString(int opcode) {
     switch (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 8ffc285..a0afc52 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
@@ -36,6 +36,10 @@
 
   public void onInvokeMethodInstruction(DexMethod method, IntList arguments) {}
 
+  public void onInvokeDirect(DexMethod method, IntList arguments) {
+    onInvokeMethodInstruction(method, arguments);
+  }
+
   public void onInvokeVirtual(DexMethod method, IntList arguments) {
     onInvokeMethodInstruction(method, arguments);
   }
@@ -70,13 +74,17 @@
           }
           break;
         }
+      case LIROpcodes.INVOKEDIRECT:
+        {
+          DexMethod target = getInvokeInstructionTarget(view);
+          IntList arguments = getInvokeInstructionArguments(view);
+          onInvokeDirect(target, arguments);
+          break;
+        }
       case LIROpcodes.INVOKEVIRTUAL:
         {
-          DexMethod target = (DexMethod) getConstantItem(view.getNextConstantOperand());
-          IntList arguments = new IntArrayList();
-          while (view.hasMoreOperands()) {
-            arguments.add(view.getNextValueOperand());
-          }
+          DexMethod target = getInvokeInstructionTarget(view);
+          IntList arguments = getInvokeInstructionArguments(view);
           onInvokeVirtual(target, arguments);
           break;
         }
@@ -95,4 +103,16 @@
         throw new Unimplemented("No dispatch for opcode " + LIROpcodes.toString(view.getOpcode()));
     }
   }
+
+  private DexMethod getInvokeInstructionTarget(LIRInstructionView view) {
+    return (DexMethod) getConstantItem(view.getNextConstantOperand());
+  }
+
+  private IntList getInvokeInstructionArguments(LIRInstructionView view) {
+    IntList arguments = new IntArrayList();
+    while (view.hasMoreOperands()) {
+      arguments.add(view.getNextValueOperand());
+    }
+    return arguments;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index d784613..f02405c 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1793,6 +1793,7 @@
   public static class TestingOptions {
 
     public boolean neverReuseCfLocalRegisters = false;
+    public boolean roundtripThroughLIR = false;
     private boolean hasReadCheckDeterminism = false;
     private DeterminismChecker determinismChecker = null;
 
diff --git a/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java b/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
index 6bf8f98..82c0d55 100644
--- a/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
+++ b/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
@@ -3,45 +3,11 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.lightir;
 
-import com.android.tools.r8.ClassFileConsumer;
-import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
+import static org.junit.Assume.assumeTrue;
+
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.dex.Marker;
-import com.android.tools.r8.dex.Marker.Tool;
-import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.ClassKind;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.JarApplicationReader;
-import com.android.tools.r8.graph.JarClassFileReader;
-import com.android.tools.r8.graph.LazyLoadedDexApplication.Builder;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlockIterator;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionIterator;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.CfBuilder;
-import com.android.tools.r8.ir.optimize.CodeRewriter;
-import com.android.tools.r8.ir.optimize.DeadCodeRemover;
-import com.android.tools.r8.jar.CfApplicationWriter;
-import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.Timing;
-import it.unimi.dsi.fastutil.objects.Reference2IntMap;
-import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
-import java.nio.file.Path;
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -57,47 +23,18 @@
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withDefaultCfRuntime().build();
+    return getTestParameters().withDefaultRuntimes().build();
   }
 
   private final TestParameters parameters;
-  private AppView<?> appView;
-  private ProgramMethod method;
 
   public LIRRoundtripTest(TestParameters parameters) {
     this.parameters = parameters;
   }
 
-  @Before
-  public void setUp() throws Exception {
-    DexItemFactory factory = new DexItemFactory();
-    InternalOptions options = new InternalOptions(factory, new Reporter());
-    options.programConsumer = ClassFileConsumer.emptyConsumer();
-    Builder builder = DexApplication.builder(options, Timing.empty());
-    JarClassFileReader<DexProgramClass> reader =
-        new JarClassFileReader<>(
-            new JarApplicationReader(options),
-            clazz -> {
-              builder.addProgramClass(clazz);
-              clazz
-                  .programMethods()
-                  .forEach(
-                      m -> {
-                        if (m.getReference().qualifiedName().endsWith("main")) {
-                          method = m;
-                        }
-                      });
-            },
-            ClassKind.PROGRAM);
-    reader.read(Origin.unknown(), ToolHelper.getClassAsBytes(TestClass.class));
-    appView =
-        AppView.createForD8(
-            AppInfo.createInitialAppInfo(
-                builder.build(), GlobalSyntheticsStrategy.forNonSynthesizing()));
-  }
-
   @Test
   public void testReference() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
     testForJvm()
         .addProgramClasses(TestClass.class)
         .run(parameters.getRuntime(), TestClass.class)
@@ -106,66 +43,15 @@
 
   @Test
   public void testRoundtrip() throws Exception {
-    IRCode irCode1 = translateCf2IR();
-    LIRCode lirCode = translateIR2LIR(irCode1);
-    IRCode irCode2 = translateLIR2IR(lirCode);
-    translateIR2Cf(irCode2);
-    Path out = writeToFile();
-    testForJvm()
-        .addProgramFiles(out)
+    testForD8(parameters.getBackend())
+        .release()
+        .addProgramClasses(TestClass.class)
+        .addOptionsModification(
+            o -> {
+              o.testing.forceIRForCfToCfDesugar = true;
+              o.testing.roundtripThroughLIR = true;
+            })
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutputLines("Hello, world!");
   }
-
-  private Path writeToFile() {
-    Path out = temp.getRoot().toPath().resolve("out.jar");
-    Marker fakeMarker = new Marker(Tool.D8);
-    ArchiveConsumer consumer = new ArchiveConsumer(out);
-    new CfApplicationWriter(appView, fakeMarker).write(consumer);
-    consumer.finished(appView.reporter());
-    return out;
-  }
-
-  private IRCode translateCf2IR() {
-    CfCode cfCode = method.getDefinition().getCode().asCfCode();
-    return cfCode.buildIR(method, appView, Origin.unknown());
-  }
-
-  private LIRCode translateIR2LIR(IRCode irCode) {
-    Reference2IntMap<Value> values = new Reference2IntOpenHashMap<>();
-    int index = 0;
-    for (Instruction instruction : irCode.instructions()) {
-      if (instruction.hasOutValue()) {
-        values.put(instruction.outValue(), index);
-      }
-      index++;
-    }
-    LIRBuilder<Value> builder =
-        new LIRBuilder<Value>(values::getInt).setMetadata(irCode.metadata());
-    BasicBlockIterator blockIt = irCode.listIterator();
-    while (blockIt.hasNext()) {
-      BasicBlock block = blockIt.next();
-      // TODO(b/225838009): Support control flow.
-      assert !block.hasPhis();
-      InstructionIterator it = block.iterator();
-      while (it.hasNext()) {
-        Instruction instruction = it.next();
-        instruction.buildLIR(builder);
-      }
-    }
-    return builder.build();
-  }
-
-  private IRCode translateLIR2IR(LIRCode lirCode) {
-    return new LIR2IRBuilder(appView).translate(method, lirCode);
-  }
-
-  private void translateIR2Cf(IRCode irCode) {
-    CodeRewriter codeRewriter = new CodeRewriter(appView);
-    DeadCodeRemover deadCodeRemover = new DeadCodeRemover(appView, codeRewriter);
-    CfCode cfCode =
-        new CfBuilder(appView, method, irCode, BytecodeMetadataProvider.empty())
-            .build(deadCodeRemover);
-    method.setCode(cfCode, appView);
-  }
 }