Merge "Simplify error reporting."
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 300335a..6d7ae19 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -3,11 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.cf;
 
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
 import com.android.tools.r8.cf.code.CfArrayLength;
 import com.android.tools.r8.cf.code.CfArrayLoad;
 import com.android.tools.r8.cf.code.CfArrayStore;
-import com.android.tools.r8.cf.code.CfBinop;
 import com.android.tools.r8.cf.code.CfCheckCast;
+import com.android.tools.r8.cf.code.CfCmp;
 import com.android.tools.r8.cf.code.CfConstClass;
 import com.android.tools.r8.cf.code.CfConstMethodHandle;
 import com.android.tools.r8.cf.code.CfConstMethodType;
@@ -27,11 +28,14 @@
 import com.android.tools.r8.cf.code.CfInvokeDynamic;
 import com.android.tools.r8.cf.code.CfLabel;
 import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
 import com.android.tools.r8.cf.code.CfMonitor;
 import com.android.tools.r8.cf.code.CfMultiANewArray;
+import com.android.tools.r8.cf.code.CfNeg;
 import com.android.tools.r8.cf.code.CfNew;
 import com.android.tools.r8.cf.code.CfNewArray;
 import com.android.tools.r8.cf.code.CfNop;
+import com.android.tools.r8.cf.code.CfNumberConversion;
 import com.android.tools.r8.cf.code.CfPosition;
 import com.android.tools.r8.cf.code.CfReturn;
 import com.android.tools.r8.cf.code.CfReturnVoid;
@@ -41,7 +45,6 @@
 import com.android.tools.r8.cf.code.CfSwitch.Kind;
 import com.android.tools.r8.cf.code.CfThrow;
 import com.android.tools.r8.cf.code.CfTryCatch;
-import com.android.tools.r8.cf.code.CfUnop;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
@@ -230,12 +233,24 @@
     print(monitor.getType() == Monitor.Type.ENTER ? "monitorenter" : "monitorexit");
   }
 
