Merge "Reserved parent package prefix should be reserved indeed."
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index fa19a7c..e1f9c63 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -14,9 +14,13 @@
 
 public class CfCode extends Code {
 
+  private final int maxStack;
+  private final int maxLocals;
   private final List<CfInstruction> instructions;
 
-  public CfCode(List<CfInstruction> instructions) {
+  public CfCode(int maxStack, int maxLocals, List<CfInstruction> instructions) {
+    this.maxStack = maxStack;
+    this.maxLocals = maxLocals;
     this.instructions = instructions;
   }
 
@@ -35,8 +39,7 @@
       instruction.write(visitor);
     }
     visitor.visitEnd();
-    // TODO(zerny): Consider computing max-stack (and frames?) height as part of building Cf.
-    visitor.visitMaxs(0, 0); // Trigger computation of max stack (and frames).
+    visitor.visitMaxs(maxStack, maxLocals);
   }
 
   @Override
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 14a2a98..56d8de2 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
@@ -113,7 +113,7 @@
         default:
           throw new Unreachable("Unexpected numeric type " + type.name());
       }
-    } else if (!rightValue().needsRegister()) {
+    } else if (!needsValueInRegister(rightValue())) {
       assert !isSub();  // Constants in instructions for sub must be handled in subclass Sub.
       assert fitsInDexInstruction(rightValue());
       ConstNumber right = rightValue().getConstInstruction().asConstNumber();
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index e7b82d6..cdcf070 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -291,13 +291,38 @@
 
   public abstract int compareNonValueParts(Instruction other);
 
-  private boolean identicalAfterRegisterAllocation(
-      Value a, int aInstr, Value b, int bInstr, RegisterAllocator allocator) {
+  private boolean identicalInputAfterRegisterAllocation(
+      Value a, int aInstrNumber, Instruction bInstr, Value b, int bInstrNumber,
+      RegisterAllocator allocator) {
+    if (needsValueInRegister(a) != bInstr.needsValueInRegister(b)) {
+      return false;
+    }
+    if (needsValueInRegister(a)) {
+      if (allocator.getRegisterForValue(a, aInstrNumber) !=
+          allocator.getRegisterForValue(b, bInstrNumber)) {
+        return false;
+      }
+    } else {
+      ConstNumber aNum = a.getConstInstruction().asConstNumber();
+      ConstNumber bNum = b.getConstInstruction().asConstNumber();
+      if (!aNum.identicalNonValueNonPositionParts(bNum)) {
+        return false;
+      }
+    }
+    if (a.outType() != b.outType()) {
+      return false;
+    }
+    return true;
+  }
+
+  private boolean identicalOutputAfterRegisterAllocation(
+      Value a, int aInstrNumber, Value b, int bInstrNumber, RegisterAllocator allocator) {
     if (a.needsRegister() != b.needsRegister()) {
       return false;
     }
     if (a.needsRegister()) {
-      if (allocator.getRegisterForValue(a, aInstr) != allocator.getRegisterForValue(b, bInstr)) {
+      if (allocator.getRegisterForValue(a, aInstrNumber) !=
+          allocator.getRegisterForValue(b, bInstrNumber)) {
         return false;
       }
     } else {
@@ -327,7 +352,7 @@
       if (other.outValue == null) {
         return false;
       }
-      if (!identicalAfterRegisterAllocation(
+      if (!identicalOutputAfterRegisterAllocation(
           outValue, getNumber(), other.outValue, other.getNumber(), allocator)) {
         return false;
       }
@@ -341,7 +366,8 @@
     for (int j = 0; j < inValues.size(); j++) {
       Value in0 = inValues.get(j);
       Value in1 = other.inValues.get(j);
-      if (!identicalAfterRegisterAllocation(in0, getNumber(), in1, other.getNumber(), allocator)) {
+      if (!identicalInputAfterRegisterAllocation(in0, getNumber(), other, in1, other.getNumber(),
+          allocator)) {
         return false;
       }
     }
@@ -924,6 +950,6 @@
   public abstract Constraint inliningConstraint(AppInfoWithSubtyping info, DexType holder);
 
   public void insertLoadAndStores(InstructionListIterator it, StackHelper stack) {
-    throw new Unimplemented("Implment load/store insertion for: " + getInstructionName());
+    throw new Unimplemented("Implement load/store insertion for: " + getInstructionName());
   }
 }
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 5b19aab..efd0fc9 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
@@ -90,7 +90,7 @@
         default:
           throw new Unreachable("Unexpected type " + type);
       }
-    } else if (!rightValue().needsRegister()) {
+    } else if (!needsValueInRegister(rightValue())) {
       assert fitsInDexInstruction(rightValue());
       ConstNumber right = rightValue().getConstInstruction().asConstNumber();
       if (right.is8Bit()) {
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 4bf7e66..aca0df8 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
@@ -128,11 +128,11 @@
   // This is overridden to give the correct value when adding the negative constant.
   @Override
   int maxInOutValueRegisterSize() {
-    if (!leftValue().needsRegister()) {
+    if (!needsValueInRegister(leftValue())) {
       assert fitsInDexInstruction(leftValue());
       ConstNumber left = leftValue().getConstInstruction().asConstNumber();
       return left.is8Bit() ? Constants.U8BIT_MAX : Constants.U4BIT_MAX;
-    } else if (!rightValue().needsRegister()) {
+    } else if (!needsValueInRegister(rightValue())) {
       assert negativeFitsInDexInstruction(rightValue());
       ConstNumber right = rightValue().getConstInstruction().asConstNumber();
       return right.negativeIs8Bit() ? Constants.U8BIT_MAX : Constants.U4BIT_MAX;
@@ -167,7 +167,7 @@
     }
 
     com.android.tools.r8.code.Instruction instruction = null;
-    if (!leftValue().needsRegister()) {
+    if (!needsValueInRegister(leftValue())) {
       // Sub instructions with small left constant is emitted as rsub.
       assert fitsInDexInstruction(leftValue());
       ConstNumber left = leftValue().getConstInstruction().asConstNumber();
@@ -179,11 +179,11 @@
         assert left.is16Bit();
         instruction = new RsubInt(dest, right, left.getIntValue());
       }
-    } else if (!rightValue().needsRegister()) {
+    } else if (!needsValueInRegister(rightValue())) {
       // Sub instructions with small right constant are emitted as add of the negative constant.
       assert negativeFitsInDexInstruction(rightValue());
       int dest = builder.allocatedRegister(outValue, getNumber());
-      assert leftValue().needsRegister();
+      assert needsValueInRegister(leftValue());
       int left = builder.allocatedRegister(leftValue(), getNumber());
       ConstNumber right = rightValue().getConstInstruction().asConstNumber();
       if (right.negativeIs8Bit()) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 15b0f69..bc5df81 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.ir.code.Store;
 import com.android.tools.r8.ir.code.Value;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
 public class CfBuilder {
@@ -98,12 +99,35 @@
   }
 
   private CfCode buildCfCode() {
+    int maxLocalNumber = -1;
+    int maxStack = 0;
+    int currentStack = 0;
     instructions = new ArrayList<>();
-    InstructionIterator it = code.instructionIterator();
-    while (it.hasNext()) {
-      it.next().buildCf(this);
+    Iterator<BasicBlock> blockIterator = code.listIterator();
+    while (blockIterator.hasNext()) {
+      BasicBlock block = blockIterator.next();
+      InstructionIterator it = block.iterator();
+      while (it.hasNext()) {
+        Instruction instruction = it.next();
+        if (instruction.outValue() != null) {
+          Value outValue = instruction.outValue();
+          if (outValue instanceof StackValue) {
+            ++currentStack;
+            maxStack = Math.max(maxStack, currentStack);
+          } else {
+            maxLocalNumber = Math.max(maxLocalNumber, outValue.getNumber());
+          }
+        }
+        for (Value inValue : instruction.inValues()) {
+          if (inValue instanceof StackValue) {
+            --currentStack;
+          }
+        }
+        instruction.buildCf(this);
+      }
     }
-    return new CfCode(instructions);
+    assert currentStack == 0;
+    return new CfCode(maxStack, maxLocalNumber + 1, instructions);
   }
 
   // Callbacks
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index bd5ae63..ede2e85 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -58,7 +58,24 @@
   }
 
   private void writeClass(DexProgramClass clazz, OutputSink outputSink) throws IOException {
-    ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+    // If there are CF representations for all methods manually compute stack height and frames.
+    // TODO(zerny): This is a temporary hack since we will need to manually compute stack maps for
+    // any methods that are IR processed.
+    int flags = 0;
+    for (DexEncodedMethod method : clazz.directMethods()) {
+      if (!method.getCode().isCfCode()) {
+        flags = ClassWriter.COMPUTE_FRAMES;
+        break;
+      }
+    }
+    if (flags == 0) {
+      for (DexEncodedMethod method : clazz.virtualMethods()) {
+        if (!method.getCode().isCfCode()) {
+          flags = ClassWriter.COMPUTE_FRAMES;
+        }
+      }
+    }
+    ClassWriter writer = new ClassWriter(flags);
     writer.visitSource(clazz.sourceFile.toString(), null);
     int version = clazz.getClassFileVersion();
     int access = clazz.accessFlags.getAsCfAccessFlags();