Add non-null IR after zero test.

NonNullMarker currently adds non-null IR after implicit instructions,
such as invoke-*, (i|a)(get|put). They are implicit in the sense that
not raising NPE implies the receiver or array is not null.  There is one
more case we can add non-null: explicit null check (actually zero test).

Since NonNullMarker is used before conducting type analysis, we do not
know whether the given value is reference type or not. Though, it is
safe to add non-null IR, since NonNull#evalute will return the type
lattice as-is if it is not a reference type.

Bug: 70794661
Change-Id: I8b32d3b5377801bdf15d3c43f719edc4fd811831
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 02a2dd9..8663b91 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -400,6 +400,10 @@
     return instructions;
   }
 
+  public boolean isEmpty() {
+    return instructions.isEmpty();
+  }
+
   public Instruction entry() {
     return instructions.get(0);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullMarker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullMarker.java
index a9fff4b..9bc2918 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullMarker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullMarker.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.DominatorTree;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.If;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionIterator;
 import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -58,6 +59,7 @@
     ListIterator<BasicBlock> blocks = code.blocks.listIterator();
     while (blocks.hasNext()) {
       BasicBlock block = blocks.next();
+      // Add non-null after instructions that implicitly indicate receiver/array is not null.
       InstructionListIterator iterator = block.listIterator();
       while (iterator.hasNext()) {
         Instruction current = iterator.next();
@@ -111,11 +113,12 @@
         // block or the new split-off block. Since NPE can be explicitly caught, nullness should be
         // propagated through dominance.
         Set<Instruction> users = knownToBeNonNullValue.uniqueUsers();
-        Set<Phi> phiUsers = knownToBeNonNullValue.uniquePhiUsers();
         Set<Instruction> dominatedUsers = Sets.newIdentityHashSet();
         Set<Phi> dominatedPhiUsers = Sets.newIdentityHashSet();
         DominatorTree dominatorTree = new DominatorTree(code);
+        Set<BasicBlock> dominatedBlocks = Sets.newIdentityHashSet();
         for (BasicBlock dominatee : dominatorTree.dominatedBlocks(blockWithNonNullInstruction)) {
+          dominatedBlocks.add(dominatee);
           InstructionListIterator dominateeIterator = dominatee.listIterator();
           if (dominatee == blockWithNonNullInstruction) {
             // In the block with the inserted non null instruction, skip instructions up to and
@@ -129,15 +132,80 @@
               dominatedUsers.add(potentialUser);
             }
           }
-          for (Phi phi : dominatee.getPhis()) {
-            if (phiUsers.contains(phi)) {
-              dominatedPhiUsers.add(phi);
-            }
+        }
+        for (Phi user : knownToBeNonNullValue.uniquePhiUsers()) {
+          if (dominatedBlocks.contains(user.getBlock())) {
+            dominatedPhiUsers.add(user);
           }
         }
         knownToBeNonNullValue.replaceSelectiveUsers(
             nonNullValue, dominatedUsers, dominatedPhiUsers);
       }
+
+      // Add non-null on top of the successor block if the current block ends with a null check.
+      if (block.exit().isIf() && block.exit().asIf().isZeroTest()) {
+        // if v EQ blockX
+        // ... (fallthrough)
+        // blockX: ...
+        //
+        //   ~>
+        //
+        // if v EQ blockX
+        // non_null_value <- non-null(v)
+        // ...
+        // blockX: ...
+        //
+        // or
+        //
+        // if v NE blockY
+        // ...
+        // blockY: ...
+        //
+        //   ~>
+        //
+        // blockY: non_null_value <- non-null(v)
+        // ...
+        If theIf = block.exit().asIf();
+        Value knownToBeNonNullValue = theIf.inValues().get(0);
+        // Avoid adding redundant non-null instruction.
+        if (!knownToBeNonNullValue.isNeverNull()) {
+          BasicBlock target = theIf.targetFromCondition(1L);
+          // Ignore uncommon empty blocks.
+          if (!target.isEmpty()) {
+            DominatorTree dominatorTree = new DominatorTree(code);
+            // Make sure there are no paths to the target block without passing the current block.
+            if (dominatorTree.dominatedBy(target, block)) {
+              // Collect users of the original value that are dominated by the target block.
+              Set<Instruction> dominatedUsers = Sets.newIdentityHashSet();
+              Set<Phi> dominatedPhiUsers = Sets.newIdentityHashSet();
+              Set<BasicBlock> dominatedBlocks =
+                  Sets.newHashSet(dominatorTree.dominatedBlocks(target));
+              for (Instruction user : knownToBeNonNullValue.uniqueUsers()) {
+                if (dominatedBlocks.contains(user.getBlock())) {
+                  dominatedUsers.add(user);
+                }
+              }
+              for (Phi user : knownToBeNonNullValue.uniquePhiUsers()) {
+                if (dominatedBlocks.contains(user.getBlock())) {
+                  dominatedPhiUsers.add(user);
+                }
+              }
+              // Avoid adding a non-null for the value without meaningful users.
+              if (!dominatedUsers.isEmpty() && !dominatedPhiUsers.isEmpty()) {
+                Value nonNullValue = code.createValue(
+                    knownToBeNonNullValue.outType(), knownToBeNonNullValue.getLocalInfo());
+                NonNull nonNull = new NonNull(nonNullValue, knownToBeNonNullValue);
+                InstructionListIterator targetIterator = target.listIterator();
+                nonNull.setPosition(targetIterator.next().getPosition());
+                targetIterator.previous();
+                targetIterator.add(nonNull);
+                knownToBeNonNullValue.replaceSelectiveUsers(
+                    nonNullValue, dominatedUsers, dominatedPhiUsers);
+              }
+            }
+          }
+        }
+      }
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
index 39cf369..1e8872e 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
@@ -24,12 +24,14 @@
 import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.NonNull;
 import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.nonnull.NonNullAfterArrayAccess;
-import com.android.tools.r8.ir.nonnull.NonNullAfterFieldAccess;
-import com.android.tools.r8.ir.nonnull.NonNullAfterInvoke;
+import com.android.tools.r8.ir.optimize.nonnull.FieldAccessTest;
+import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterArrayAccess;
+import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterFieldAccess;
+import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterInvoke;
 import com.android.tools.r8.ir.optimize.NonNullMarker;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Timing;
@@ -112,7 +114,7 @@
     buildAndTest(NonNullAfterInvoke.class, signature, false, (appInfo, typeAnalysis) -> {
       DexType assertionErrorType = appInfo.dexItemFactory.createType("Ljava/lang/AssertionError;");
       DexType mainClass = appInfo.dexItemFactory.createType(
-          "Lcom/android/tools/r8/ir/nonnull/NonNullAfterInvoke;");
+          DescriptorUtils.javaTypeToDescriptor(NonNullAfterInvoke.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
           InvokeVirtual.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, true),
           NonNull.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, false),
@@ -128,7 +130,7 @@
     buildAndTest(NonNullAfterInvoke.class, signature, true, (appInfo, typeAnalysis) -> {
       DexType assertionErrorType = appInfo.dexItemFactory.createType("Ljava/lang/AssertionError;");
       DexType mainClass = appInfo.dexItemFactory.createType(
-          "Lcom/android/tools/r8/ir/nonnull/NonNullAfterInvoke;");
+          DescriptorUtils.javaTypeToDescriptor(NonNullAfterInvoke.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
           InvokeVirtual.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, true),
           NonNull.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, false),
@@ -144,7 +146,7 @@
     buildAndTest(NonNullAfterArrayAccess.class, signature, false, (appInfo, typeAnalysis) -> {
       DexType assertionErrorType = appInfo.dexItemFactory.createType("Ljava/lang/AssertionError;");
       DexType mainClass = appInfo.dexItemFactory.createType(
-          "Lcom/android/tools/r8/ir/nonnull/NonNullAfterArrayAccess;");
+          DescriptorUtils.javaTypeToDescriptor(NonNullAfterArrayAccess.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
           // An element inside a non-null array could be null.
           ArrayGet.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, true),
@@ -170,7 +172,7 @@
     buildAndTest(NonNullAfterArrayAccess.class, signature, true, (appInfo, typeAnalysis) -> {
       DexType assertionErrorType = appInfo.dexItemFactory.createType("Ljava/lang/AssertionError;");
       DexType mainClass = appInfo.dexItemFactory.createType(
-          "Lcom/android/tools/r8/ir/nonnull/NonNullAfterArrayAccess;");
+          DescriptorUtils.javaTypeToDescriptor(NonNullAfterArrayAccess.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
           // An element inside a non-null array could be null.
           ArrayGet.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, true),
@@ -192,13 +194,13 @@
   @Test
   public void nonNullAfterSafeFieldAccess() throws Exception {
     MethodSignature signature = new MethodSignature("foo", "int",
-        new String[]{"com.android.tools.r8.ir.nonnull.FieldAccessTest"});
+        new String[]{FieldAccessTest.class.getCanonicalName()});
     buildAndTest(NonNullAfterFieldAccess.class, signature, false, (appInfo, typeAnalysis) -> {
       DexType assertionErrorType = appInfo.dexItemFactory.createType("Ljava/lang/AssertionError;");
       DexType mainClass = appInfo.dexItemFactory.createType(
-          "Lcom/android/tools/r8/ir/nonnull/NonNullAfterFieldAccess;");
+          DescriptorUtils.javaTypeToDescriptor(NonNullAfterFieldAccess.class.getCanonicalName()));
       DexType testClass = appInfo.dexItemFactory.createType(
-          "Lcom/android/tools/r8/ir/nonnull/FieldAccessTest;");
+          DescriptorUtils.javaTypeToDescriptor(FieldAccessTest.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
           Argument.class, new ClassTypeLatticeElement(testClass, true),
           NonNull.class, new ClassTypeLatticeElement(testClass, false),
@@ -212,13 +214,13 @@
   @Test
   public void stillNullAfterExceptionCatch_iget() throws Exception {
     MethodSignature signature = new MethodSignature("bar", "int",
-        new String[]{"com.android.tools.r8.ir.nonnull.FieldAccessTest"});
+        new String[]{FieldAccessTest.class.getCanonicalName()});
     buildAndTest(NonNullAfterFieldAccess.class, signature, true, (appInfo, typeAnalysis) -> {
       DexType assertionErrorType = appInfo.dexItemFactory.createType("Ljava/lang/AssertionError;");
       DexType mainClass = appInfo.dexItemFactory.createType(
-          "Lcom/android/tools/r8/ir/nonnull/NonNullAfterFieldAccess;");
+          DescriptorUtils.javaTypeToDescriptor(NonNullAfterFieldAccess.class.getCanonicalName()));
       DexType testClass = appInfo.dexItemFactory.createType(
-          "Lcom/android/tools/r8/ir/nonnull/FieldAccessTest;");
+          DescriptorUtils.javaTypeToDescriptor(FieldAccessTest.class.getCanonicalName()));
       Map<Class<? extends Instruction>, TypeLatticeElement> expectedLattices = ImmutableMap.of(
           Argument.class, new ClassTypeLatticeElement(testClass, true),
           NonNull.class, new ClassTypeLatticeElement(testClass, false),
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullMarkerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullMarkerTest.java
index 496ada2..be81483 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullMarkerTest.java
@@ -16,9 +16,12 @@
 import com.android.tools.r8.ir.code.InstancePut;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionIterator;
-import com.android.tools.r8.ir.nonnull.NonNullAfterArrayAccess;
-import com.android.tools.r8.ir.nonnull.NonNullAfterFieldAccess;
-import com.android.tools.r8.ir.nonnull.NonNullAfterInvoke;
+import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
+import com.android.tools.r8.ir.optimize.nonnull.FieldAccessTest;
+import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterArrayAccess;
+import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterFieldAccess;
+import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterInvoke;
+import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterNullCheck;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
@@ -70,25 +73,63 @@
       curr = it.next();
       if (curr.isNonNull()) {
         // Make sure non-null is added to the right place.
-        assertTrue(prev == null || NonNullMarker.throwsOnNullInput(prev));
+        assertTrue(prev == null
+            || NonNullMarker.throwsOnNullInput(prev)
+            || (prev.isIf() && prev.asIf().isZeroTest())
+            || !curr.getBlock().getPredecessors().contains(prev.getBlock()));
         count++;
       }
     }
-    assertEquals(count, expectedOccurrences);
+    assertEquals(expectedOccurrences, count);
+  }
+
+  private void checkInvokeGetsNonNullReceiver(IRCode irCode) {
+    checkInvokeReceiver(irCode, true);
+  }
+
+  private void checkInvokeGetsNullReceiver(IRCode irCode) {
+    checkInvokeReceiver(irCode, false);
+  }
+
+  private void checkInvokeReceiver(IRCode irCode, boolean isNotNull) {
+    InstructionIterator it = irCode.instructionIterator();
+    boolean metInvokeWithReceiver = false;
+    while (it.hasNext()) {
+      Instruction instruction = it.nextUntil(Instruction::isInvokeMethodWithReceiver);
+      if (instruction == null) {
+        break;
+      }
+      InvokeMethodWithReceiver invoke = instruction.asInvokeMethodWithReceiver();
+      if (invoke.isInvokeDirect()
+          || !invoke.getInvokedMethod().name.toString().contains("hashCode")) {
+        continue;
+      }
+      metInvokeWithReceiver = true;
+      if (isNotNull) {
+        assertTrue(invoke.getReceiver().isNeverNull()
+            || invoke.getReceiver().definition.isArgument());
+      } else {
+        assertFalse(invoke.getReceiver().isNeverNull());
+      }
+    }
+    assertTrue(metInvokeWithReceiver);
   }
 
   @Test
   public void nonNullAfterSafeInvokes() throws Exception {
-    MethodSignature signature =
+    MethodSignature foo =
         new MethodSignature("foo", "int", new String[]{"java.lang.String"});
-    buildAndTest(NonNullAfterInvoke.class, signature, 1, null);
+    buildAndTest(NonNullAfterInvoke.class, foo, 1, this::checkInvokeGetsNonNullReceiver);
+    MethodSignature bar =
+        new MethodSignature("bar", "int", new String[]{"java.lang.String"});
+    buildAndTest(NonNullAfterInvoke.class, bar, 2, this::checkInvokeGetsNullReceiver);
   }
 
   @Test
   public void nonNullAfterSafeArrayAccess() throws Exception {
-    MethodSignature signature =
+    MethodSignature foo =
         new MethodSignature("foo", "int", new String[]{"java.lang.String[]"});
-    buildAndTest(NonNullAfterArrayAccess.class, signature, 1, null);
+    buildAndTest(NonNullAfterArrayAccess.class, foo, 1, null);
   }
 
   @Test
@@ -100,15 +141,15 @@
 
   @Test
   public void nonNullAfterSafeFieldAccess() throws Exception {
-    MethodSignature signature = new MethodSignature("foo", "int",
-        new String[]{"com.android.tools.r8.ir.nonnull.FieldAccessTest"});
-    buildAndTest(NonNullAfterFieldAccess.class, signature, 1, null);
+    MethodSignature foo = new MethodSignature("foo", "int",
+        new String[]{FieldAccessTest.class.getCanonicalName()});
+    buildAndTest(NonNullAfterFieldAccess.class, foo, 1, null);
   }
 
   @Test
   public void avoidRedundantNonNull() throws Exception {
     MethodSignature signature = new MethodSignature("foo2", "int",
-        new String[]{"com.android.tools.r8.ir.nonnull.FieldAccessTest"});
+        new String[]{FieldAccessTest.class.getCanonicalName()});
     buildAndTest(NonNullAfterFieldAccess.class, signature, 1, ircode -> {
       // There are two InstancePut instructions of interest.
       int count = 0;
@@ -133,4 +174,16 @@
     });
   }
 
+  @Test
+  public void nonNullAfterNullCheck() throws Exception {
+    MethodSignature foo =
+        new MethodSignature("foo", "int", new String[]{"java.lang.String"});
+    buildAndTest(NonNullAfterNullCheck.class, foo, 1, this::checkInvokeGetsNonNullReceiver);
+    MethodSignature bar =
+        new MethodSignature("bar", "int", new String[]{"java.lang.String"});
+    buildAndTest(NonNullAfterNullCheck.class, bar, 1, this::checkInvokeGetsNonNullReceiver);
+    MethodSignature baz =
+        new MethodSignature("baz", "int", new String[]{"java.lang.String"});
+    buildAndTest(NonNullAfterNullCheck.class, baz, 1, this::checkInvokeGetsNullReceiver);
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
index c72cdcb..59f1184 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
@@ -10,9 +10,10 @@
 import com.android.tools.r8.code.Format22t;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.ir.nonnull.NonNullAfterArrayAccess;
-import com.android.tools.r8.ir.nonnull.NonNullAfterFieldAccess;
-import com.android.tools.r8.ir.nonnull.NonNullAfterInvoke;
+import com.android.tools.r8.ir.optimize.nonnull.FieldAccessTest;
+import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterArrayAccess;
+import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterFieldAccess;
+import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterInvoke;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
@@ -61,11 +62,11 @@
   @Test
   public void nonNullAfterSafeFieldAccess() throws Exception {
     MethodSignature foo = new MethodSignature("foo", "int",
-        new String[]{"com.android.tools.r8.ir.nonnull.FieldAccessTest"});
+        new String[]{FieldAccessTest.class.getCanonicalName()});
     MethodSignature bar = new MethodSignature("bar", "int",
-        new String[]{"com.android.tools.r8.ir.nonnull.FieldAccessTest"});
+        new String[]{FieldAccessTest.class.getCanonicalName()});
     MethodSignature foo2 = new MethodSignature("foo2", "int",
-        new String[]{"com.android.tools.r8.ir.nonnull.FieldAccessTest"});
+        new String[]{FieldAccessTest.class.getCanonicalName()});
     buildAndTest(NonNullAfterFieldAccess.class, ImmutableList.of(foo, bar, foo2));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/nonnull/FieldAccessTest.java b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/FieldAccessTest.java
similarity index 83%
rename from src/test/java/com/android/tools/r8/ir/nonnull/FieldAccessTest.java
rename to src/test/java/com/android/tools/r8/ir/optimize/nonnull/FieldAccessTest.java
index 09e94f7..dec12d5 100644
--- a/src/test/java/com/android/tools/r8/ir/nonnull/FieldAccessTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/FieldAccessTest.java
@@ -1,7 +1,7 @@
 // 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.ir.nonnull;
+package com.android.tools.r8.ir.optimize.nonnull;
 
 public class FieldAccessTest {
   String fld;
diff --git a/src/test/java/com/android/tools/r8/ir/nonnull/NonNullAfterArrayAccess.java b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/NonNullAfterArrayAccess.java
similarity index 95%
rename from src/test/java/com/android/tools/r8/ir/nonnull/NonNullAfterArrayAccess.java
rename to src/test/java/com/android/tools/r8/ir/optimize/nonnull/NonNullAfterArrayAccess.java
index 89ec208..92cedea 100644
--- a/src/test/java/com/android/tools/r8/ir/nonnull/NonNullAfterArrayAccess.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/NonNullAfterArrayAccess.java
@@ -1,7 +1,7 @@
 // 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.ir.nonnull;
+package com.android.tools.r8.ir.optimize.nonnull;
 
 public class NonNullAfterArrayAccess {
 
diff --git a/src/test/java/com/android/tools/r8/ir/nonnull/NonNullAfterFieldAccess.java b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/NonNullAfterFieldAccess.java
similarity index 96%
rename from src/test/java/com/android/tools/r8/ir/nonnull/NonNullAfterFieldAccess.java
rename to src/test/java/com/android/tools/r8/ir/optimize/nonnull/NonNullAfterFieldAccess.java
index 73ee6ea..3b7e950 100644
--- a/src/test/java/com/android/tools/r8/ir/nonnull/NonNullAfterFieldAccess.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/NonNullAfterFieldAccess.java
@@ -1,7 +1,7 @@
 // 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.ir.nonnull;
+package com.android.tools.r8.ir.optimize.nonnull;
 
 public class NonNullAfterFieldAccess {
 
diff --git a/src/test/java/com/android/tools/r8/ir/nonnull/NonNullAfterInvoke.java b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/NonNullAfterInvoke.java
similarity index 94%
rename from src/test/java/com/android/tools/r8/ir/nonnull/NonNullAfterInvoke.java
rename to src/test/java/com/android/tools/r8/ir/optimize/nonnull/NonNullAfterInvoke.java
index a2c472a..8aab7fe 100644
--- a/src/test/java/com/android/tools/r8/ir/nonnull/NonNullAfterInvoke.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/NonNullAfterInvoke.java
@@ -1,7 +1,7 @@
 // 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.ir.nonnull;
+package com.android.tools.r8.ir.optimize.nonnull;
 
 public class NonNullAfterInvoke {
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/nonnull/NonNullAfterNullCheck.java b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/NonNullAfterNullCheck.java
new file mode 100644
index 0000000..d943339
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/NonNullAfterNullCheck.java
@@ -0,0 +1,38 @@
+// 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.ir.optimize.nonnull;
+
+public class NonNullAfterNullCheck {
+
+  public static int foo(String arg) {
+    if (arg != null) {
+      return arg.contains("null") ? arg.hashCode() : 0;
+    }
+    return -1;
+  }
+
+  public static int bar(String arg) {
+    if (arg == null) {
+      return -1;
+    } else {
+      return arg.contains("null") ? arg.hashCode() : 0;
+    }
+  }
+
+  public static int baz(String arg) {
+    if (arg != null) {
+      if (arg == null) {
+        throw new AssertionError("Unreachable.");
+      }
+    }
+    // not dominated by null check.
+    return arg.hashCode();
+  }
+
+  public static void main(String[] args) {
+    foo("non-null");
+    bar("non-null");
+    baz("non-null");
+  }
+}