[LIR] Add support for item-based const string.

Also migrates legacy example tests regalloc, returns and staticfield.

Bug: b/225838009
Bug: b/167145686
Change-Id: If1a173572d003d7ad760b93aaffc74fd732113d5
diff --git a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
index 3a5623d..51182ac 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
@@ -22,6 +22,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.naming.dexitembasedstring.NameComputationInfo;
 
 public class DexItemBasedConstString extends ConstInstruction {
@@ -89,6 +90,11 @@
   }
 
   @Override
+  public void buildLir(LirBuilder<Value, ?> builder) {
+    builder.addDexItemBasedConstString(item, nameComputationInfo);
+  }
+
+  @Override
   public boolean identicalNonValueNonPositionParts(Instruction other) {
     return other.isDexItemBasedConstString()
         && other.asDexItemBasedConstString().item == item
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 d2a3038..abccb80 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -29,6 +30,7 @@
 import com.android.tools.r8.ir.code.ConstString;
 import com.android.tools.r8.ir.code.DebugLocalWrite;
 import com.android.tools.r8.ir.code.DebugPosition;
+import com.android.tools.r8.ir.code.DexItemBasedConstString;
 import com.android.tools.r8.ir.code.Div;
 import com.android.tools.r8.ir.code.Goto;
 import com.android.tools.r8.ir.code.IRCode;
@@ -72,6 +74,7 @@
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
 import com.android.tools.r8.lightir.LirCode.PositionEntry;
+import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
 import com.android.tools.r8.utils.ListUtils;
 import com.google.common.collect.ImmutableList;
 import it.unimi.dsi.fastutil.ints.Int2IntMap;
@@ -426,11 +429,22 @@
 
     @Override
     public void onConstString(DexString string) {
-      Value dest = getOutValueForNextInstruction(TypeElement.stringClassType(appView));
+      Value dest =
+          getOutValueForNextInstruction(
+              TypeElement.stringClassType(appView, Nullability.definitelyNotNull()));
       addInstruction(new ConstString(dest, string));
     }
 
     @Override
+    public void onDexItemBasedConstString(
+        DexReference item, NameComputationInfo<?> nameComputationInfo) {
+      Value dest =
+          getOutValueForNextInstruction(
+              TypeElement.stringClassType(appView, Nullability.definitelyNotNull()));
+      addInstruction(new DexItemBasedConstString(dest, item, nameComputationInfo));
+    }
+
+    @Override
     public void onConstClass(DexType type) {
       Value dest =
           getOutValueForNextInstruction(
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 dd33679..b0334c9 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -33,6 +34,7 @@
 import com.android.tools.r8.lightir.LirCode.DebugLocalInfoTable;
 import com.android.tools.r8.lightir.LirCode.PositionEntry;
 import com.android.tools.r8.lightir.LirCode.TryCatchTable;
+import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
 import com.android.tools.r8.utils.ListUtils;
 import com.google.common.collect.ImmutableList;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -125,6 +127,14 @@
     }
   }
 
+  public static class NameComputationPayload extends InstructionPayload {
+    public final NameComputationInfo<?> nameComputationInfo;
+
+    public NameComputationPayload(NameComputationInfo<?> nameComputationInfo) {
+      this.nameComputationInfo = nameComputationInfo;
+    }
+  }
+
   public LirBuilder(DexMethod method, LirEncodingStrategy<V, EV> strategy, DexItemFactory factory) {
     this.factory = factory;
     constants = new Reference2IntOpenHashMap<>();
@@ -772,4 +782,11 @@
     return addInstructionTemplate(
         opcode, Collections.emptyList(), ImmutableList.of(array, index, value));
   }
+
+  public LirBuilder<V, EV> addDexItemBasedConstString(
+      DexReference item, NameComputationInfo<?> nameComputationInfo) {
+    NameComputationPayload payload = new NameComputationPayload(nameComputationInfo);
+    return addInstructionTemplate(
+        LirOpcodes.ITEMBASEDCONSTSTRING, ImmutableList.of(item, payload), Collections.emptyList());
+  }
 }
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 c4f2076..e4111ff 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
@@ -196,6 +196,7 @@
   int DEBUGLOCALWRITE = 213;
   int INVOKENEWARRAY = 214;
   int NEWARRAYFILLEDDATA = 215;