-  public void print(CfBinop binop) {
-    print(opcodeName(binop.getOpcode()));
+  public void print(CfArithmeticBinop arithmeticBinop) {
+    print(opcodeName(arithmeticBinop.getAsmOpcode()));
   }
 
-  public void print(CfUnop unop) {
-    print(opcodeName(unop.getOpcode()));
+  public void print(CfCmp cmp) {
+    print(opcodeName(cmp.getAsmOpcode()));
+  }
+
+  public void print(CfLogicalBinop logicalBinop) {
+    print(opcodeName(logicalBinop.getAsmOpcode()));
+  }
+
+  public void print(CfNeg neg) {
+    print(opcodeName(neg.getAsmOpcode()));
+  }
+
+  public void print(CfNumberConversion numberConversion) {
+    print(opcodeName(numberConversion.getAsmOpcode()));
   }
 
   public void print(CfConstString constString) {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
new file mode 100644
index 0000000..423bba2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfArithmeticBinop.java
@@ -0,0 +1,119 @@
+// Copyright (c) 2018, 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.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfArithmeticBinop extends CfInstruction {
+
+  public enum Opcode {
+    Add,
+    Sub,
+    Mul,
+    Div,
+    Rem,
+  }
+
+  private final Opcode opcode;
+  private final NumericType type;
+
+  public CfArithmeticBinop(Opcode opcode, NumericType type) {
+    assert opcode != null;
+    assert type != null;
+    this.opcode = opcode;
+    this.type = type;
+  }
+
+  public static CfArithmeticBinop fromAsm(int opcode) {
+    switch (opcode) {
+      case Opcodes.IADD:
+        return new CfArithmeticBinop(Opcode.Add, NumericType.INT);
+      case Opcodes.LADD:
+        return new CfArithmeticBinop(Opcode.Add, NumericType.LONG);
+      case Opcodes.FADD:
+        return new CfArithmeticBinop(Opcode.Add, NumericType.FLOAT);
+      case Opcodes.DADD:
+        return new CfArithmeticBinop(Opcode.Add, NumericType.DOUBLE);
+      case Opcodes.ISUB:
+        return new CfArithmeticBinop(Opcode.Sub, NumericType.INT);
+      case Opcodes.LSUB:
+        return new CfArithmeticBinop(Opcode.Sub, NumericType.LONG);
+      case Opcodes.FSUB:
+        return new CfArithmeticBinop(Opcode.Sub, NumericType.FLOAT);
+      case Opcodes.DSUB:
+        return new CfArithmeticBinop(Opcode.Sub, NumericType.DOUBLE);
+      case Opcodes.IMUL:
+        return new CfArithmeticBinop(Opcode.Mul, NumericType.INT);
+      case Opcodes.LMUL:
+        return new CfArithmeticBinop(Opcode.Mul, NumericType.LONG);
+      case Opcodes.FMUL:
+        return new CfArithmeticBinop(Opcode.Mul, NumericType.FLOAT);
+      case Opcodes.DMUL:
+        return new CfArithmeticBinop(Opcode.Mul, NumericType.DOUBLE);
+      case Opcodes.IDIV:
+        return new CfArithmeticBinop(Opcode.Div, NumericType.INT);
+      case Opcodes.LDIV:
+        return new CfArithmeticBinop(Opcode.Div, NumericType.LONG);
+      case Opcodes.FDIV:
+        return new CfArithmeticBinop(Opcode.Div, NumericType.FLOAT);
+      case Opcodes.DDIV:
+        return new CfArithmeticBinop(Opcode.Div, NumericType.DOUBLE);
+      case Opcodes.IREM:
+        return new CfArithmeticBinop(Opcode.Rem, NumericType.INT);
+      case Opcodes.LREM:
+        return new CfArithmeticBinop(Opcode.Rem, NumericType.LONG);
+      case Opcodes.FREM:
+        return new CfArithmeticBinop(Opcode.Rem, NumericType.FLOAT);
+      case Opcodes.DREM:
+        return new CfArithmeticBinop(Opcode.Rem, NumericType.DOUBLE);
+      default:
+        throw new Unreachable("Wrong ASM opcode for CfArithmeticBinop " + opcode);
+    }
+  }
+
+  public int getAsmOpcode() {
+    switch (opcode) {
+      case Add:
+        return Opcodes.IADD + getAsmOpcodeTypeOffset();
+      case Sub:
+        return Opcodes.ISUB + getAsmOpcodeTypeOffset();
+      case Mul:
+        return Opcodes.IMUL + getAsmOpcodeTypeOffset();
+      case Div:
+        return Opcodes.IDIV + getAsmOpcodeTypeOffset();
+      case Rem:
+        return Opcodes.IREM + getAsmOpcodeTypeOffset();
+      default:
+        throw new Unreachable("CfArithmeticBinop has unknown opcode " + opcode);
+    }
+  }
+
+  private int getAsmOpcodeTypeOffset() {
+    switch (type) {
+      case LONG:
+        return Opcodes.LADD - Opcodes.IADD;
+      case FLOAT:
+        return Opcodes.FADD - Opcodes.IADD;
+      case DOUBLE:
+        return Opcodes.DADD - Opcodes.IADD;
+      default:
+        return 0;
+    }
+  }
+
+  @Override
+  public void print(CfPrinter printer) {
+    printer.print(this);
+  }
+
+  @Override
+  public void write(MethodVisitor visitor, NamingLens lens) {
+    visitor.visitInsn(getAsmOpcode());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfBinop.java b/src/main/java/com/android/tools/r8/cf/code/CfBinop.java
deleted file mode 100644
index 6f8e868..0000000
--- a/src/main/java/com/android/tools/r8/cf/code/CfBinop.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) 2017, 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.cf.code;
-
-import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.naming.NamingLens;
-import org.objectweb.asm.MethodVisitor;
-
-public class CfBinop extends CfInstruction {
-
-  private final int opcode;
-
-  public CfBinop(int opcode) {
-    this.opcode = opcode;
-  }
-
-  public int getOpcode() {
-    return opcode;
-  }
-
-  @Override
-  public void write(MethodVisitor visitor, NamingLens lens) {
-    visitor.visitInsn(opcode);
-  }
-
-  @Override
-  public void print(CfPrinter printer) {
-    printer.print(this);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
new file mode 100644
index 0000000..9c6d3d7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCmp.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2018, 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.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.Cmp;
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfCmp extends CfInstruction {
+
+  private final Bias bias;
+  private final NumericType type;
+
+  public CfCmp(Bias bias, NumericType type) {
+    assert bias != null;
+    assert type != null;
+    assert type == NumericType.LONG || type == NumericType.FLOAT || type == NumericType.DOUBLE;
+    assert type != NumericType.LONG || bias == Cmp.Bias.NONE;
+    assert type == NumericType.LONG || bias != Cmp.Bias.NONE;
+    this.bias = bias;
+    this.type = type;
+  }
+
+  public static CfCmp fromAsm(int opcode) {
+    switch (opcode) {
+      case Opcodes.LCMP:
+        return new CfCmp(Bias.NONE, NumericType.LONG);
+      case Opcodes.FCMPL:
+        return new CfCmp(Bias.LT, NumericType.FLOAT);
+      case Opcodes.FCMPG:
+        return new CfCmp(Bias.GT, NumericType.FLOAT);
+      case Opcodes.DCMPL:
+        return new CfCmp(Bias.LT, NumericType.DOUBLE);
+      case Opcodes.DCMPG:
+        return new CfCmp(Bias.GT, NumericType.DOUBLE);
+      default:
+        throw new Unreachable("Wrong ASM opcode for CfCmp " + opcode);
+    }
+  }
+
+  public int getAsmOpcode() {
+    switch (type) {
+      case LONG:
+        return Opcodes.LCMP;
+      case FLOAT:
+        return bias == Cmp.Bias.LT ? Opcodes.FCMPL : Opcodes.FCMPG;
+      case DOUBLE:
+        return bias == Cmp.Bias.LT ? Opcodes.DCMPL : Opcodes.DCMPG;
+      default:
+        throw new Unreachable("CfCmp has unknown type " + type);
+    }
+  }
+
+  @Override
+  public void print(CfPrinter printer) {
+    printer.print(this);
+  }
+
+  @Override
+  public void write(MethodVisitor visitor, NamingLens lens) {
+    visitor.visitInsn(getAsmOpcode());
+  }
+}
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
new file mode 100644
index 0000000..2aab288
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfLogicalBinop.java
@@ -0,0 +1,94 @@
+// Copyright (c) 2018, 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.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfLogicalBinop extends CfInstruction {
+
+  public enum Opcode {
+    Shl,
+    Shr,
+    Ushr,
+    And,
+    Or,
+    Xor,
+  }
+
+  private final Opcode opcode;
+  private final NumericType type;
+
+  public CfLogicalBinop(Opcode opcode, NumericType type) {
+    assert opcode != null;
+    assert type != null;
+    assert type != NumericType.FLOAT && type != NumericType.DOUBLE;
+    this.opcode = opcode;
+    this.type = type;
+  }
+
+  public static CfLogicalBinop fromAsm(int opcode) {
+    switch (opcode) {
+      case Opcodes.ISHL:
+        return new CfLogicalBinop(Opcode.Shl, NumericType.INT);
+      case Opcodes.LSHL:
+        return new CfLogicalBinop(Opcode.Shl, NumericType.LONG);
+      case Opcodes.ISHR:
+        return new CfLogicalBinop(Opcode.Shr, NumericType.INT);
+      case Opcodes.LSHR:
+        return new CfLogicalBinop(Opcode.Shr, NumericType.LONG);
+      case Opcodes.IUSHR:
+        return new CfLogicalBinop(Opcode.Ushr, NumericType.INT);
+      case Opcodes.LUSHR:
+        return new CfLogicalBinop(Opcode.Ushr, NumericType.LONG);
+      case Opcodes.IAND:
+        return new CfLogicalBinop(Opcode.And, NumericType.INT);
+      case Opcodes.LAND:
+        return new CfLogicalBinop(Opcode.And, NumericType.LONG);
+      case Opcodes.IOR:
+        return new CfLogicalBinop(Opcode.Or, NumericType.INT);
+      case Opcodes.LOR:
+        return new CfLogicalBinop(Opcode.Or, NumericType.LONG);
+      case Opcodes.IXOR:
+        return new CfLogicalBinop(Opcode.Xor, NumericType.INT);
+      case Opcodes.LXOR:
+        return new CfLogicalBinop(Opcode.Xor, NumericType.LONG);
+      default:
+        throw new Unreachable("Wrong ASM opcode for CfLogicalBinop " + opcode);
+    }
+  }
+
+  public int getAsmOpcode() {
+    switch (opcode) {
+      case Shl:
+        return type.isWide() ? Opcodes.LSHL : Opcodes.ISHL;
+      case Shr:
+        return type.isWide() ? Opcodes.LSHR : Opcodes.ISHR;
+      case Ushr:
+        return type.isWide() ? Opcodes.LUSHR : Opcodes.IUSHR;
+      case And:
+        return type.isWide() ? Opcodes.LAND : Opcodes.IAND;
+      case Or:
+        return type.isWide() ? Opcodes.LOR : Opcodes.IOR;
+      case Xor:
+        return type.isWide() ? Opcodes.LXOR : Opcodes.IXOR;
+      default:
+        throw new Unreachable("CfLogicalBinop has unknown opcode " + opcode);
+    }
+  }
+
+  @Override
+  public void print(CfPrinter printer) {
+    printer.print(this);
+  }
+
+  @Override
+  public void write(MethodVisitor visitor, NamingLens lens) {
+    visitor.visitInsn(getAsmOpcode());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNeg.java b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
new file mode 100644
index 0000000..db0019a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNeg.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2017, 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.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfNeg extends CfInstruction {
+
+  private final NumericType type;
+
+  public CfNeg(NumericType type) {
+    this.type = type;
+  }
+
+  @Override
+  public void write(MethodVisitor visitor, NamingLens lens) {
+    visitor.visitInsn(getAsmOpcode());
+  }
+
+  @Override
+  public void print(CfPrinter printer) {
+    printer.print(this);
+  }
+
+  public int getAsmOpcode() {
+    switch (type) {
+      case BYTE:
+      case CHAR:
+      case SHORT:
+      case INT:
+        return Opcodes.INEG;
+      case LONG:
+        return Opcodes.LNEG;
+      case FLOAT:
+        return Opcodes.FNEG;
+      case DOUBLE:
+        return Opcodes.DNEG;
+      default:
+        throw new Unreachable("Invalid type for CfNeg " + type);
+    }
+  }
+
+  public static CfNeg fromAsm(int opcode) {
+    switch (opcode) {
+      case Opcodes.INEG:
+        return new CfNeg(NumericType.INT);
+      case Opcodes.LNEG:
+        return new CfNeg(NumericType.LONG);
+      case Opcodes.FNEG:
+        return new CfNeg(NumericType.FLOAT);
+      case Opcodes.DNEG:
+        return new CfNeg(NumericType.DOUBLE);
+      default:
+        throw new Unreachable("Invalid opcode for CfNeg " + opcode);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
new file mode 100644
index 0000000..b585cb6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNumberConversion.java
@@ -0,0 +1,130 @@
+// Copyright (c) 2018, 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.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class CfNumberConversion extends CfInstruction {
+
+  private final NumericType from;
+  private final NumericType to;
+
+  public CfNumberConversion(NumericType from, NumericType to) {
+    assert from != to;
+    assert from != NumericType.BYTE && from != NumericType.SHORT && from != NumericType.CHAR;
+    assert (to != NumericType.BYTE && to != NumericType.SHORT && to != NumericType.CHAR)
+        || from == NumericType.INT;
+    this.from = from;
+    this.to = to;
+  }
+
+  @Override
+  public void write(MethodVisitor visitor, NamingLens lens) {
+    visitor.visitInsn(this.getAsmOpcode());
+  }
+
+  @Override
+  public void print(CfPrinter printer) {
+    printer.print(this);
+  }
+
+  public int getAsmOpcode() {
+    switch (from) {
+      case INT:
+        switch (to) {
+          case BYTE:
+            return Opcodes.I2B;
+          case CHAR:
+            return Opcodes.I2C;
+          case SHORT:
+            return Opcodes.I2S;
+          case LONG:
+            return Opcodes.I2L;
+          case FLOAT:
+            return Opcodes.I2F;
+          case DOUBLE:
+            return Opcodes.I2D;
+          default:
+            throw new Unreachable("Invalid CfNumberConversion from " + from + " to " + to);
+        }
+      case LONG:
+        switch (to) {
+          case INT:
+            return Opcodes.L2I;
+          case FLOAT:
+            return Opcodes.L2F;
+          case DOUBLE:
+            return Opcodes.L2D;
+          default:
+            throw new Unreachable("Invalid CfNumberConversion from " + from + " to " + to);
+        }
+      case FLOAT:
+        switch (to) {
+          case INT:
+            return Opcodes.F2I;
+          case LONG:
+            return Opcodes.F2L;
+          case DOUBLE:
+            return Opcodes.F2D;
+          default:
+            throw new Unreachable("Invalid CfNumberConversion from " + from + " to " + to);
+        }
+      case DOUBLE:
+        switch (to) {
+          case INT:
+            return Opcodes.D2I;
+          case LONG:
+            return Opcodes.D2L;
+          case FLOAT:
+            return Opcodes.D2F;
+          default:
+            throw new Unreachable("Invalid CfNumberConversion from " + from + " to " + to);
+        }
+      default:
+        throw new Unreachable("Invalid CfNumberConversion from " + from + " to " + to);
+    }
+  }
+
+  public static CfNumberConversion fromAsm(int opcode) {
+    switch (opcode) {
+      case Opcodes.I2L:
+        return new CfNumberConversion(NumericType.INT, NumericType.LONG);
+      case Opcodes.I2F:
+        return new CfNumberConversion(NumericType.INT, NumericType.FLOAT);
+      case Opcodes.I2D:
+        return new CfNumberConversion(NumericType.INT, NumericType.DOUBLE);
+      case Opcodes.L2I:
+        return new CfNumberConversion(NumericType.LONG, NumericType.INT);
+      case Opcodes.L2F:
+        return new CfNumberConversion(NumericType.LONG, NumericType.FLOAT);
+      case Opcodes.L2D:
+        return new CfNumberConversion(NumericType.LONG, NumericType.DOUBLE);
+      case Opcodes.F2I:
+        return new CfNumberConversion(NumericType.FLOAT, NumericType.INT);
+      case Opcodes.F2L:
+        return new CfNumberConversion(NumericType.FLOAT, NumericType.LONG);
+      case Opcodes.F2D:
+        return new CfNumberConversion(NumericType.FLOAT, NumericType.DOUBLE);
+      case Opcodes.D2I:
+        return new CfNumberConversion(NumericType.DOUBLE, NumericType.INT);
+      case Opcodes.D2L:
+        return new CfNumberConversion(NumericType.DOUBLE, NumericType.LONG);
+      case Opcodes.D2F:
+        return new CfNumberConversion(NumericType.DOUBLE, NumericType.FLOAT);
+      case Opcodes.I2B:
+        return new CfNumberConversion(NumericType.INT, NumericType.BYTE);
+      case Opcodes.I2C:
+        return new CfNumberConversion(NumericType.INT, NumericType.CHAR);
+      case Opcodes.I2S:
+        return new CfNumberConversion(NumericType.INT, NumericType.SHORT);
+      default:
+        throw new Unreachable("Unexpected CfNumberConversion opcode " + opcode);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfUnop.java b/src/main/java/com/android/tools/r8/cf/code/CfUnop.java
deleted file mode 100644
index 6b4bf76..0000000
--- a/src/main/java/com/android/tools/r8/cf/code/CfUnop.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) 2017, 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.cf.code;
-
-import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.naming.NamingLens;
-import org.objectweb.asm.MethodVisitor;
-
-public class CfUnop extends CfInstruction {
-
-  private final int opcode;
-
-  public CfUnop(int opcode) {
-    this.opcode = opcode;
-  }
-
-  @Override
-  public void write(MethodVisitor visitor, NamingLens lens) {
-    visitor.visitInsn(this.opcode);
-  }
-
-  @Override
-  public void print(CfPrinter printer) {
-    printer.print(this);
-  }
-
-  public int getOpcode() {
-    return opcode;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 47f476d..247f790 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -9,11 +9,12 @@
 import static org.objectweb.asm.Opcodes.ASM6;
 
 import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
 import com.android.tools.r8.cf.code.CfArrayLength;
 import com.android.tools.r8.cf.code.CfArrayLoad;
 import com.android.tools.r8.cf.code.CfArrayStore;
-import com.android.tools.r8.cf.code.CfBinop;
 import com.android.tools.r8.cf.code.CfCheckCast;
+import com.android.tools.r8.cf.code.CfCmp;
 import com.android.tools.r8.cf.code.CfConstClass;
 import com.android.tools.r8.cf.code.CfConstMethodHandle;
 import com.android.tools.r8.cf.code.CfConstMethodType;
@@ -33,11 +34,14 @@
 import com.android.tools.r8.cf.code.CfInvokeDynamic;
 import com.android.tools.r8.cf.code.CfLabel;
 import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
 import com.android.tools.r8.cf.code.CfMonitor;
 import com.android.tools.r8.cf.code.CfMultiANewArray;
+import com.android.tools.r8.cf.code.CfNeg;
 import com.android.tools.r8.cf.code.CfNew;
 import com.android.tools.r8.cf.code.CfNewArray;
 import com.android.tools.r8.cf.code.CfNop;
+import com.android.tools.r8.cf.code.CfNumberConversion;
 import com.android.tools.r8.cf.code.CfPosition;
 import com.android.tools.r8.cf.code.CfReturn;
 import com.android.tools.r8.cf.code.CfReturnVoid;
@@ -46,7 +50,6 @@
 import com.android.tools.r8.cf.code.CfSwitch;
 import com.android.tools.r8.cf.code.CfThrow;
 import com.android.tools.r8.cf.code.CfTryCatch;
-import com.android.tools.r8.cf.code.CfUnop;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
@@ -857,13 +860,13 @@
         case Opcodes.LREM:
         case Opcodes.FREM:
         case Opcodes.DREM:
-          instructions.add(new CfBinop(opcode));
+          instructions.add(CfArithmeticBinop.fromAsm(opcode));
           break;
         case Opcodes.INEG:
         case Opcodes.LNEG:
         case Opcodes.FNEG:
         case Opcodes.DNEG:
-          instructions.add(new CfUnop(opcode));
+          instructions.add(CfNeg.fromAsm(opcode));
           break;
         case Opcodes.ISHL:
         case Opcodes.LSHL:
@@ -877,7 +880,7 @@
         case Opcodes.LOR:
         case Opcodes.IXOR:
         case Opcodes.LXOR:
-          instructions.add(new CfBinop(opcode));
+          instructions.add(CfLogicalBinop.fromAsm(opcode));
           break;
         case Opcodes.I2L:
         case Opcodes.I2F:
@@ -894,14 +897,14 @@
         case Opcodes.I2B:
         case Opcodes.I2C:
         case Opcodes.I2S:
-          instructions.add(new CfUnop(opcode));
+          instructions.add(CfNumberConversion.fromAsm(opcode));
           break;
         case Opcodes.LCMP:
         case Opcodes.FCMPL:
         case Opcodes.FCMPG:
         case Opcodes.DCMPL:
         case Opcodes.DCMPG:
-          instructions.add(new CfBinop(opcode));
+          instructions.add(CfCmp.fromAsm(opcode));
           break;
         case Opcodes.IRETURN:
           instructions.add(new CfReturn(ValueType.INT));
diff --git a/src/main/java/com/android/tools/r8/ir/code/Add.java b/src/main/java/com/android/tools/r8/ir/code/Add.java
index a147d21..d5896da 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Add.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Add.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
 import com.android.tools.r8.code.AddDouble;
 import com.android.tools.r8.code.AddDouble2Addr;
 import com.android.tools.r8.code.AddFloat;
@@ -14,8 +15,6 @@
 import com.android.tools.r8.code.AddIntLit8;
 import com.android.tools.r8.code.AddLong;
 import com.android.tools.r8.code.AddLong2Addr;
-import com.android.tools.r8.errors.Unreachable;
-import org.objectweb.asm.Opcodes;
 
 public class Add extends ArithmeticBinop {
 
@@ -119,21 +118,7 @@
   }
 
   @Override
-  int getCfOpcode() {
-    switch (type) {
-      case BYTE:
-      case CHAR:
-      case SHORT:
-      case INT:
-        return Opcodes.IADD;
-      case FLOAT:
-        return Opcodes.FADD;
-      case LONG:
-        return Opcodes.LADD;
-      case DOUBLE:
-        return Opcodes.DADD;
-      default:
-        throw new Unreachable("Unexpected numeric type in add: " + type);
-    }
+  CfArithmeticBinop.Opcode getCfOpcode() {
+    return CfArithmeticBinop.Opcode.Add;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/And.java b/src/main/java/com/android/tools/r8/ir/code/And.java
index 461b1ec..283f369 100644
--- a/src/main/java/com/android/tools/r8/ir/code/And.java
+++ b/src/main/java/com/android/tools/r8/ir/code/And.java
@@ -4,14 +4,13 @@
 
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfLogicalBinop;
 import com.android.tools.r8.code.AndInt;
 import com.android.tools.r8.code.AndInt2Addr;
 import com.android.tools.r8.code.AndIntLit16;
 import com.android.tools.r8.code.AndIntLit8;
 import com.android.tools.r8.code.AndLong;
 import com.android.tools.r8.code.AndLong2Addr;
-import com.android.tools.r8.errors.Unreachable;
-import org.objectweb.asm.Opcodes;
 
 public class And extends LogicalBinop {
 
@@ -85,19 +84,7 @@
   }
 
   @Override
-  int getCfOpcode() {
-    switch (type) {
-      case BYTE:
-      case CHAR:
-      case SHORT:
-      case INT:
-      case FLOAT:
-        return Opcodes.IAND;
-      case LONG:
-      case DOUBLE:
-        return Opcodes.LAND;
-      default:
-        throw new Unreachable("Unexpected numeric type in logical and: " + type);
-    }
+  CfLogicalBinop.Opcode getCfOpcode() {
+    return CfLogicalBinop.Opcode.And;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
index b0b6b9e..296fba5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
@@ -3,11 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.ir.analysis.constant.Bottom;
 import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
 import com.android.tools.r8.ir.analysis.constant.LatticeElement;
+import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import java.util.function.Function;
 
@@ -157,4 +159,11 @@
     }
     return Bottom.getInstance();
   }
+
+  abstract CfArithmeticBinop.Opcode getCfOpcode();
+
+  @Override
+  public void buildCf(CfBuilder builder) {
+    builder.add(new CfArithmeticBinop(getCfOpcode(), type));
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Binop.java b/src/main/java/com/android/tools/r8/ir/code/Binop.java
index c2f21ac..9e6738a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Binop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Binop.java
@@ -7,13 +7,11 @@
 import static com.android.tools.r8.dex.Constants.U8BIT_MAX;
 
 import com.android.tools.r8.cf.LoadStoreHelper;
-import com.android.tools.r8.cf.code.CfBinop;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.PrimitiveTypeLatticeElement;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
-import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
@@ -136,13 +134,6 @@
     helper.storeOutValue(this, it);
   }
 
-  abstract int getCfOpcode();
-
-  @Override
-  public void buildCf(CfBuilder builder) {
-    builder.add(new CfBinop(getCfOpcode()));
-  }
-
   @Override
   public TypeLatticeElement evaluate(
       AppInfo appInfo, Function<Value, TypeLatticeElement> getLatticeElement) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Cmp.java b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
index 2e84e4e..239e404 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Cmp.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfCmp;
 import com.android.tools.r8.code.CmpLong;
 import com.android.tools.r8.code.CmpgDouble;
 import com.android.tools.r8.code.CmpgFloat;
@@ -13,12 +14,12 @@
 import com.android.tools.r8.ir.analysis.constant.Bottom;
 import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
 import com.android.tools.r8.ir.analysis.constant.LatticeElement;
+import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.utils.LongInterval;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.StringUtils.BraceType;
 import java.util.function.Function;
-import org.objectweb.asm.Opcodes;
 
 public class Cmp extends Binop {
 
@@ -222,17 +223,7 @@
   }
 
   @Override
-  int getCfOpcode() {
-    switch (type) {
-      case LONG:
-        assert bias == Bias.NONE;
-        return Opcodes.LCMP;
-      case FLOAT:
-        return bias == Bias.GT ? Opcodes.FCMPG : Opcodes.FCMPL;
-      case DOUBLE:
-        return bias == Bias.GT ? Opcodes.DCMPG : Opcodes.DCMPL;
-      default:
-        throw new Unreachable("Unexpected cmp type: " + type);
-    }
+  public void buildCf(CfBuilder builder) {
+    builder.add(new CfCmp(bias, type));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Div.java b/src/main/java/com/android/tools/r8/ir/code/Div.java
index 0add78c..6c9537a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Div.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Div.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
 import com.android.tools.r8.code.DivDouble;
 import com.android.tools.r8.code.DivDouble2Addr;
 import com.android.tools.r8.code.DivFloat;
@@ -13,11 +14,9 @@
 import com.android.tools.r8.code.DivIntLit8;
 import com.android.tools.r8.code.DivLong;
 import com.android.tools.r8.code.DivLong2Addr;
-import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.ir.analysis.constant.Bottom;
 import com.android.tools.r8.ir.analysis.constant.LatticeElement;
 import java.util.function.Function;
-import org.objectweb.asm.Opcodes;
 
 public class Div extends ArithmeticBinop {
 
@@ -140,21 +139,7 @@
   }
 
   @Override
-  int getCfOpcode() {
-    switch (type) {
-      case BYTE:
-      case CHAR:
-      case SHORT:
-      case INT:
-        return Opcodes.IDIV;
-      case FLOAT:
-        return Opcodes.FDIV;
-      case LONG:
-        return Opcodes.LDIV;
-      case DOUBLE:
-        return Opcodes.DDIV;
-      default:
-        throw new Unreachable("Unexpected numeric type: " + type);
-    }
+  CfArithmeticBinop.Opcode getCfOpcode() {
+    return CfArithmeticBinop.Opcode.Div;
   }
 }
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 8314e3f..4b34ca1 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
@@ -3,11 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfLogicalBinop;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.ir.analysis.constant.Bottom;
 import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
 import com.android.tools.r8.ir.analysis.constant.LatticeElement;
+import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import java.util.function.Function;
 
@@ -133,4 +135,11 @@
     }
     return Bottom.getInstance();
   }
+
+  abstract CfLogicalBinop.Opcode getCfOpcode();
+
+  @Override
+  public void buildCf(CfBuilder builder) {
+    builder.add(new CfLogicalBinop(getCfOpcode(), type));
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Mul.java b/src/main/java/com/android/tools/r8/ir/code/Mul.java
index b78d288..7407f2e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Mul.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Mul.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
 import com.android.tools.r8.code.MulDouble;
 import com.android.tools.r8.code.MulDouble2Addr;
 import com.android.tools.r8.code.MulFloat;
@@ -14,8 +15,6 @@
 import com.android.tools.r8.code.MulIntLit8;
 import com.android.tools.r8.code.MulLong;
 import com.android.tools.r8.code.MulLong2Addr;
-import com.android.tools.r8.errors.Unreachable;
-import org.objectweb.asm.Opcodes;
 
 public class Mul extends ArithmeticBinop {
 
@@ -131,21 +130,7 @@
   }
 
   @Override
-  int getCfOpcode() {
-    switch (type) {
-      case BYTE:
-      case CHAR:
-      case SHORT:
-      case INT:
-        return Opcodes.IMUL;
-      case FLOAT:
-        return Opcodes.FMUL;
-      case LONG:
-        return Opcodes.LMUL;
-      case DOUBLE:
-        return Opcodes.DMUL;
-      default:
-        throw new Unreachable("Unexpected numeric type: " + type);
-    }
+  CfArithmeticBinop.Opcode getCfOpcode() {
+    return CfArithmeticBinop.Opcode.Mul;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Neg.java b/src/main/java/com/android/tools/r8/ir/code/Neg.java
index f6e91a4..16b2922 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Neg.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Neg.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfNeg;
 import com.android.tools.r8.code.NegDouble;
 import com.android.tools.r8.code.NegFloat;
 import com.android.tools.r8.code.NegInt;
@@ -11,9 +12,9 @@
 import com.android.tools.r8.ir.analysis.constant.Bottom;
 import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
 import com.android.tools.r8.ir.analysis.constant.LatticeElement;
+import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import java.util.function.Function;
-import org.objectweb.asm.Opcodes;
 
 public class Neg extends Unop {
 
@@ -99,18 +100,7 @@
   }
 
   @Override
-  public int getCfOpcode() {
-    switch (type) {
-      case INT:
-        return Opcodes.INEG;
-      case FLOAT:
-        return Opcodes.FNEG;
-      case LONG:
-        return Opcodes.LNEG;
-      case DOUBLE:
-        return Opcodes.DNEG;
-      default:
-        throw new Unreachable("Unexpected type: " + type);
-    }
+  public void buildCf(CfBuilder builder) {
+    builder.add(new CfNeg(type));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Not.java b/src/main/java/com/android/tools/r8/ir/code/Not.java
index 8b35b02..1037c34 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Not.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Not.java
@@ -4,8 +4,8 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.cf.LoadStoreHelper;
-import com.android.tools.r8.cf.code.CfBinop;
 import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
 import com.android.tools.r8.code.NotInt;
 import com.android.tools.r8.code.NotLong;
 import com.android.tools.r8.errors.Unreachable;
@@ -15,7 +15,6 @@
 import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import java.util.function.Function;
-import org.objectweb.asm.Opcodes;
 
 public class Not extends Unop {
 
@@ -96,13 +95,8 @@
   }
 
   @Override
-  public int getCfOpcode() {
-    throw new Unreachable("Unexpected request for 'not' opcode which is translated to 'xor -1'");
-  }
-
-  @Override
   public void buildCf(CfBuilder builder) {
     builder.add(new CfConstNumber(-1, ValueType.fromNumericType(type)));
-    builder.add(new CfBinop(type.isWide() ? Opcodes.LXOR : Opcodes.IXOR));
+    builder.add(new CfLogicalBinop(CfLogicalBinop.Opcode.Xor, type));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
index bc3be95..f1c0049 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfNumberConversion;
 import com.android.tools.r8.code.DoubleToFloat;
 import com.android.tools.r8.code.DoubleToInt;
 import com.android.tools.r8.code.DoubleToLong;
@@ -19,8 +20,8 @@
 import com.android.tools.r8.code.LongToFloat;
 import com.android.tools.r8.code.LongToInt;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.conversion.DexBuilder;
-import org.objectweb.asm.Opcodes;
 
 public class NumberConversion extends Unop {
 
@@ -145,60 +146,7 @@
   }
 
   @Override
-  public int getCfOpcode() {
-    switch (from) {
-      case INT:
-        switch (to) {
-          case BYTE:
-            return Opcodes.I2B;
-          case CHAR:
-            return Opcodes.I2C;
-          case SHORT:
-            return Opcodes.I2S;
-          case FLOAT:
-            return Opcodes.I2F;
-          case LONG:
-            return Opcodes.I2L;
-          case DOUBLE:
-            return Opcodes.I2D;
-          default:
-            throw new Unreachable("Unexpected type conversion: " + from + " -> " + to);
-        }
-      case FLOAT:
-        switch (to) {
-          case INT:
-            return Opcodes.F2I;
-          case LONG:
-            return Opcodes.F2L;
-          case DOUBLE:
-            return Opcodes.F2D;
-          default:
-            throw new Unreachable("Unexpected type conversion: " + from + " -> " + to);
-        }
-      case LONG:
-        switch (to) {
-          case INT:
-            return Opcodes.L2I;
-          case FLOAT:
-            return Opcodes.L2F;
-          case DOUBLE:
-            return Opcodes.L2D;
-          default:
-            throw new Unreachable("Unexpected type conversion: " + from + " -> " + to);
-        }
-      case DOUBLE:
-        switch (to) {
-          case INT:
-            return Opcodes.D2I;
-          case LONG:
-            return Opcodes.D2L;
-          case FLOAT:
-            return Opcodes.D2F;
-          default:
-            throw new Unreachable("Unexpected type conversion: " + from + " -> " + to);
-        }
-      default:
-        throw new Unreachable("Unexpected type conversion: " + from + " -> " + to);
-    }
+  public void buildCf(CfBuilder builder) {
+    builder.add(new CfNumberConversion(from, to));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Or.java b/src/main/java/com/android/tools/r8/ir/code/Or.java
index 4e5dc9c..9136be3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Or.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Or.java
@@ -3,14 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfLogicalBinop;
 import com.android.tools.r8.code.OrInt;
 import com.android.tools.r8.code.OrInt2Addr;
 import com.android.tools.r8.code.OrIntLit16;
 import com.android.tools.r8.code.OrIntLit8;
 import com.android.tools.r8.code.OrLong;
 import com.android.tools.r8.code.OrLong2Addr;
-import com.android.tools.r8.errors.Unreachable;
-import org.objectweb.asm.Opcodes;
 
 public class Or extends LogicalBinop {
 
@@ -84,17 +83,7 @@
   }
 
   @Override
-  int getCfOpcode() {
-    switch (type) {
-      case BYTE:
-      case CHAR:
-      case SHORT:
-      case INT:
-        return Opcodes.IOR;
-      case LONG:
-        return Opcodes.LOR;
-      default:
-        throw new Unreachable("Unexpected numeric type for or: " + type);
-    }
+  CfLogicalBinop.Opcode getCfOpcode() {
+    return CfLogicalBinop.Opcode.Or;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Rem.java b/src/main/java/com/android/tools/r8/ir/code/Rem.java
index d584eed..02eb712 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Rem.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Rem.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
 import com.android.tools.r8.code.RemDouble;
 import com.android.tools.r8.code.RemDouble2Addr;
 import com.android.tools.r8.code.RemFloat;
@@ -13,11 +14,9 @@
 import com.android.tools.r8.code.RemIntLit8;
 import com.android.tools.r8.code.RemLong;
 import com.android.tools.r8.code.RemLong2Addr;
-import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.ir.analysis.constant.Bottom;
 import com.android.tools.r8.ir.analysis.constant.LatticeElement;
 import java.util.function.Function;
-import org.objectweb.asm.Opcodes;
 
 public class Rem extends ArithmeticBinop {
 
@@ -140,21 +139,7 @@
   }
 
   @Override
-  int getCfOpcode() {
-    switch (type) {
-      case BYTE:
-      case CHAR:
-      case SHORT:
-      case INT:
-        return Opcodes.IREM;
-      case FLOAT:
-        return Opcodes.FREM;
-      case LONG:
-        return Opcodes.LREM;
-      case DOUBLE:
-        return Opcodes.DREM;
-      default:
-        throw new Unreachable("Unexpected numeric type: " + type);
-    }
+  CfArithmeticBinop.Opcode getCfOpcode() {
+    return CfArithmeticBinop.Opcode.Rem;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Shl.java b/src/main/java/com/android/tools/r8/ir/code/Shl.java
index c959740..57b3190 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Shl.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Shl.java
@@ -3,13 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfLogicalBinop;
 import com.android.tools.r8.code.ShlInt;
 import com.android.tools.r8.code.ShlInt2Addr;
 import com.android.tools.r8.code.ShlIntLit8;
 import com.android.tools.r8.code.ShlLong;
 import com.android.tools.r8.code.ShlLong2Addr;
 import com.android.tools.r8.errors.Unreachable;
-import org.objectweb.asm.Opcodes;
 
 public class Shl extends LogicalBinop {
 
@@ -89,17 +89,7 @@
   }
 
   @Override
-  int getCfOpcode() {
-    switch (type) {
-      case BYTE:
-      case CHAR:
-      case SHORT:
-      case INT:
-        return Opcodes.ISHL;
-      case LONG:
-        return Opcodes.LSHL;
-      default:
-        throw new Unreachable("Unexpected numeric type in shift: " + type);
-    }
+  CfLogicalBinop.Opcode getCfOpcode() {
+    return CfLogicalBinop.Opcode.Shl;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Shr.java b/src/main/java/com/android/tools/r8/ir/code/Shr.java
index 35f8f61..18c38ed 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Shr.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Shr.java
@@ -3,13 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfLogicalBinop;
 import com.android.tools.r8.code.ShrInt;
 import com.android.tools.r8.code.ShrInt2Addr;
 import com.android.tools.r8.code.ShrIntLit8;
 import com.android.tools.r8.code.ShrLong;
 import com.android.tools.r8.code.ShrLong2Addr;
 import com.android.tools.r8.errors.Unreachable;
-import org.objectweb.asm.Opcodes;
 
 public class Shr extends LogicalBinop {
 
@@ -89,17 +89,7 @@
   }
 
   @Override
-  int getCfOpcode() {
-    switch (type) {
-      case BYTE:
-      case CHAR:
-      case SHORT:
-      case INT:
-        return Opcodes.ISHR;
-      case LONG:
-        return Opcodes.LSHR;
-      default:
-        throw new Unreachable("Unexpected numeric type in shift: " + type);
-    }
+  CfLogicalBinop.Opcode getCfOpcode() {
+    return CfLogicalBinop.Opcode.Shr;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Sub.java b/src/main/java/com/android/tools/r8/ir/code/Sub.java
index abd6c60..b263e63 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Sub.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Sub.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
 import com.android.tools.r8.code.AddIntLit16;
 import com.android.tools.r8.code.AddIntLit8;
 import com.android.tools.r8.code.RsubInt;
@@ -19,7 +20,6 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.ir.conversion.DexBuilder;
-import org.objectweb.asm.Opcodes;
 
 public class Sub extends ArithmeticBinop {
 
@@ -208,21 +208,7 @@
   }
 
   @Override
-  int getCfOpcode() {
-    switch (type) {
-      case BYTE:
-      case CHAR:
-      case SHORT:
-      case INT:
-        return Opcodes.ISUB;
-      case FLOAT:
-        return Opcodes.FSUB;
-      case LONG:
-        return Opcodes.LSUB;
-      case DOUBLE:
-        return Opcodes.DSUB;
-      default:
-        throw new Unreachable("Unexpected numeric type: " + type);
-    }
+  CfArithmeticBinop.Opcode getCfOpcode() {
+    return CfArithmeticBinop.Opcode.Sub;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Unop.java b/src/main/java/com/android/tools/r8/ir/code/Unop.java
index 94758fb..6db3ad5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Unop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Unop.java
@@ -4,13 +4,11 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.cf.LoadStoreHelper;
-import com.android.tools.r8.cf.code.CfUnop;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.PrimitiveTypeLatticeElement;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
-import com.android.tools.r8.ir.conversion.CfBuilder;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import java.util.function.Function;
@@ -60,13 +58,6 @@
     helper.storeOutValue(this, it);
   }
 
-  public abstract int getCfOpcode();
-
-  @Override
-  public void buildCf(CfBuilder builder) {
-    builder.add(new CfUnop(getCfOpcode()));
-  }
-
   @Override
   public TypeLatticeElement evaluate(
       AppInfo appInfo, Function<Value, TypeLatticeElement> getLatticeElement) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Ushr.java b/src/main/java/com/android/tools/r8/ir/code/Ushr.java
index e0fb715..d1e5a69 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Ushr.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Ushr.java
@@ -3,13 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfLogicalBinop;
 import com.android.tools.r8.code.UshrInt;
 import com.android.tools.r8.code.UshrInt2Addr;
 import com.android.tools.r8.code.UshrIntLit8;
 import com.android.tools.r8.code.UshrLong;
 import com.android.tools.r8.code.UshrLong2Addr;
 import com.android.tools.r8.errors.Unreachable;
-import org.objectweb.asm.Opcodes;
 
 public class Ushr extends LogicalBinop {
 
@@ -89,17 +89,7 @@
   }
 
   @Override
-  int getCfOpcode() {
-    switch (type) {
-      case BYTE:
-      case CHAR:
-      case SHORT:
-      case INT:
-        return Opcodes.IUSHR;
-      case LONG:
-        return Opcodes.LUSHR;
-      default:
-        throw new Unreachable("Unexpected numeric type in shift: " + type);
-    }
+  CfLogicalBinop.Opcode getCfOpcode() {
+    return CfLogicalBinop.Opcode.Ushr;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Xor.java b/src/main/java/com/android/tools/r8/ir/code/Xor.java
index 3050f70..2e9c497 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Xor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Xor.java
@@ -3,13 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.cf.code.CfLogicalBinop;
 import com.android.tools.r8.code.XorInt;
 import com.android.tools.r8.code.XorInt2Addr;
 import com.android.tools.r8.code.XorIntLit16;
 import com.android.tools.r8.code.XorIntLit8;
 import com.android.tools.r8.code.XorLong;
 import com.android.tools.r8.code.XorLong2Addr;
-import org.objectweb.asm.Opcodes;
 
 public class Xor extends LogicalBinop {
 
@@ -83,7 +83,7 @@
   }
 
   @Override
-  int getCfOpcode() {
-    return type.isWide() ? Opcodes.LXOR : Opcodes.IXOR;
+  CfLogicalBinop.Opcode getCfOpcode() {
+    return CfLogicalBinop.Opcode.Xor;
   }
 }