Merge "Fix longstanding bug around implicit NPEs and GC, version 2."
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index a13efca..a90ff3f 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -1394,37 +1394,12 @@
 }
 
 bool CodeGenerator::CanMoveNullCheckToUser(HNullCheck* null_check) {
-  HInstruction* first_next_not_move = null_check->GetNextDisregardingMoves();
-
-  return (first_next_not_move != nullptr)
-      && first_next_not_move->CanDoImplicitNullCheckOn(null_check->InputAt(0));
+  return null_check->IsEmittedAtUseSite();
 }
 
 void CodeGenerator::MaybeRecordImplicitNullCheck(HInstruction* instr) {
-  if (!compiler_options_.GetImplicitNullChecks()) {
-    return;
-  }
-
-  // If we are from a static path don't record the pc as we can't throw NPE.
-  // NB: having the checks here makes the code much less verbose in the arch
-  // specific code generators.
-  if (instr->IsStaticFieldSet() || instr->IsStaticFieldGet()) {
-    return;
-  }
-
-  if (!instr->CanDoImplicitNullCheckOn(instr->InputAt(0))) {
-    return;
-  }
-
-  // Find the first previous instruction which is not a move.
-  HInstruction* first_prev_not_move = instr->GetPreviousDisregardingMoves();
-
-  // If the instruction is a null check it means that `instr` is the first user
-  // and needs to record the pc.
-  if (first_prev_not_move != nullptr && first_prev_not_move->IsNullCheck()) {
-    HNullCheck* null_check = first_prev_not_move->AsNullCheck();
-    // TODO: The parallel moves modify the environment. Their changes need to be
-    // reverted otherwise the stack maps at the throw point will not be correct.
+  HNullCheck* null_check = instr->GetImplicitNullCheck();
+  if (null_check != nullptr) {
     RecordPcInfo(null_check, null_check->GetDexPc());
   }
 }
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 462225a..a460f77 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -5065,7 +5065,7 @@
     return;
   }
   {
-    // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
+    // Ensure that between load and RecordPcInfo there are no pools emitted.
     EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
     Location obj = instruction->GetLocations()->InAt(0);
     __ Ldr(wzr, HeapOperandFrom(obj, Offset(0)));
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index 86687e6..f186191 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -453,7 +453,7 @@
 
     ASSERT_FALSE(equal->IsEmittedAtUseSite());
     graph->BuildDominatorTree();
-    PrepareForRegisterAllocation(graph).Run();
+    PrepareForRegisterAllocation(graph, *compiler_options_).Run();
     ASSERT_TRUE(equal->IsEmittedAtUseSite());
 
     auto hook_before_codegen = [](HGraph* graph_in) {
diff --git a/compiler/optimizing/codegen_test_utils.h b/compiler/optimizing/codegen_test_utils.h
index 9181126..8c062f0 100644
--- a/compiler/optimizing/codegen_test_utils.h
+++ b/compiler/optimizing/codegen_test_utils.h
@@ -288,7 +288,7 @@
   {
     ScopedArenaAllocator local_allocator(graph->GetArenaStack());
     SsaLivenessAnalysis liveness(graph, codegen, &local_allocator);
-    PrepareForRegisterAllocation(graph).Run();
+    PrepareForRegisterAllocation(graph, codegen->GetCompilerOptions()).Run();
     liveness.Analyze();
     std::unique_ptr<RegisterAllocator> register_allocator =
         RegisterAllocator::Create(&local_allocator, codegen, liveness);
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index 0fb90fb..60f513c 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -38,7 +38,7 @@
   // on how instructions are ordered.
   RemoveSuspendChecks(graph);
   // `Inline` conditions into ifs.
-  PrepareForRegisterAllocation(graph).Run();
+  PrepareForRegisterAllocation(graph, *compiler_options_).Run();
   return graph;
 }
 
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index 72f995e..f11f7a9 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -47,7 +47,7 @@
 void LivenessTest::TestCode(const std::vector<uint16_t>& data, const char* expected) {
   HGraph* graph = CreateCFG(data);
   // `Inline` conditions into ifs.
-  PrepareForRegisterAllocation(graph).Run();
+  PrepareForRegisterAllocation(graph, *compiler_options_).Run();
   std::unique_ptr<CodeGenerator> codegen = CodeGenerator::Create(graph, *compiler_options_);
   SsaLivenessAnalysis liveness(graph, codegen.get(), GetScopedAllocator());
   liveness.Analyze();
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 8b9e1da..d88b036 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -2079,6 +2079,19 @@
     return false;
   }
 
+  // If this instruction will do an implicit null check, return the `HNullCheck` associated
+  // with it. Otherwise return null.
+  HNullCheck* GetImplicitNullCheck() const {
+    // Find the first previous instruction which is not a move.
+    HInstruction* first_prev_not_move = GetPreviousDisregardingMoves();
+    if (first_prev_not_move != nullptr &&
+        first_prev_not_move->IsNullCheck() &&
+        first_prev_not_move->IsEmittedAtUseSite()) {
+      return first_prev_not_move->AsNullCheck();
+    }
+    return nullptr;
+  }
+
   virtual bool IsActualObject() const {
     return GetType() == DataType::Type::kReference;
   }
@@ -4713,7 +4726,7 @@
 
   bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE {
     // TODO: Add implicit null checks in intrinsics.
-    return (obj == InputAt(0)) && !GetLocations()->Intrinsified();
+    return (obj == InputAt(0)) && !IsIntrinsic();
   }
 
   uint32_t GetVTableIndex() const { return vtable_index_; }
@@ -4753,7 +4766,7 @@
 
   bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE {
     // TODO: Add implicit null checks in intrinsics.
-    return (obj == InputAt(0)) && !GetLocations()->Intrinsified();
+    return (obj == InputAt(0)) && !IsIntrinsic();
   }
 
   bool NeedsDexCacheOfDeclaringClass() const OVERRIDE {
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index c40cbcf..f52b96d 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -570,7 +570,7 @@
   {
     PassScope scope(PrepareForRegisterAllocation::kPrepareForRegisterAllocationPassName,
                     pass_observer);
-    PrepareForRegisterAllocation(graph, stats).Run();
+    PrepareForRegisterAllocation(graph, codegen->GetCompilerOptions(), stats).Run();
   }
   // Use local allocator shared by SSA liveness analysis and register allocator.
   // (Register allocator creates new objects in the liveness data.)
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 060613d..fc81740 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -17,6 +17,7 @@
 #include "prepare_for_register_allocation.h"
 
 #include "dex/dex_file_types.h"
+#include "driver/compiler_options.h"
 #include "jni/jni_internal.h"
 #include "optimizing_compiler_stats.h"
 #include "well_known_classes.h"
@@ -27,7 +28,7 @@
   // Order does not matter.
   for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) {
     // No need to visit the phis.
-    for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
+    for (HInstructionIteratorHandleChanges inst_it(block->GetInstructions()); !inst_it.Done();
          inst_it.Advance()) {
       inst_it.Current()->Accept(this);
     }
@@ -50,6 +51,19 @@
 
 void PrepareForRegisterAllocation::VisitNullCheck(HNullCheck* check) {
   check->ReplaceWith(check->InputAt(0));
+  if (compiler_options_.GetImplicitNullChecks()) {
+    HInstruction* next = check->GetNext();
+
+    // The `PrepareForRegisterAllocation` pass removes `HBoundType` from the graph,
+    // so do it ourselves now to not prevent optimizations.
+    while (next->IsBoundType()) {
+      next = next->GetNext();
+      VisitBoundType(next->GetPrevious()->AsBoundType());
+    }
+    if (next->CanDoImplicitNullCheckOn(check->InputAt(0))) {
+      check->MarkEmittedAtUseSite();
+    }
+  }
 }
 
 void PrepareForRegisterAllocation::VisitDivZeroCheck(HDivZeroCheck* check) {
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index f6e4d3e..2978add 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -21,6 +21,7 @@
 
 namespace art {
 
+class CompilerOptions;
 class OptimizingCompilerStats;
 
 /**
@@ -30,9 +31,11 @@
  */
 class PrepareForRegisterAllocation : public HGraphDelegateVisitor {
  public:
-  explicit PrepareForRegisterAllocation(HGraph* graph,
-                                        OptimizingCompilerStats* stats = nullptr)
-      : HGraphDelegateVisitor(graph, stats) {}
+  PrepareForRegisterAllocation(HGraph* graph,
+                               const CompilerOptions& compiler_options,
+                               OptimizingCompilerStats* stats = nullptr)
+      : HGraphDelegateVisitor(graph, stats),
+        compiler_options_(compiler_options) {}
 
   void Run();
 
@@ -56,6 +59,8 @@
   bool CanMoveClinitCheck(HInstruction* input, HInstruction* user) const;
   bool CanEmitConditionAt(HCondition* condition, HInstruction* user) const;
 
+  const CompilerOptions& compiler_options_;
+
   DISALLOW_COPY_AND_ASSIGN(PrepareForRegisterAllocation);
 };
 
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 2f782f3..62a70d6 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -103,9 +103,9 @@
   ComputeLiveInAndLiveOutSets();
 }
 
-static void RecursivelyProcessInputs(HInstruction* current,
-                                     HInstruction* actual_user,
-                                     BitVector* live_in) {
+void SsaLivenessAnalysis::RecursivelyProcessInputs(HInstruction* current,
+                                                   HInstruction* actual_user,
+                                                   BitVector* live_in) {
   HInputsRef inputs = current->GetInputs();
   for (size_t i = 0; i < inputs.size(); ++i) {
     HInstruction* input = inputs[i];
@@ -131,11 +131,40 @@
       // Check that the inlined input is not a phi. Recursing on loop phis could
       // lead to an infinite loop.
       DCHECK(!input->IsPhi());
+      DCHECK(!input->HasEnvironment());
       RecursivelyProcessInputs(input, actual_user, live_in);
     }
   }
 }
 
+void SsaLivenessAnalysis::ProcessEnvironment(HInstruction* current,
+                                             HInstruction* actual_user,
+                                             BitVector* live_in) {
+  for (HEnvironment* environment = current->GetEnvironment();
+       environment != nullptr;
+       environment = environment->GetParent()) {
+    // Handle environment uses. See statements (b) and (c) of the
+    // SsaLivenessAnalysis.
+    for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+      HInstruction* instruction = environment->GetInstructionAt(i);
+      if (instruction == nullptr) {
+        continue;
+      }
+      bool should_be_live = ShouldBeLiveForEnvironment(current, instruction);
+      // If this environment use does not keep the instruction live, it does not
+      // affect the live range of that instruction.
+      if (should_be_live) {
+        CHECK(instruction->HasSsaIndex()) << instruction->DebugName();
+        live_in->SetBit(instruction->GetSsaIndex());
+        instruction->GetLiveInterval()->AddUse(current,
+                                               environment,
+                                               i,
+                                               actual_user);
+      }
+    }
+  }
+}
+
 void SsaLivenessAnalysis::ComputeLiveRanges() {
   // Do a post order visit, adding inputs of instructions live in the block where
   // that instruction is defined, and killing instructions that are being visited.
@@ -186,32 +215,6 @@
         current->GetLiveInterval()->SetFrom(current->GetLifetimePosition());
       }
 
-      // Process the environment first, because we know their uses come after
-      // or at the same liveness position of inputs.
-      for (HEnvironment* environment = current->GetEnvironment();
-           environment != nullptr;
-           environment = environment->GetParent()) {
-        // Handle environment uses. See statements (b) and (c) of the
-        // SsaLivenessAnalysis.
-        for (size_t i = 0, e = environment->Size(); i < e; ++i) {
-          HInstruction* instruction = environment->GetInstructionAt(i);
-          if (instruction == nullptr) {
-            continue;
-          }
-          bool should_be_live = ShouldBeLiveForEnvironment(current, instruction);
-          // If this environment use does not keep the instruction live, it does not
-          // affect the live range of that instruction.
-          if (should_be_live) {
-            CHECK(instruction->HasSsaIndex()) << instruction->DebugName();
-            live_in->SetBit(instruction->GetSsaIndex());
-            instruction->GetLiveInterval()->AddUse(current,
-                                                   environment,
-                                                   i,
-                                                   /* actual_user */ nullptr);
-          }
-        }
-      }
-
       // Process inputs of instructions.
       if (current->IsEmittedAtUseSite()) {
         if (kIsDebugBuild) {
@@ -224,6 +227,16 @@
           DCHECK(!current->HasEnvironmentUses());
         }
       } else {
+        // Process the environment first, because we know their uses come after
+        // or at the same liveness position of inputs.
+        ProcessEnvironment(current, current, live_in);
+
+        // Special case implicit null checks. We want their environment uses to be
+        // emitted at the instruction doing the actual null check.
+        HNullCheck* check = current->GetImplicitNullCheck();
+        if (check != nullptr) {
+          ProcessEnvironment(check, current, live_in);
+        }
         RecursivelyProcessInputs(current, current, live_in);
       }
     }
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 83ca5bd..cebd4ad 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -1252,6 +1252,13 @@
   // Update the live_out set of the block and returns whether it has changed.
   bool UpdateLiveOut(const HBasicBlock& block);
 
+  static void ProcessEnvironment(HInstruction* instruction,
+                                 HInstruction* actual_user,
+                                 BitVector* live_in);
+  static void RecursivelyProcessInputs(HInstruction* instruction,
+                                       HInstruction* actual_user,
+                                       BitVector* live_in);
+
   // Returns whether `instruction` in an HEnvironment held by `env_holder`
   // should be kept live by the HEnvironment.
   static bool ShouldBeLiveForEnvironment(HInstruction* env_holder, HInstruction* instruction) {
diff --git a/test/565-checker-condition-liveness/src/Main.java b/test/565-checker-condition-liveness/src/Main.java
index 6b6619f..374e136 100644
--- a/test/565-checker-condition-liveness/src/Main.java
+++ b/test/565-checker-condition-liveness/src/Main.java
@@ -32,20 +32,20 @@
   }
 
   /// CHECK-START: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
-  /// CHECK-DAG:  <<IntArg:i\d+>>   ParameterValue        env_uses:[21,25]
-  /// CHECK-DAG:  <<RefArg:l\d+>>   ParameterValue        env_uses:[11,21,25]
-  /// CHECK-DAG:  <<Array:l\d+>>    ParameterValue        env_uses:[11,21,25]
-  /// CHECK-DAG:  <<Const1:i\d+>>   IntConstant 1         env_uses:[21,25]
+  /// CHECK-DAG:  <<IntArg:i\d+>>   ParameterValue        env_uses:[25]
+  /// CHECK-DAG:  <<RefArg:l\d+>>   ParameterValue        env_uses:[11,25]
+  /// CHECK-DAG:  <<Array:l\d+>>    ParameterValue        env_uses:[11,25]
+  /// CHECK-DAG:  <<Const1:i\d+>>   IntConstant 1         env_uses:[25]
   /// CHECK-DAG:                    SuspendCheck          env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]]           liveness:10
   /// CHECK-DAG:                    NullCheck             env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]]  liveness:20
   /// CHECK-DAG:                    BoundsCheck           env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]]  liveness:24
   /// CHECK-DAG:                    TryBoundary
 
   /// CHECK-START-DEBUGGABLE: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
-  /// CHECK-DAG:  <<IntArg:i\d+>>   ParameterValue        env_uses:[11,21,25]
-  /// CHECK-DAG:  <<RefArg:l\d+>>   ParameterValue        env_uses:[11,21,25]
-  /// CHECK-DAG:  <<Array:l\d+>>    ParameterValue        env_uses:[11,21,25]
-  /// CHECK-DAG:  <<Const1:i\d+>>   IntConstant 1         env_uses:[21,25]
+  /// CHECK-DAG:  <<IntArg:i\d+>>   ParameterValue        env_uses:[11,25]
+  /// CHECK-DAG:  <<RefArg:l\d+>>   ParameterValue        env_uses:[11,25]
+  /// CHECK-DAG:  <<Array:l\d+>>    ParameterValue        env_uses:[11,25]
+  /// CHECK-DAG:  <<Const1:i\d+>>   IntConstant 1         env_uses:[25]
   /// CHECK-DAG:                    SuspendCheck          env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]]           liveness:10
   /// CHECK-DAG:                    NullCheck             env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]]  liveness:20
   /// CHECK-DAG:                    BoundsCheck           env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]]  liveness:24
@@ -62,18 +62,18 @@
 
   /// CHECK-START: void Main.testBoundsCheck(int, java.lang.Object, int[]) liveness (after)
   /// CHECK-DAG:  <<IntArg:i\d+>>   ParameterValue        env_uses:[]
-  /// CHECK-DAG:  <<RefArg:l\d+>>   ParameterValue        env_uses:[11,17,21]
-  /// CHECK-DAG:  <<Array:l\d+>>    ParameterValue        env_uses:[11,17,21]
+  /// CHECK-DAG:  <<RefArg:l\d+>>   ParameterValue        env_uses:[11,21]
+  /// CHECK-DAG:  <<Array:l\d+>>    ParameterValue        env_uses:[11,21]
   /// CHECK-DAG:  <<Const1:i\d+>>   IntConstant 1         env_uses:[]
   /// CHECK-DAG:                    SuspendCheck          env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]]           liveness:10
   /// CHECK-DAG:                    NullCheck             env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]]  liveness:16
   /// CHECK-DAG:                    BoundsCheck           env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]]  liveness:20
 
   /// CHECK-START-DEBUGGABLE: void Main.testBoundsCheck(int, java.lang.Object, int[]) liveness (after)
-  /// CHECK-DAG:  <<IntArg:i\d+>>   ParameterValue        env_uses:[11,17,21]
-  /// CHECK-DAG:  <<RefArg:l\d+>>   ParameterValue        env_uses:[11,17,21]
-  /// CHECK-DAG:  <<Array:l\d+>>    ParameterValue        env_uses:[11,17,21]
-  /// CHECK-DAG:  <<Const1:i\d+>>   IntConstant 1         env_uses:[17,21]
+  /// CHECK-DAG:  <<IntArg:i\d+>>   ParameterValue        env_uses:[11,21]
+  /// CHECK-DAG:  <<RefArg:l\d+>>   ParameterValue        env_uses:[11,21]
+  /// CHECK-DAG:  <<Array:l\d+>>    ParameterValue        env_uses:[11,21]
+  /// CHECK-DAG:  <<Const1:i\d+>>   IntConstant 1         env_uses:[21]
   /// CHECK-DAG:                    SuspendCheck          env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]]           liveness:10
   /// CHECK-DAG:                    NullCheck             env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]]  liveness:16
   /// CHECK-DAG:                    BoundsCheck           env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]]  liveness:20
@@ -83,20 +83,22 @@
 
   /// CHECK-START: void Main.testDeoptimize(int, java.lang.Object, int[]) liveness (after)
   /// CHECK-DAG:  <<IntArg:i\d+>>   ParameterValue        env_uses:[25]
-  /// CHECK-DAG:  <<RefArg:l\d+>>   ParameterValue        env_uses:[13,19,25]
-  /// CHECK-DAG:  <<Array:l\d+>>    ParameterValue        env_uses:[13,19,25]
+  /// CHECK-DAG:  <<RefArg:l\d+>>   ParameterValue        env_uses:[13,21,25]
+  /// CHECK-DAG:  <<Array:l\d+>>    ParameterValue        env_uses:[13,21,25]
   /// CHECK-DAG:  <<Const0:i\d+>>   IntConstant 0         env_uses:[25]
   /// CHECK-DAG:                    SuspendCheck          env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]]           liveness:12
   /// CHECK-DAG:                    NullCheck             env:[[<<Const0>>,<<IntArg>>,<<RefArg>>,<<Array>>]]  liveness:18
+  /// CHECK-DAG:                    ArrayLength                                                               liveness:20
   /// CHECK-DAG:                    Deoptimize            env:[[<<Const0>>,<<IntArg>>,<<RefArg>>,<<Array>>]]  liveness:24
 
   /// CHECK-START-DEBUGGABLE: void Main.testDeoptimize(int, java.lang.Object, int[]) liveness (after)
-  /// CHECK-DAG:  <<IntArg:i\d+>>   ParameterValue        env_uses:[13,19,25]
-  /// CHECK-DAG:  <<RefArg:l\d+>>   ParameterValue        env_uses:[13,19,25]
-  /// CHECK-DAG:  <<Array:l\d+>>    ParameterValue        env_uses:[13,19,25]
+  /// CHECK-DAG:  <<IntArg:i\d+>>   ParameterValue        env_uses:[13,21,25]
+  /// CHECK-DAG:  <<RefArg:l\d+>>   ParameterValue        env_uses:[13,21,25]
+  /// CHECK-DAG:  <<Array:l\d+>>    ParameterValue        env_uses:[13,21,25]
   /// CHECK-DAG:  <<Const0:i\d+>>   IntConstant 0         env_uses:[19,25]
   /// CHECK-DAG:                    SuspendCheck          env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]]           liveness:12
   /// CHECK-DAG:                    NullCheck             env:[[<<Const0>>,<<IntArg>>,<<RefArg>>,<<Array>>]]  liveness:18
+  /// CHECK-DAG:                    ArrayLength                                                               liveness:20
   /// CHECK-DAG:                    Deoptimize            env:[[<<Const0>>,<<IntArg>>,<<RefArg>>,<<Array>>]]  liveness:24
   //
   // A value that's not live in compiled code may still be needed in interpreter,
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 9ade2b1..a280f85 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1039,12 +1039,6 @@
         "description": ["Test timing out under gcstress possibly due to slower unwinding by libbacktrace"]
     },
     {
-        "tests": ["624-checker-stringops"],
-        "variant": "optimizing & gcstress | speed-profile & gcstress",
-        "bug": "b/111545159",
-        "description": ["Seem to expose some error with our gc when run in these configurations"]
-    },
-    {
         "tests": ["021-string2"],
         "variant": "jit & debuggable",
         "bug": "b/109791792",
diff --git a/tools/libcore_gcstress_failures.txt b/tools/libcore_gcstress_failures.txt
index 6840f9e..965e85c 100644
--- a/tools/libcore_gcstress_failures.txt
+++ b/tools/libcore_gcstress_failures.txt
@@ -33,15 +33,5 @@
           "org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet_hasNext",
           "libcore.java.text.DecimalFormatTest#testCurrencySymbolSpacing",
           "libcore.java.text.SimpleDateFormatTest#testLocales"]
-},
-{
-  description: "GC crash",
-  result: EXEC_FAILED,
-  bug: 111545159,
-  names: ["org.apache.harmony.tests.java.util.AbstractSequentialListTest#test_addAllILjava_util_Collection",
-          "org.apache.harmony.tests.java.util.HashtableTest#test_putLjava_lang_ObjectLjava_lang_Object",
-          "org.apache.harmony.tests.java.util.VectorTest#test_addAllILjava_util_Collection",
-          "org.apache.harmony.tests.java.util.VectorTest#test_addAllLjava_util_Collection",
-          "org.apache.harmony.tests.java.io.BufferedWriterTest#test_write_LStringII_Exception"]
 }
 ]