+  int ITEMBASEDCONSTSTRING = 216;
 
   static String toString(int opcode) {
     switch (opcode) {
@@ -512,6 +513,8 @@
         return "INVOKENEWARRAY";
       case NEWARRAYFILLEDDATA:
         return "NEWARRAYFILLEDDATA";
+      case ITEMBASEDCONSTSTRING:
+        return "ITEMBASEDCONSTSTRING";
 
       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 e7733fc..8277988 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.IfType;
@@ -16,6 +17,8 @@
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.lightir.LirBuilder.FillArrayPayload;
 import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
+import com.android.tools.r8.lightir.LirBuilder.NameComputationPayload;
+import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -87,6 +90,11 @@
     onInstruction();
   }
 
+  public void onDexItemBasedConstString(
+      DexReference item, NameComputationInfo<?> nameComputationInfo) {
+    onInstruction();
+  }
+
   public void onConstClass(DexType type) {
     onInstruction();
   }
@@ -1043,6 +1051,14 @@
           onCmpInstruction(opcode, leftValue, rightValue);
           return;
         }
+      case LirOpcodes.ITEMBASEDCONSTSTRING:
+        {
+          DexReference item = (DexReference) getConstantItem(view.getNextConstantOperand());
+          NameComputationPayload payload =
+              (NameComputationPayload) getConstantItem(view.getNextConstantOperand());
+          onDexItemBasedConstString(item, payload.nameComputationInfo);
+          return;
+        }
       default:
         throw new Unimplemented("No dispatch for opcode " + LirOpcodes.toString(opcode));
     }
diff --git a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
index d30288b..7aaf668 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
@@ -6,12 +6,14 @@
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.IfType;
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
+import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
 import com.android.tools.r8.utils.StringUtils;
 import java.util.Arrays;
 import java.util.List;
@@ -151,6 +153,12 @@
   }
 
   @Override
+  public void onDexItemBasedConstString(
+      DexReference item, NameComputationInfo<?> nameComputationInfo) {
+    appendOutValue().append("item(").append(item).append(")");
+  }
+
+  @Override
   public void onConstClass(DexType type) {
     appendOutValue().append("class(").append(type).append(")");
   }
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 44bf2f7..a303b8c 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -27,9 +27,6 @@
     String[] tests = {
       "arithmetic.Arithmetic",
       "inlining.Inlining",
-      "regalloc.RegAlloc",
-      "returns.Returns",
-      "staticfield.StaticField",
       "stringbuilding.StringBuilding",
       "switches.Switches",
       "sync.Sync",
diff --git a/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java b/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java
index 3e2ae80..c870a8e 100644
--- a/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java
@@ -60,22 +60,6 @@
   }
 
   @Test
-  public void testRegAlloc() throws Exception {
-    testDebugging("regalloc", "RegAlloc");
-  }
-
-  @Test
-  public void testReturns() throws Exception {
-    testDebugging("returns", "Returns");
-  }
-
-  @Test
-  public void testStaticField() throws Exception {
-    // TODO(b/79671093): D8 has different line number info during stepping.
-    testDebuggingJvmOnly("staticfield", "StaticField");
-  }
-
-  @Test
   public void testStringBuilding() throws Exception {
     testDebugging("stringbuilding", "StringBuilding");
   }
diff --git a/src/test/examples/regalloc/RegAlloc.java b/src/test/java/com/android/tools/r8/examples/regalloc/RegAlloc.java
similarity index 99%
rename from src/test/examples/regalloc/RegAlloc.java
rename to src/test/java/com/android/tools/r8/examples/regalloc/RegAlloc.java
index 68ad054..0b7474c 100644
--- a/src/test/examples/regalloc/RegAlloc.java
+++ b/src/test/java/com/android/tools/r8/examples/regalloc/RegAlloc.java
@@ -5,7 +5,7 @@
 // This code is not run directly. It needs to be compiled to dex code.
 // 'regalloc.dex' is what is run.
 
