Implement join of single frame types

Change-Id: Id0ce02ad60956aa6a02b066de7df5d885a6f3a3a
diff --git a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
index 6fcc84a..cfa791d 100644
--- a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
@@ -542,7 +542,7 @@
       }
     } else {
       assert frameType.isInitialized();
-      if (frameType.isNull()) {
+      if (frameType.isNullType()) {
         return frameTypeType() + ".initialized(" + dexItemFactoryType() + ".nullValueType)";
       } else {
         return frameTypeType()
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
index 642f492..94c2a25 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -75,11 +75,11 @@
       return UninitializedThis.SINGLETON;
     }
 
-    public static FrameType top() {
+    public static Top top() {
       return Top.SINGLETON;
     }
 
-    public static FrameType oneWord() {
+    public static OneWord oneWord() {
       return OneWord.SINGLETON;
     }
 
@@ -101,7 +101,7 @@
       return false;
     }
 
-    public boolean isNull() {
+    public boolean isNullType() {
       return false;
     }
 
@@ -290,7 +290,33 @@
 
     @Override
     public SingleFrameType join(SingleFrameType frameType) {
-      // TODO(b/214496607): Implement this.
+      if (equals(frameType)) {
+        return this;
+      }
+      if (frameType.isOneWord() || frameType.isTop()) {
+        return frameType;
+      }
+      if (frameType.isUninitializedObject()) {
+        return oneWord();
+      }
+      assert frameType.isInitialized();
+      DexType otherType = frameType.asSingleInitializedType().getInitializedType();
+      assert type != otherType;
+      if (type.isPrimitiveType()) {
+        // TODO(b/214496607): Should two different non-wide primitives join to int instead of
+        //  OneWord?
+        return oneWord();
+      }
+      assert type.isReferenceType();
+      if (isNullType()) {
+        return otherType.isReferenceType() ? frameType : oneWord();
+      }
+      if (frameType.isNullType()) {
+        return this;
+      }
+      assert type.isArrayType() || type.isClassType();
+      assert otherType.isArrayType() || otherType.isClassType();
+      // TODO(b/214496607): Implement join of different reference types using class hierarchy.
       throw new Unimplemented();
     }
 
@@ -368,7 +394,7 @@
     }
 
     @Override
-    public boolean isNull() {
+    public boolean isNullType() {
       return type.isNullValueType();
     }
 
@@ -475,8 +501,7 @@
 
     @Override
     public SingleFrameType join(SingleFrameType frameType) {
-      // TODO(b/214496607): Implement this.
-      throw new Unimplemented();
+      return this;
     }
 
     @Override
@@ -512,8 +537,14 @@
 
     @Override
     public SingleFrameType join(SingleFrameType frameType) {
-      // TODO(b/214496607): Implement this.
-      throw new Unimplemented();
+      if (equals(frameType)) {
+        return this;
+      }
+      // TODO(b/231126561): By unifying OneWord and Top, this could always return OneWord.
+      if (frameType.isTop()) {
+        return frameType;
+      }
+      return oneWord();
     }
 
     @Override
@@ -587,8 +618,14 @@
 
     @Override
     public SingleFrameType join(SingleFrameType frameType) {
-      // TODO(b/214496607): Implement this.
-      throw new Unimplemented();
+      if (this == frameType) {
+        return this;
+      }
+      // TODO(b/231126561): By unifying OneWord and Top, this could always return OneWord.
+      if (frameType.isTop()) {
+        return frameType;
+      }
+      return oneWord();
     }
 
     @Override
@@ -635,8 +672,8 @@
 
     @Override
     public SingleFrameType join(SingleFrameType frameType) {
-      // TODO(b/214496607): Implement this.
-      throw new Unimplemented();
+      // TODO(b/231126561): By unifying OneWord and Top this could always return `this`.
+      return frameType.isTop() ? frameType : this;
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/SingleFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/SingleFrameType.java
index 2f1ead8..0bfde72 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/SingleFrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/SingleFrameType.java
@@ -5,10 +5,25 @@
 package com.android.tools.r8.cf.code.frame;
 
 import com.android.tools.r8.cf.code.CfFrame.FrameType;
+import com.android.tools.r8.cf.code.CfFrame.SingleInitializedType;
 
 public interface SingleFrameType {
 
   FrameType asFrameType();
 
+  boolean isInitialized();
+
+  SingleInitializedType asSingleInitializedType();
+
+  boolean isNullType();
+
+  boolean isOneWord();
+
+  boolean isTop();
+
+  boolean isUninitializedObject();
+
+  boolean isUninitializedThis();
+
   SingleFrameType join(SingleFrameType frameType);
 }