[LIR] Add xor and array get and put instructions.

Bug: b/225838009
Change-Id: I653a6b1ac6724d64d463c08eb5a5419c62917742
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
index 32afbc5..a173410 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
@@ -106,6 +106,10 @@
   }
 
   public int getAsmOpcode() {
+    return getAsmOpcode(opcode, type);
+  }
+
+  public static int getAsmOpcode(Opcode opcode, NumericType type) {
     switch (opcode) {
       case Shl:
         return type.isWide() ? Opcodes.LSHL : Opcodes.ISHL;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
index 2efe2c3..ca151c5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -345,7 +345,7 @@
         Instruction materializingInstruction = objects.get(i).buildIR(appView, code);
         instructionIterator.add(materializingInstruction);
         instructionIterator.add(
-            new ArrayPut(
+            ArrayPut.create(
                 MemberType.OBJECT,
                 newObjectsValue,
                 indexValue,
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
index 2bfd3c3..ab357ed 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -31,6 +31,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.lightir.LirBuilder;
 import java.util.Arrays;
 import java.util.Set;
 
@@ -286,4 +287,14 @@
     int index = index().getConstInstruction().asConstNumber().getIntValue();
     return newArraySize <= 0 || index < 0 || newArraySize <= index;
   }
+
+  @Override
+  public void buildLir(LirBuilder<Value, ?> builder) {
+    if (getMemberType().isObject()) {
+      DexType destType = dest().getType().asReferenceType().toDexType(builder.factory());
+      builder.addArrayGetObject(destType, array(), index());
+    } else {
+      builder.addArrayGetPrimitive(getMemberType(), array(), index());
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index 5f07431..eff1adc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.lightir.LirBuilder;
 import java.util.Arrays;
 
 public class ArrayPut extends ArrayAccess {
@@ -36,14 +37,29 @@
 
   private MemberType type;
 
-  public ArrayPut(MemberType type, Value array, Value index, Value value) {
+  public static ArrayPut create(MemberType type, Value array, Value index, Value value) {
+    ArrayPut put = new ArrayPut(type, array, index, value);
+    assert put.verify();
+    return put;
+  }
+
+  public static ArrayPut createWithoutVerification(
+      MemberType type, Value array, Value index, Value value) {
+    return new ArrayPut(type, array, index, value);
+  }
+
+  private ArrayPut(MemberType type, Value array, Value index, Value value) {
     super(null, Arrays.asList(array, index, value));
-    assert type != null;
-    assert array.verifyCompatible(ValueType.OBJECT);
-    assert index.verifyCompatible(ValueType.INT);
     this.type = type;
   }
 
+  private boolean verify() {
+    assert type != null;
+    assert array().verifyCompatible(ValueType.OBJECT);
+    assert index().verifyCompatible(ValueType.INT);
+    return true;
+  }
+
   @Override
   public int opcode() {
     return Opcodes.ARRAY_PUT;
@@ -251,6 +267,11 @@
 
   @Override
   public ArrayAccess withMemberType(MemberType newMemberType) {
-    return new ArrayPut(newMemberType, array(), index(), value());
+    return ArrayPut.create(newMemberType, array(), index(), value());
+  }
+
+  @Override
+  public void buildLir(LirBuilder<Value, ?> builder) {
+    builder.addArrayPut(getMemberType(), array(), index(), value());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
index cfc2ee7..5457a36 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.lightir.LirBuilder;
 import java.util.function.Function;
 
 public abstract class LogicalBinop extends Binop {
@@ -143,4 +144,9 @@
   public void buildCf(CfBuilder builder) {
     builder.add(new CfLogicalBinop(getCfOpcode(), type), this);
   }
+
+  @Override
+  public void buildLir(LirBuilder<Value, ?> builder) {
+    builder.addLogicalBinop(getCfOpcode(), type, leftValue(), rightValue());
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index e28dba3..cf7a34f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -1158,7 +1158,7 @@
     Value inValue = readRegister(value, ValueTypeConstraint.fromMemberType(type));
     Value inArray = readRegister(array, ValueTypeConstraint.OBJECT);
     Value inIndex = readRegister(index, ValueTypeConstraint.INT);
-    ArrayPut instruction = new ArrayPut(type, inArray, inIndex, inValue);
+    ArrayPut instruction = ArrayPut.create(type, inArray, inIndex, inValue);
     if (!type.isPrecise()) {
       addImpreciseInstruction(instruction);
     }
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 fc8d1f2..a5417c6 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
@@ -1041,6 +1041,7 @@
       OptimizationFeedback feedback,
       BytecodeMetadataProvider bytecodeMetadataProvider,
       Timing timing) {
+    IRCode oldCode = code;
     if (options.testing.roundtripThroughLir) {
       code = roundtripThroughLir(code, feedback, bytecodeMetadataProvider, timing);
     }
@@ -1075,6 +1076,9 @@
     LirCode<EV> lirCode =
         IR2LirConverter.translate(code, strategy.getEncodingStrategy(), appView.dexItemFactory());
     timing.end();
+    // Check that printing does not fail.
+    String lirString = lirCode.toString();
+    assert !lirString.isEmpty();
     timing.begin("LIR->IR (" + name + ")");
     IRCode irCode =
         Lir2IRConverter.translate(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
index 0c1c56f..1653ae2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordFieldValuesRewriter.java
@@ -112,7 +112,7 @@
       intConstantI.setPosition(constantPosition);
       iterator.add(intConstantI);
       ArrayPut arrayPut =
-          new ArrayPut(
+          ArrayPut.create(
               MemberType.OBJECT,
               newArrayEmpty.outValue(),
               intConstantI.outValue(),
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 4eea25d..a3e20e6 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.lightir;
 
+import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
@@ -11,10 +12,13 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.Add;
 import com.android.tools.r8.ir.code.Argument;
+import com.android.tools.r8.ir.code.ArrayGet;
 import com.android.tools.r8.ir.code.ArrayLength;
+import com.android.tools.r8.ir.code.ArrayPut;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.CatchHandlers;
 import com.android.tools.r8.ir.code.Cmp;
@@ -36,6 +40,7 @@
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.InvokeSuper;
 import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.Monitor;
 import com.android.tools.r8.ir.code.MonitorType;
 import com.android.tools.r8.ir.code.MoveException;
@@ -54,9 +59,12 @@
 import com.android.tools.r8.ir.code.Sub;
 import com.android.tools.r8.ir.code.Throw;
 import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.code.Xor;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.lightir.LirCode.PositionEntry;
 import com.android.tools.r8.utils.ListUtils;
+import com.google.common.collect.ImmutableList;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
 import it.unimi.dsi.fastutil.ints.IntArrayList;
@@ -202,8 +210,7 @@
         // LIR has no value-user info so after building is done, removed unused values.
         for (Instruction instruction : block.getInstructions()) {
           if (instruction.hasOutValue()
-              && !instruction.isArgument()
-              && !instruction.isMoveException()
+              && instruction.isInvoke()
               && instruction.hasUnusedOutValue()) {
             instruction.clearOutValue();
           }
@@ -330,6 +337,11 @@
     }
 
     @Override
+    public void onInstruction() {
+      throw new Unimplemented("Missing IR conversion");
+    }
+
+    @Override
     public void onConstNull() {
       Value dest = getOutValueForNextInstruction(TypeElement.getNull());
       addInstruction(new ConstNumber(dest, 0));
@@ -342,38 +354,66 @@
     }
 
     @Override
+    public void onConstFloat(int value) {
+      Value dest = getOutValueForNextInstruction(TypeElement.getFloat());
+      addInstruction(new ConstNumber(dest, value));
+    }
+
+    @Override
+    public void onConstLong(long value) {
+      Value dest = getOutValueForNextInstruction(TypeElement.getLong());
+      addInstruction(new ConstNumber(dest, value));
+    }
+
+    @Override
+    public void onConstDouble(long value) {
+      Value dest = getOutValueForNextInstruction(TypeElement.getDouble());
+      addInstruction(new ConstNumber(dest, value));
+    }
+
+    TypeElement valueTypeElement(NumericType type) {
+      return PrimitiveTypeElement.fromNumericType(type);
+    }
+
+    @Override
     public void onAdd(NumericType type, EV leftValueIndex, EV rightValueIndex) {
-      Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+      Value dest = getOutValueForNextInstruction(valueTypeElement(type));
       addInstruction(
           Add.createNonNormalized(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
     }
 
     @Override
     public void onSub(NumericType type, EV leftValueIndex, EV rightValueIndex) {
-      Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+      Value dest = getOutValueForNextInstruction(valueTypeElement(type));
       addInstruction(new Sub(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
     }
 
     @Override
     public void onMul(NumericType type, EV leftValueIndex, EV rightValueIndex) {
-      Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+      Value dest = getOutValueForNextInstruction(valueTypeElement(type));
       addInstruction(
           Mul.createNonNormalized(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
     }
 
     @Override
     public void onDiv(NumericType type, EV leftValueIndex, EV rightValueIndex) {
-      Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+      Value dest = getOutValueForNextInstruction(valueTypeElement(type));
       addInstruction(new Div(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
     }
 
     @Override
     public void onRem(NumericType type, EV leftValueIndex, EV rightValueIndex) {
-      Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+      Value dest = getOutValueForNextInstruction(valueTypeElement(type));
       addInstruction(new Rem(type, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
     }
 
     @Override
+    public void onXor(NumericType type, EV left, EV right) {
+      Value dest = getOutValueForNextInstruction(valueTypeElement(type));
+      addInstruction(Xor.createNonNormalized(type, dest, getValue(left), getValue(right)));
+    }
+
+    @Override
     public void onConstString(DexString string) {
       Value dest = getOutValueForNextInstruction(TypeElement.stringClassType(appView));
       addInstruction(new ConstString(dest, string));
@@ -398,6 +438,17 @@
     }
 
     @Override
+    public void onIfCmp(IfType ifKind, int blockIndex, EV leftValueIndex, EV rightValueIndex) {
+      BasicBlock targetBlock = getBasicBlock(blockIndex);
+      Value leftValue = getValue(leftValueIndex);
+      Value rightValue = getValue(rightValueIndex);
+      addInstruction(new If(ifKind, ImmutableList.of(leftValue, rightValue)));
+      currentBlock.link(targetBlock);
+      currentBlock.link(getBasicBlock(nextInstructionIndex));
+      closeCurrentBlock();
+    }
+
+    @Override
     public void onFallthrough() {
       int nextBlockIndex = peekNextInstructionIndex() + 1;
       onGoto(nextBlockIndex);
@@ -500,6 +551,12 @@
     }
 
     @Override
+    public void onReturn(EV value) {
+      addInstruction(new Return(getValue(value)));
+      closeCurrentBlock();
+    }
+
+    @Override
     public void onArrayLength(EV arrayValueIndex) {
       Value dest = getOutValueForNextInstruction(TypeElement.getInt());
       Value arrayValue = getValue(arrayValueIndex);
@@ -580,5 +637,27 @@
     public void onMonitorExit(EV value) {
       addInstruction(new Monitor(MonitorType.EXIT, getValue(value)));
     }
+
+    @Override
+    public void onArrayGetObject(DexType type, EV array, EV index) {
+      Value dest = getOutValueForNextInstruction(type.toTypeElement(appView));
+      addInstruction(new ArrayGet(MemberType.OBJECT, dest, getValue(array), getValue(index)));
+    }
+
+    @Override
+    public void onArrayGetPrimitive(MemberType type, EV array, EV index) {
+      // Convert the member type to a "stack value type", e.g., byte, char etc to int.
+      ValueType valueType = ValueType.fromMemberType(type);
+      DexType dexType = valueType.toDexType(appView.dexItemFactory());
+      Value dest = getOutValueForNextInstruction(dexType.toTypeElement(appView));
+      addInstruction(new ArrayGet(type, dest, getValue(array), getValue(index)));
+    }
+
+    @Override
+    public void onArrayPut(MemberType type, EV array, EV index, EV value) {
+      addInstruction(
+          ArrayPut.createWithoutVerification(
+              type, getValue(array), getValue(index), getValue(value)));
+    }
   }
 }
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 91ce188..95624bb 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.cf.code.CfArithmeticBinop;
 import com.android.tools.r8.cf.code.CfArithmeticBinop.Opcode;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
 import com.android.tools.r8.cf.code.CfNumberConversion;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
@@ -22,6 +23,7 @@
 import com.android.tools.r8.ir.code.Cmp.Bias;
 import com.android.tools.r8.ir.code.IRMetadata;
 import com.android.tools.r8.ir.code.IfType;
+import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.MonitorType;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.Position;
@@ -88,6 +90,10 @@
     flushedPosition = currentPosition;
   }
 
+  public DexItemFactory factory() {
+    return factory;
+  }
+
   public boolean verifyCurrentValueIndex(int valueIndex) {
     assert instructionCount + argumentCount == valueIndex;
     return true;
@@ -109,7 +115,6 @@
 
   public LirBuilder<V, EV> setCurrentPosition(Position position) {
     assert position != null;
-    assert position != Position.none();
     currentPosition = position;
     return this;
   }
@@ -566,7 +571,7 @@
   }
 
   public LirBuilder<V, EV> addArithmeticBinop(
-      Opcode binop, NumericType type, V leftValue, V rightValue) {
+      CfArithmeticBinop.Opcode binop, NumericType type, V leftValue, V rightValue) {
     // The LIR and CF opcodes are the same values, check that the two endpoints match.
     assert LirOpcodes.IADD == CfArithmeticBinop.getAsmOpcode(Opcode.Add, NumericType.INT);
     assert LirOpcodes.DREM == CfArithmeticBinop.getAsmOpcode(Opcode.Rem, NumericType.DOUBLE);
@@ -574,6 +579,17 @@
     return addTwoValueInstruction(opcode, leftValue, rightValue);
   }
 
+  public LirBuilder<V, EV> addLogicalBinop(
+      CfLogicalBinop.Opcode binop, NumericType type, V leftValue, V rightValue) {
+    // The LIR and CF opcodes are the same values, check that the two endpoints match.
+    assert LirOpcodes.ISHL
+        == CfLogicalBinop.getAsmOpcode(CfLogicalBinop.Opcode.Shl, NumericType.INT);
+    assert LirOpcodes.LXOR
+        == CfLogicalBinop.getAsmOpcode(CfLogicalBinop.Opcode.Xor, NumericType.LONG);
+    int opcode = CfLogicalBinop.getAsmOpcode(binop, type);
+    return addTwoValueInstruction(opcode, leftValue, rightValue);
+  }
+
   public LirBuilder<V, EV> addMonitor(MonitorType type, V value) {
     return addOneValueInstruction(
         type == MonitorType.ENTER ? LirOpcodes.MONITORENTER : LirOpcodes.MONITOREXIT, value);
@@ -590,4 +606,73 @@
     assert opcode <= LirOpcodes.I2S;
     return addOneValueInstruction(opcode, value);
   }
+
+  public LirBuilder<V, EV> addArrayGetObject(DexType destType, V array, V index) {
+    return addInstructionTemplate(
+        LirOpcodes.AALOAD, Collections.singletonList(destType), ImmutableList.of(array, index));
+  }
+
+  public LirBuilder<V, EV> addArrayGetPrimitive(MemberType memberType, V array, V index) {
+    int opcode;
+    switch (memberType) {
+      case BOOLEAN_OR_BYTE:
+        opcode = LirOpcodes.BALOAD;
+        break;
+      case CHAR:
+        opcode = LirOpcodes.CALOAD;
+        break;
+      case SHORT:
+        opcode = LirOpcodes.SALOAD;
+        break;
+      case INT:
+        opcode = LirOpcodes.IALOAD;
+        break;
+      case FLOAT:
+        opcode = LirOpcodes.FALOAD;
+        break;
+      case LONG:
+        opcode = LirOpcodes.LALOAD;
+        break;
+      case DOUBLE:
+        opcode = LirOpcodes.DALOAD;
+        break;
+      default:
+        throw new Unreachable("Unexpected object or imprecise member type: " + memberType);
+    }
+    return addInstructionTemplate(opcode, Collections.emptyList(), ImmutableList.of(array, index));
+  }
+
+  public LirBuilder<V, EV> addArrayPut(MemberType memberType, V array, V index, V value) {
+    int opcode;
+    switch (memberType) {
+      case BOOLEAN_OR_BYTE:
+        opcode = LirOpcodes.BASTORE;
+        break;
+      case CHAR:
+        opcode = LirOpcodes.CASTORE;
+        break;
+      case SHORT:
+        opcode = LirOpcodes.SASTORE;
+        break;
+      case INT:
+        opcode = LirOpcodes.IASTORE;
+        break;
+      case FLOAT:
+        opcode = LirOpcodes.FASTORE;
+        break;
+      case LONG:
+        opcode = LirOpcodes.LASTORE;
+        break;
+      case DOUBLE:
+        opcode = LirOpcodes.DASTORE;
+        break;
+      case OBJECT:
+        opcode = LirOpcodes.AASTORE;
+        break;
+      default:
+        throw new Unreachable("Unexpected imprecise member type: " + memberType);
+    }
+    return addInstructionTemplate(
+        opcode, Collections.emptyList(), ImmutableList.of(array, index, value));
+  }
 }
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 de3a7b4..6af7bb5 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
@@ -5,12 +5,14 @@
 
 import com.android.tools.r8.cf.code.CfNumberConversion;
 import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.Unreachable;
 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.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 java.util.ArrayList;
 import java.util.List;
@@ -79,6 +81,38 @@
     onInstruction();
   }
 
+  private void onArrayGetInternal(MemberType type, LirInstructionView view) {
+    if (type.isObject()) {
+      DexType destType = (DexType) getConstantItem(view.getNextConstantOperand());
+      EV array = getNextValueOperand(view);
+      EV index = getNextValueOperand(view);
+      onArrayGetObject(destType, array, index);
+    } else {
+      EV array = getNextValueOperand(view);
+      EV index = getNextValueOperand(view);
+      onArrayGetPrimitive(type, array, index);
+    }
+  }
+
+  public void onArrayGetPrimitive(MemberType type, EV array, EV index) {
+    onInstruction();
+  }
+
+  public void onArrayGetObject(DexType type, EV array, EV index) {
+    onInstruction();
+  }
+
+  private void onArrayPutInternal(MemberType type, LirInstructionView view) {
+    EV array = getNextValueOperand(view);
+    EV index = getNextValueOperand(view);
+    EV value = getNextValueOperand(view);
+    onArrayPut(type, array, index, value);
+  }
+
+  public void onArrayPut(MemberType type, EV array, EV index, EV value) {
+    onInstruction();
+  }
+
   public void onAdd(NumericType type, EV leftValueIndex, EV rightValueIndex) {
     onInstruction();
   }
@@ -179,6 +213,36 @@
     onRem(NumericType.DOUBLE, leftValueIndex, rightValueIndex);
   }
 
+  private void onLogicalBinopInternal(int opcode, LirInstructionView view) {
+    EV left = getNextValueOperand(view);
+    EV right = getNextValueOperand(view);
+    switch (opcode) {
+      case LirOpcodes.ISHL:
+      case LirOpcodes.LSHL:
+      case LirOpcodes.ISHR:
+      case LirOpcodes.LSHR:
+      case LirOpcodes.IUSHR:
+      case LirOpcodes.LUSHR:
+      case LirOpcodes.IAND:
+      case LirOpcodes.LAND:
+      case LirOpcodes.IOR:
+      case LirOpcodes.LOR:
+        throw new Unimplemented();
+      case LirOpcodes.IXOR:
+        onXor(NumericType.INT, left, right);
+        return;
+      case LirOpcodes.LXOR:
+        onXor(NumericType.INT, left, right);
+        return;
+      default:
+        throw new Unreachable("Unexpected logical binop: " + opcode);
+    }
+  }
+
+  public void onXor(NumericType type, EV left, EV right) {
+    onInstruction();
+  }
+
   public void onNumberConversion(int opcode, EV value) {
     assert LirOpcodes.I2L <= opcode;
     assert opcode <= LirOpcodes.I2S;
@@ -190,10 +254,27 @@
     onInstruction();
   }
 
+  private void onIfInternal(IfType ifKind, LirInstructionView view) {
+    int blockIndex = view.getNextBlockOperand();
+    EV valueIndex = getNextValueOperand(view);
+    onIf(ifKind, blockIndex, valueIndex);
+  }
+
   public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
     onInstruction();
   }
 
+  private void onIfCmpInternal(IfType ifKind, LirInstructionView view) {
+    int blockIndex = view.getNextBlockOperand();
+    EV leftValueIndex = getNextValueOperand(view);
+    EV rightValueIndex = getNextValueOperand(view);
+    onIfCmp(ifKind, blockIndex, leftValueIndex, rightValueIndex);
+  }
+
+  public void onIfCmp(IfType ifKind, int blockIndex, EV leftValueIndex, EV rightValueIndex) {
+    onInstruction();
+  }
+
   public void onGoto(int blockIndex) {
     onInstruction();
   }
@@ -258,6 +339,10 @@
     onInstruction();
   }
 
+  public void onReturn(EV value) {
+    onInstruction();
+  }
+
   public void onArrayLength(EV arrayValueIndex) {
     onInstruction();
   }
@@ -365,6 +450,92 @@
           onConstDouble(value);
           return;
         }
+      case LirOpcodes.IALOAD:
+        {
+          onArrayGetInternal(MemberType.INT, view);
+          break;
+        }
+      case LirOpcodes.LALOAD:
+        {
+          onArrayGetInternal(MemberType.LONG, view);
+          break;
+        }
+      case LirOpcodes.FALOAD:
+        {
+          onArrayGetInternal(MemberType.FLOAT, view);
+          break;
+        }
+      case LirOpcodes.DALOAD:
+        {
+          onArrayGetInternal(MemberType.DOUBLE, view);
+          break;
+        }
+      case LirOpcodes.AALOAD:
+        {
+          onArrayGetInternal(MemberType.OBJECT, view);
+          break;
+        }
+      case LirOpcodes.BALOAD:
+        {
+          onArrayGetInternal(MemberType.BOOLEAN_OR_BYTE, view);
+          break;
+        }
+      case LirOpcodes.CALOAD:
+        {
+          onArrayGetInternal(MemberType.CHAR, view);
+          break;
+        }
+      case LirOpcodes.SALOAD:
+        {
+          onArrayGetInternal(MemberType.SHORT, view);
+          break;
+        }
+      case LirOpcodes.IASTORE:
+        {
+          onArrayPutInternal(MemberType.INT, view);
+          break;
+        }
+      case LirOpcodes.LASTORE:
+        {
+          onArrayPutInternal(MemberType.LONG, view);
+          break;
+        }
+      case LirOpcodes.FASTORE:
+        {
+          onArrayPutInternal(MemberType.FLOAT, view);
+          break;
+        }
+
+      case LirOpcodes.DASTORE:
+        {
+          onArrayPutInternal(MemberType.DOUBLE, view);
+          break;
+        }
+
+      case LirOpcodes.AASTORE:
+        {
+          onArrayPutInternal(MemberType.OBJECT, view);
+          break;
+        }
+
+      case LirOpcodes.BASTORE:
+        {
+          onArrayPutInternal(MemberType.BOOLEAN_OR_BYTE, view);
+          break;
+        }
+
+      case LirOpcodes.CASTORE:
+        {
+          onArrayPutInternal(MemberType.CHAR, view);
+          break;
+        }
+
+      case LirOpcodes.SASTORE:
+        {
+          onArrayPutInternal(MemberType.SHORT, view);
+          break;
+        }
+
       case LirOpcodes.IADD:
         {
           EV leftValueIndex = getNextValueOperand(view);
@@ -505,6 +676,22 @@
           onRemDouble(leftValueIndex, rightValueIndex);
           return;
         }
+      case LirOpcodes.ISHL:
+      case LirOpcodes.LSHL:
+      case LirOpcodes.ISHR:
+      case LirOpcodes.LSHR:
+      case LirOpcodes.IUSHR:
+      case LirOpcodes.LUSHR:
+      case LirOpcodes.IAND:
+      case LirOpcodes.LAND:
+      case LirOpcodes.IOR:
+      case LirOpcodes.LOR:
+      case LirOpcodes.IXOR:
+      case LirOpcodes.LXOR:
+        {
+          onLogicalBinopInternal(opcode, view);
+          return;
+        }
       case LirOpcodes.I2L:
       case LirOpcodes.I2F:
       case LirOpcodes.I2D:
@@ -525,11 +712,84 @@
           onNumberConversion(opcode, value);
           return;
         }
+      case LirOpcodes.IFEQ:
+        {
+          onIfInternal(IfType.EQ, view);
+          return;
+        }
       case LirOpcodes.IFNE:
         {
-          int blockIndex = view.getNextBlockOperand();
-          EV valueIndex = getNextValueOperand(view);
-          onIf(IfType.NE, blockIndex, valueIndex);
+          onIfInternal(IfType.NE, view);
+          return;
+        }
+      case LirOpcodes.IFLT:
+        {
+          onIfInternal(IfType.LT, view);
+          return;
+        }
+      case LirOpcodes.IFGE:
+        {
+          onIfInternal(IfType.GE, view);
+          return;
+        }
+      case LirOpcodes.IFGT:
+        {
+          onIfInternal(IfType.GT, view);
+          return;
+        }
+      case LirOpcodes.IFLE:
+        {
+          onIfInternal(IfType.LE, view);
+          return;
+        }
+      case LirOpcodes.IFNULL:
+        {
+          onIfInternal(IfType.EQ, view);
+          return;
+        }
+      case LirOpcodes.IFNONNULL:
+        {
+          onIfInternal(IfType.NE, view);
+          return;
+        }
+      case LirOpcodes.IF_ICMPEQ:
+        {
+          onIfCmpInternal(IfType.EQ, view);
+          return;
+        }
+      case LirOpcodes.IF_ICMPNE:
+        {
+          onIfCmpInternal(IfType.NE, view);
+          return;
+        }
+      case LirOpcodes.IF_ICMPLT:
+        {
+          onIfCmpInternal(IfType.LT, view);
+          return;
+        }
+      case LirOpcodes.IF_ICMPGE:
+        {
+          onIfCmpInternal(IfType.GE, view);
+          return;
+        }
+      case LirOpcodes.IF_ICMPGT:
+        {
+          onIfCmpInternal(IfType.GT, view);
+          return;
+        }
+      case LirOpcodes.IF_ICMPLE:
+        {
+          onIfCmpInternal(IfType.LE, view);
+          return;
+        }
+      case LirOpcodes.IF_ACMPEQ:
+        {
+          onIfCmpInternal(IfType.EQ, view);
+          return;
+        }
+      case LirOpcodes.IF_ACMPNE:
+        {
+          onIfCmpInternal(IfType.NE, view);
           return;
         }
       case LirOpcodes.GOTO:
@@ -624,6 +884,12 @@
           onReturnVoid();
           return;
         }
+      case LirOpcodes.ARETURN:
+        {
+          EV value = getNextValueOperand(view);
+          onReturn(value);
+          return;
+        }
       case LirOpcodes.ARRAYLENGTH:
         {
           onArrayLength(getNextValueOperand(view));
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 19d5350..58a00f6 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
@@ -9,6 +9,7 @@
 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.utils.StringUtils;
 import java.util.Arrays;
@@ -129,18 +130,47 @@
   }
 
   @Override
+  public void onConstFloat(int value) {
+    appendOutValue().append(Float.intBitsToFloat(value));
+  }
+
+  @Override
+  public void onConstLong(long value) {
+    appendOutValue().append(value);
+  }
+
+  @Override
+  public void onConstDouble(long value) {
+    appendOutValue().append(Double.longBitsToDouble(value));
+  }
+
+  @Override
   public void onConstString(DexString string) {
     appendOutValue().append("str(").append(string).append(")");
   }
 
   @Override
+  public void onAdd(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+    appendOutValue();
+    appendValueArguments(leftValueIndex, rightValueIndex);
+  }
+
+  @Override
+  public void onSub(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+    appendOutValue();
+    appendValueArguments(leftValueIndex, rightValueIndex);
+  }
+
+  @Override
   public void onDiv(NumericType type, EV leftValueIndex, EV rightValueIndex) {
-    appendOutValue()
-        .append(fmtValueIndex(leftValueIndex))
-        .append(' ')
-        .append(fmtValueIndex(rightValueIndex))
-        .append(' ')
-        .append(type);
+    appendOutValue();
+    appendValueArguments(leftValueIndex, rightValueIndex);
+  }
+
+  @Override
+  public void onXor(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+    appendOutValue();
+    appendValueArguments(leftValueIndex, rightValueIndex);
   }
 
   @Override
@@ -151,7 +181,14 @@
 
   @Override
   public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
-    builder.append(fmtValueIndex(valueIndex)).append(' ').append(fmtInsnIndex(blockIndex));
+    appendValueArguments(valueIndex);
+    builder.append(fmtInsnIndex(blockIndex));
+  }
+
+  @Override
+  public void onIfCmp(IfType ifKind, int blockIndex, EV leftValueIndex, EV rightValueIndex) {
+    appendValueArguments(leftValueIndex, rightValueIndex);
+    builder.append(fmtInsnIndex(blockIndex));
   }
 
   @Override
@@ -175,6 +212,12 @@
   }
 
   @Override
+  public void onNewInstance(DexType clazz) {
+    appendOutValue();
+    builder.append(clazz);
+  }
+
+  @Override
   public void onInvokeMethodInstruction(DexMethod method, List<EV> arguments) {
     if (!method.getReturnType().isVoidType()) {
       appendOutValue();
@@ -225,11 +268,36 @@
   }
 
   @Override
+  public void onReturn(EV value) {
+    appendValueArguments(value);
+  }
+
+  @Override
   public void onArrayLength(EV arrayValueIndex) {
     appendOutValue().append(fmtValueIndex(arrayValueIndex));
   }
 
   @Override
+  public void onArrayGetPrimitive(MemberType type, EV array, EV index) {
+    appendOutValue();
+    appendValueArguments(array, index);
+    builder.append(type);
+  }
+
+  @Override
+  public void onArrayGetObject(DexType type, EV array, EV index) {
+    appendOutValue();
+    appendValueArguments(array, index);
+    builder.append(type);
+  }
+
+  @Override
+  public void onArrayPut(MemberType type, EV array, EV index, EV value) {
+    appendValueArguments(array, index, value);
+    builder.append(type);
+  }
+
+  @Override
   public void onDebugPosition() {
     // Nothing to append.
   }
diff --git a/src/test/java/com/android/tools/r8/examples/ExamplesTestBase.java b/src/test/java/com/android/tools/r8/examples/ExamplesTestBase.java
index 093702c..76af63f 100644
--- a/src/test/java/com/android/tools/r8/examples/ExamplesTestBase.java
+++ b/src/test/java/com/android/tools/r8/examples/ExamplesTestBase.java
@@ -45,6 +45,7 @@
   public void runTestR8() throws Exception {
     parameters.assumeR8TestParameters();
     testForR8(parameters.getBackend())
+        .addOptionsModification(o -> o.testing.roundtripThroughLir = true)
         .setMinApi(parameters)
         .addProgramClasses(getTestClasses())
         .addKeepMainRule(getMainClass())