Fix longstanding bug around implicit NPEs and GC, version 2.

The TODO has been there since M (so forever :)):
https://android-review.googlesource.com/c/platform/art/+/122794/13//COMMIT_MSG#13

We hardly see the issue in our tests as we need to have:
1) A GC happening while creating the NPE object.
2) ParallelMoves between the NullCheck and implicit null check operation
   that moves references.

The CL piggy backs on the "IsEmittedAtUseSite" flag, to set implicit
null checks with it. The liveness analysis then special cases implicit
null checks to record environment uses at the location of the actual
instruction that will do the implicit null check.

Test: test.py --gcstress
Test: run-libcore-tests --gcstress
bug: 111545159
Change-Id: I3ecea4fe0d7e483e93db83281ca10db47da228c5
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 260920c..4ebe12e 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -5558,7 +5558,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 7322a35..9378bff 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1038,12 +1038,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"]
 }
 ]