-package regalloc;
+package com.android.tools.r8.examples.regalloc;
 
 // Various test cases that are challenging for the register allocator.
 public class RegAlloc {
diff --git a/src/test/java/com/android/tools/r8/examples/regalloc/RegAllocTestRunner.java b/src/test/java/com/android/tools/r8/examples/regalloc/RegAllocTestRunner.java
new file mode 100644
index 0000000..df0c295
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regalloc/RegAllocTestRunner.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2023, 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.examples.regalloc;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.examples.regalloc.RegAlloc.BoxedInteger;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RegAllocTestRunner extends ExamplesTestBase {
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+  }
+
+  public RegAllocTestRunner(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  public Class<?> getMainClass() {
+    return RegAlloc.class;
+  }
+
+  @Override
+  public List<Class<?>> getTestClasses() throws Exception {
+    return ImmutableList.of(getMainClass(), BoxedInteger.class);
+  }
+
+  @Override
+  public String getExpected() {
+    return StringUtils.lines(
+        "binaryOpUsingHighRegistersArguments: 17016 257 507",
+        "binaryDoubleOpUsingHighRegistersArguments: 251.0 125.0",
+        "binaryOpUsingHighRegistersLocals 518",
+        "instance get many registers42",
+        "sum: 33670",
+        "binaryOpUsingHighRegistersLocals 518.0",
+        "sum: 33670.0");
+  }
+
+  @Test
+  public void testDesugaring() throws Exception {
+    runTestDesugaring();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    runTestR8();
+  }
+
+  @Test
+  public void testDebug() throws Exception {
+    runTestDebugComparator();
+  }
+}
diff --git a/src/test/examples/returns/Returns.java b/src/test/java/com/android/tools/r8/examples/returns/Returns.java
similarity index 92%
rename from src/test/examples/returns/Returns.java
rename to src/test/java/com/android/tools/r8/examples/returns/Returns.java
index c496ce5..f3955df 100644
--- a/src/test/examples/returns/Returns.java
+++ b/src/test/java/com/android/tools/r8/examples/returns/Returns.java
@@ -2,7 +2,7 @@
 // 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 returns;
+package com.android.tools.r8.examples.returns;
 
 public class  Returns {
   public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/examples/returns/ReturnsTestRunner.java b/src/test/java/com/android/tools/r8/examples/returns/ReturnsTestRunner.java
new file mode 100644
index 0000000..1a18ae6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/returns/ReturnsTestRunner.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2023, 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.examples.returns;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ReturnsTestRunner extends ExamplesTestBase {
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+  }
+
+  public ReturnsTestRunner(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  public Class<?> getMainClass() {
+    return Returns.class;
+  }
+
+  @Override
+  public String getExpected() {
+    return "";
+  }
+
+  @Test
+  public void testDesugaring() throws Exception {
+    runTestDesugaring();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    runTestR8();
+  }
+
+  @Test
+  public void testDebug() throws Exception {
+    runTestDebugComparator();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/examples/returns/StaticFieldTestRunner.java b/src/test/java/com/android/tools/r8/examples/returns/StaticFieldTestRunner.java
new file mode 100644
index 0000000..81ca334
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/returns/StaticFieldTestRunner.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2023, 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.examples.returns;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.examples.staticfield.StaticField;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StaticFieldTestRunner extends ExamplesTestBase {
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+  }
+
+  public StaticFieldTestRunner(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  public Class<?> getMainClass() {
+    return StaticField.class;
+  }
+
+  @Override
+  public String getExpected() {
+    return StringUtils.lines("101010", "101010", "ABC", "ABC");
+  }
+
+  @Test
+  public void testDesugaring() throws Exception {
+    runTestDesugaring();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    runTestR8();
+  }
+
+  @Test
+  public void testDebug() throws Exception {
+    // TODO(b/79671093): DEX has different line number info during stepping.
+    Assume.assumeTrue(parameters.isCfRuntime());
+    runTestDebugComparator();
+  }
+}
diff --git a/src/test/examples/staticfield/StaticField.java b/src/test/java/com/android/tools/r8/examples/staticfield/StaticField.java
similarity index 95%
rename from src/test/examples/staticfield/StaticField.java
rename to src/test/java/com/android/tools/r8/examples/staticfield/StaticField.java
index f4d5854..5c48a2e 100644
--- a/src/test/examples/staticfield/StaticField.java
+++ b/src/test/java/com/android/tools/r8/examples/staticfield/StaticField.java
@@ -2,7 +2,7 @@
 // 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 staticfield;
+package com.android.tools.r8.examples.staticfield;
 
 public class StaticField {