Empty merge of sc-v2-dev-plus-aosp-without-vendor@8433047

Bug: 226662282
Merged-In: Ica2872d5f5dd6ad85ccecfce263d6335a575362e
Change-Id: Ia6f6883c6d5e56e4109c954b9559a16ecf1841cf
diff --git a/TEST_MAPPING b/TEST_MAPPING
index ad8df10..827a29f 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1187,6 +1187,9 @@
       "name": "art-run-test-730-checker-inlining-super[com.google.android.art.apex]"
     },
     {
+      "name": "art-run-test-731-bounds-check-slow-path[com.google.android.art.apex]"
+    },
+    {
       "name": "art-run-test-805-TooDeepClassInstanceOf[com.google.android.art.apex]"
     },
     {
@@ -2479,6 +2482,9 @@
       "name": "art-run-test-730-checker-inlining-super"
     },
     {
+      "name": "art-run-test-731-bounds-check-slow-path"
+    },
+    {
       "name": "art-run-test-805-TooDeepClassInstanceOf"
     },
     {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 4025684..8c6b802 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -150,41 +150,62 @@
     LocationSummary* locations = instruction_->GetLocations();
     CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
     __ Bind(GetEntryLabel());
-    // We're moving two locations to locations that could overlap, so we need a parallel
-    // move resolver.
     if (instruction_->CanThrowIntoCatchBlock()) {
       // Live registers will be restored in the catch block if caught.
-      SaveLiveRegisters(codegen, instruction_->GetLocations());
+      SaveLiveRegisters(codegen, locations);
     }
 
-    // Are we using an array length from memory?
-    HInstruction* array_length = instruction_->InputAt(1);
+    Location index_loc = locations->InAt(0);
     Location length_loc = locations->InAt(1);
     InvokeRuntimeCallingConvention calling_convention;
-    if (array_length->IsArrayLength() && array_length->IsEmittedAtUseSite()) {
-      // Load the array length into our temporary.
-      HArrayLength* length = array_length->AsArrayLength();
-      uint32_t len_offset = CodeGenerator::GetArrayLengthOffset(length);
+    Location index_arg = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
+    Location length_arg = Location::RegisterLocation(calling_convention.GetRegisterAt(1));
+
+    // Are we using an array length from memory?
+    if (!length_loc.IsValid()) {
+      DCHECK(instruction_->InputAt(1)->IsArrayLength());
+      HArrayLength* array_length = instruction_->InputAt(1)->AsArrayLength();
+      DCHECK(array_length->IsEmittedAtUseSite());
+      uint32_t len_offset = CodeGenerator::GetArrayLengthOffset(array_length);
       Location array_loc = array_length->GetLocations()->InAt(0);
-      Address array_len(array_loc.AsRegister<Register>(), len_offset);
-      length_loc = Location::RegisterLocation(calling_convention.GetRegisterAt(1));
-      // Check for conflicts with index.
-      if (length_loc.Equals(locations->InAt(0))) {
-        // We know we aren't using parameter 2.
-        length_loc = Location::RegisterLocation(calling_convention.GetRegisterAt(2));
+      if (!index_loc.Equals(length_arg)) {
+        // The index is not clobbered by loading the length directly to `length_arg`.
+        __ movl(length_arg.AsRegister<Register>(),
+                Address(array_loc.AsRegister<Register>(), len_offset));
+        x86_codegen->Move32(index_arg, index_loc);
+      } else if (!array_loc.Equals(index_arg)) {
+        // The array reference is not clobbered by the index move.
+        x86_codegen->Move32(index_arg, index_loc);
+        __ movl(length_arg.AsRegister<Register>(),
+                Address(array_loc.AsRegister<Register>(), len_offset));
+      } else {
+        // We do not have a temporary we could use, so swap the registers using the
+        // parallel move resolver and replace the array with the length afterwards.
+        codegen->EmitParallelMoves(
+            index_loc,
+            index_arg,
+            DataType::Type::kInt32,
+            array_loc,
+            length_arg,
+            DataType::Type::kReference);
+        __ movl(length_arg.AsRegister<Register>(),
+                Address(length_arg.AsRegister<Register>(), len_offset));
       }
-      __ movl(length_loc.AsRegister<Register>(), array_len);
-      if (mirror::kUseStringCompression && length->IsStringLength()) {
-        __ shrl(length_loc.AsRegister<Register>(), Immediate(1));
+      if (mirror::kUseStringCompression && array_length->IsStringLength()) {
+        __ shrl(length_arg.AsRegister<Register>(), Immediate(1));
       }
+    } else {
+      // We're moving two locations to locations that could overlap,
+      // so we need a parallel move resolver.
+      codegen->EmitParallelMoves(
+          index_loc,
+          index_arg,
+          DataType::Type::kInt32,
+          length_loc,
+          length_arg,
+          DataType::Type::kInt32);
     }
-    x86_codegen->EmitParallelMoves(
-        locations->InAt(0),
-        Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
-        DataType::Type::kInt32,
-        length_loc,
-        Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
-        DataType::Type::kInt32);
+
     QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
         ? kQuickThrowStringBounds
         : kQuickThrowArrayBounds;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 8c1a533..d919fa7 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -203,39 +203,54 @@
     __ Bind(GetEntryLabel());
     if (instruction_->CanThrowIntoCatchBlock()) {
       // Live registers will be restored in the catch block if caught.
-      SaveLiveRegisters(codegen, instruction_->GetLocations());
-    }
-    // Are we using an array length from memory?
-    HInstruction* array_length = instruction_->InputAt(1);
-    Location length_loc = locations->InAt(1);
-    InvokeRuntimeCallingConvention calling_convention;
-    if (array_length->IsArrayLength() && array_length->IsEmittedAtUseSite()) {
-      // Load the array length into our temporary.
-      HArrayLength* length = array_length->AsArrayLength();
-      uint32_t len_offset = CodeGenerator::GetArrayLengthOffset(length);
-      Location array_loc = array_length->GetLocations()->InAt(0);
-      Address array_len(array_loc.AsRegister<CpuRegister>(), len_offset);
-      length_loc = Location::RegisterLocation(calling_convention.GetRegisterAt(1));
-      // Check for conflicts with index.
-      if (length_loc.Equals(locations->InAt(0))) {
-        // We know we aren't using parameter 2.
-        length_loc = Location::RegisterLocation(calling_convention.GetRegisterAt(2));
-      }
-      __ movl(length_loc.AsRegister<CpuRegister>(), array_len);
-      if (mirror::kUseStringCompression && length->IsStringLength()) {
-        __ shrl(length_loc.AsRegister<CpuRegister>(), Immediate(1));
-      }
+      SaveLiveRegisters(codegen, locations);
     }
 
-    // We're moving two locations to locations that could overlap, so we need a parallel
-    // move resolver.
-    codegen->EmitParallelMoves(
-        locations->InAt(0),
-        Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
-        DataType::Type::kInt32,
-        length_loc,
-        Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
-        DataType::Type::kInt32);
+    Location index_loc = locations->InAt(0);
+    Location length_loc = locations->InAt(1);
+    InvokeRuntimeCallingConvention calling_convention;
+    Location index_arg = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
+    Location length_arg = Location::RegisterLocation(calling_convention.GetRegisterAt(1));
+
+    // Are we using an array length from memory?
+    if (!length_loc.IsValid()) {
+      DCHECK(instruction_->InputAt(1)->IsArrayLength());
+      HArrayLength* array_length = instruction_->InputAt(1)->AsArrayLength();
+      DCHECK(array_length->IsEmittedAtUseSite());
+      uint32_t len_offset = CodeGenerator::GetArrayLengthOffset(array_length);
+      Location array_loc = array_length->GetLocations()->InAt(0);
+      Address array_len(array_loc.AsRegister<CpuRegister>(), len_offset);
+      if (!index_loc.Equals(length_arg)) {
+        // The index is not clobbered by loading the length directly to `length_arg`.
+        __ movl(length_arg.AsRegister<CpuRegister>(), array_len);
+        x86_64_codegen->Move(index_arg, index_loc);
+      } else if (!array_loc.Equals(index_arg)) {
+        // The array reference is not clobbered by the index move.
+        x86_64_codegen->Move(index_arg, index_loc);
+        __ movl(length_arg.AsRegister<CpuRegister>(), array_len);
+      } else {
+        // Load the array length into `TMP`.
+        DCHECK(codegen->IsBlockedCoreRegister(TMP));
+        __ movl(CpuRegister(TMP), array_len);
+        // Single move to CPU register does not clobber `TMP`.
+        x86_64_codegen->Move(index_arg, index_loc);
+        __ movl(length_arg.AsRegister<CpuRegister>(), CpuRegister(TMP));
+      }
+      if (mirror::kUseStringCompression && array_length->IsStringLength()) {
+        __ shrl(length_arg.AsRegister<CpuRegister>(), Immediate(1));
+      }
+    } else {
+      // We're moving two locations to locations that could overlap,
+      // so we need a parallel move resolver.
+      codegen->EmitParallelMoves(
+          index_loc,
+          index_arg,
+          DataType::Type::kInt32,
+          length_loc,
+          length_arg,
+          DataType::Type::kInt32);
+    }
+
     QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
         ? kQuickThrowStringBounds
         : kQuickThrowArrayBounds;
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 77dfe68..2d7c208 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -491,9 +491,9 @@
 }
 
 bool HLoopOptimization::Run() {
-  // Skip if there is no loop or the graph has try-catch/irreducible loops.
+  // Skip if there is no loop or the graph has irreducible loops.
   // TODO: make this less of a sledgehammer.
-  if (!graph_->HasLoops() || graph_->HasTryCatch() || graph_->HasIrreducibleLoops()) {
+  if (!graph_->HasLoops() || graph_->HasIrreducibleLoops()) {
     return false;
   }
 
@@ -502,7 +502,7 @@
   loop_allocator_ = &allocator;
 
   // Perform loop optimizations.
-  bool didLoopOpt = LocalRun();
+  const bool did_loop_opt = LocalRun();
   if (top_loop_ == nullptr) {
     graph_->SetHasLoops(false);  // no more loops
   }
@@ -511,7 +511,7 @@
   loop_allocator_ = nullptr;
   last_loop_ = top_loop_ = nullptr;
 
-  return didLoopOpt;
+  return did_loop_opt;
 }
 
 //
@@ -519,7 +519,6 @@
 //
 
 bool HLoopOptimization::LocalRun() {
-  bool didLoopOpt = false;
   // Build the linear order using the phase-local allocator. This step enables building
   // a loop hierarchy that properly reflects the outer-inner and previous-next relation.
   ScopedArenaVector<HBasicBlock*> linear_order(loop_allocator_->Adapter(kArenaAllocLinearOrder));
@@ -532,34 +531,37 @@
     }
   }
 
+  // TODO(solanes): How can `top_loop_` be null if `graph_->HasLoops()` is true?
+  if (top_loop_ == nullptr) {
+    return false;
+  }
+
   // Traverse the loop hierarchy inner-to-outer and optimize. Traversal can use
   // temporary data structures using the phase-local allocator. All new HIR
   // should use the global allocator.
-  if (top_loop_ != nullptr) {
-    ScopedArenaSet<HInstruction*> iset(loop_allocator_->Adapter(kArenaAllocLoopOptimization));
-    ScopedArenaSafeMap<HInstruction*, HInstruction*> reds(
-        std::less<HInstruction*>(), loop_allocator_->Adapter(kArenaAllocLoopOptimization));
-    ScopedArenaSet<ArrayReference> refs(loop_allocator_->Adapter(kArenaAllocLoopOptimization));
-    ScopedArenaSafeMap<HInstruction*, HInstruction*> map(
-        std::less<HInstruction*>(), loop_allocator_->Adapter(kArenaAllocLoopOptimization));
-    ScopedArenaSafeMap<HInstruction*, HInstruction*> perm(
-        std::less<HInstruction*>(), loop_allocator_->Adapter(kArenaAllocLoopOptimization));
-    // Attach.
-    iset_ = &iset;
-    reductions_ = &reds;
-    vector_refs_ = &refs;
-    vector_map_ = &map;
-    vector_permanent_map_ = &perm;
-    // Traverse.
-    didLoopOpt = TraverseLoopsInnerToOuter(top_loop_);
-    // Detach.
-    iset_ = nullptr;
-    reductions_ = nullptr;
-    vector_refs_ = nullptr;
-    vector_map_ = nullptr;
-    vector_permanent_map_ = nullptr;
-  }
-  return didLoopOpt;
+  ScopedArenaSet<HInstruction*> iset(loop_allocator_->Adapter(kArenaAllocLoopOptimization));
+  ScopedArenaSafeMap<HInstruction*, HInstruction*> reds(
+      std::less<HInstruction*>(), loop_allocator_->Adapter(kArenaAllocLoopOptimization));
+  ScopedArenaSet<ArrayReference> refs(loop_allocator_->Adapter(kArenaAllocLoopOptimization));
+  ScopedArenaSafeMap<HInstruction*, HInstruction*> map(
+      std::less<HInstruction*>(), loop_allocator_->Adapter(kArenaAllocLoopOptimization));
+  ScopedArenaSafeMap<HInstruction*, HInstruction*> perm(
+      std::less<HInstruction*>(), loop_allocator_->Adapter(kArenaAllocLoopOptimization));
+  // Attach.
+  iset_ = &iset;
+  reductions_ = &reds;
+  vector_refs_ = &refs;
+  vector_map_ = &map;
+  vector_permanent_map_ = &perm;
+  // Traverse.
+  const bool did_loop_opt = TraverseLoopsInnerToOuter(top_loop_);
+  // Detach.
+  iset_ = nullptr;
+  reductions_ = nullptr;
+  vector_refs_ = nullptr;
+  vector_map_ = nullptr;
+  vector_permanent_map_ = nullptr;
+  return did_loop_opt;
 }
 
 void HLoopOptimization::AddLoop(HLoopInformation* loop_info) {
@@ -618,6 +620,18 @@
       induction_range_.ReVisit(node->loop_info);
       changed = true;
     }
+
+    CalculateAndSetTryCatchKind(node);
+    if (node->try_catch_kind == LoopNode::TryCatchKind::kHasTryCatch) {
+      // The current optimizations assume that the loops do not contain try/catches.
+      // TODO(solanes, 227283906): Assess if we can modify them to work with try/catches.
+      continue;
+    }
+
+    DCHECK(node->try_catch_kind == LoopNode::TryCatchKind::kNoTryCatch)
+        << "kind: " << static_cast<int>(node->try_catch_kind)
+        << ". LoopOptimization requires the loops to not have try catches.";
+
     // Repeat simplifications in the loop-body until no more changes occur.
     // Note that since each simplification consists of eliminating code (without
     // introducing new code), this process is always finite.
@@ -635,6 +649,37 @@
   return changed;
 }
 
+void HLoopOptimization::CalculateAndSetTryCatchKind(LoopNode* node) {
+  DCHECK(node != nullptr);
+  DCHECK(node->try_catch_kind == LoopNode::TryCatchKind::kUnknown)
+      << "kind: " << static_cast<int>(node->try_catch_kind)
+      << ". SetTryCatchKind should be called only once per LoopNode.";
+
+  // If a inner loop has a try catch, then the outer loop has one too (as it contains `inner`).
+  // Knowing this, we could skip iterating through all of the outer loop's parents with a simple
+  // check.
+  for (LoopNode* inner = node->inner; inner != nullptr; inner = inner->next) {
+    DCHECK(inner->try_catch_kind != LoopNode::TryCatchKind::kUnknown)
+        << "kind: " << static_cast<int>(inner->try_catch_kind)
+        << ". Should have updated the inner loop before the outer loop.";
+
+    if (inner->try_catch_kind == LoopNode::TryCatchKind::kHasTryCatch) {
+      node->try_catch_kind = LoopNode::TryCatchKind::kHasTryCatch;
+      return;
+    }
+  }
+
+  for (HBlocksInLoopIterator it_loop(*node->loop_info); !it_loop.Done(); it_loop.Advance()) {
+    HBasicBlock* block = it_loop.Current();
+    if (block->GetTryCatchInformation() != nullptr) {
+      node->try_catch_kind = LoopNode::TryCatchKind::kHasTryCatch;
+      return;
+    }
+  }
+
+  node->try_catch_kind = LoopNode::TryCatchKind::kNoTryCatch;
+}
+
 //
 // Optimization.
 //
@@ -782,8 +827,6 @@
   return TryOptimizeInnerLoopFinite(node) || TryPeelingAndUnrolling(node);
 }
 
-
-
 //
 // Scalar loop peeling and unrolling: generic part methods.
 //
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index 3acd5b1..b178616 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -57,12 +57,23 @@
           outer(nullptr),
           inner(nullptr),
           previous(nullptr),
-          next(nullptr) {}
+          next(nullptr),
+          try_catch_kind(TryCatchKind::kUnknown) {}
+
+    enum class TryCatchKind {
+      kUnknown,
+      // Either if we have a try catch in the loop, or if the loop is inside of an outer try catch,
+      // we set `kHasTryCatch`.
+      kHasTryCatch,
+      kNoTryCatch
+    };
+
     HLoopInformation* loop_info;
     LoopNode* outer;
     LoopNode* inner;
     LoopNode* previous;
     LoopNode* next;
+    TryCatchKind try_catch_kind;
   };
 
   /*
@@ -131,6 +142,11 @@
   // Returns true if loops nested inside current loop (node) have changed.
   bool TraverseLoopsInnerToOuter(LoopNode* node);
 
+  // Calculates `node`'s `try_catch_kind` and sets it to:
+  // 1) kHasTryCatch if it has try catches (or if it's inside of an outer try catch)
+  // 2) kNoTryCatch otherwise.
+  void CalculateAndSetTryCatchKind(LoopNode* node);
+
   //
   // Optimization.
   //
diff --git a/compiler/optimizing/superblock_cloner.cc b/compiler/optimizing/superblock_cloner.cc
index b46d193..a5f919c 100644
--- a/compiler/optimizing/superblock_cloner.cc
+++ b/compiler/optimizing/superblock_cloner.cc
@@ -855,11 +855,20 @@
 }
 
 bool SuperblockCloner::IsSubgraphClonable() const {
-  // TODO: Support irreducible graphs and graphs with try-catch.
-  if (graph_->HasIrreducibleLoops() || graph_->HasTryCatch()) {
+  // TODO: Support irreducible graphs and subgraphs with try-catch.
+  if (graph_->HasIrreducibleLoops()) {
     return false;
   }
 
+  for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+    if (!IsInOrigBBSet(block)) {
+      continue;
+    }
+    if (block->GetTryCatchInformation() != nullptr) {
+      return false;
+    }
+  }
+
   HInstructionMap live_outs(
       std::less<HInstruction*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
 
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 51daa30..c0dea85 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -1108,12 +1108,20 @@
   size_t Prune() REQUIRES_SHARED(Locks::mutator_lock_) {
     ClassTable* class_table =
         Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader_);
+    WriterMutexLock mu(Thread::Current(), class_table->lock_);
     for (mirror::Class* klass : classes_to_prune_) {
-      std::string storage;
-      const char* descriptor = klass->GetDescriptor(&storage);
-      bool result = class_table->Remove(descriptor);
-      DCHECK(result);
-      DCHECK(!class_table->Remove(descriptor)) << descriptor;
+      uint32_t hash = ClassTable::TableSlot::HashDescriptor(klass);
+      DCHECK(!class_table->classes_.empty());
+      ClassTable::ClassSet& last_class_set = class_table->classes_.back();
+      auto it = last_class_set.FindWithHash(ClassTable::TableSlot(klass, hash), hash);
+      DCHECK(it != last_class_set.end());
+      last_class_set.erase(it);
+      DCHECK(std::none_of(class_table->classes_.begin(),
+                          class_table->classes_.end(),
+                          [klass, hash](ClassTable::ClassSet& class_set) {
+                            ClassTable::TableSlot slot(klass, hash);
+                            return class_set.FindWithHash(slot, hash) != class_set.end();
+                          }));
     }
     return defined_class_count_;
   }
diff --git a/libartbase/base/array_ref.h b/libartbase/base/array_ref.h
index 064e26b..859c2a1 100644
--- a/libartbase/base/array_ref.h
+++ b/libartbase/base/array_ref.h
@@ -65,7 +65,7 @@
 
   template <typename U,
             size_t size,
-            typename = typename std::enable_if<std::is_same<T, const U>::value>::type>
+            typename = std::enable_if_t<std::is_same_v<T, const U>>>
   explicit constexpr ArrayRef(U (&array)[size])
       : array_(array), size_(size) {
   }
@@ -75,17 +75,14 @@
   }
 
   template <typename Vector,
-            typename = typename std::enable_if<
-                std::is_same<typename Vector::value_type, value_type>::value>::type>
+            typename = std::enable_if_t<std::is_same_v<typename Vector::value_type, value_type>>>
   explicit ArrayRef(Vector& v)
       : array_(v.data()), size_(v.size()) {
   }
 
   template <typename Vector,
-            typename = typename std::enable_if<
-                std::is_same<
-                    typename std::add_const<typename Vector::value_type>::type,
-                    value_type>::value>::type>
+            typename = std::enable_if_t<
+                std::is_same_v<std::add_const_t<typename Vector::value_type>, value_type>>>
   explicit ArrayRef(const Vector& v)
       : array_(v.data()), size_(v.size()) {
   }
@@ -101,7 +98,7 @@
   }
 
   template <typename U>
-  typename std::enable_if<std::is_same<T, const U>::value, ArrayRef>::type&
+  std::enable_if_t<std::is_same_v<T, const U>, ArrayRef>&
   operator=(const ArrayRef<U>& other) {
     return *this = ArrayRef(other);
   }
diff --git a/libartbase/base/bit_memory_region.h b/libartbase/base/bit_memory_region.h
index cca4217..c5224a5 100644
--- a/libartbase/base/bit_memory_region.h
+++ b/libartbase/base/bit_memory_region.h
@@ -101,8 +101,8 @@
   ATTRIBUTE_NO_SANITIZE_ADDRESS  // We might touch extra bytes due to the alignment.
   ATTRIBUTE_NO_SANITIZE_HWADDRESS  // The hwasan uses different attribute.
   ALWAYS_INLINE Result LoadBits(size_t bit_offset, size_t bit_length) const {
-    static_assert(std::is_integral<Result>::value, "Result must be integral");
-    static_assert(std::is_unsigned<Result>::value, "Result must be unsigned");
+    static_assert(std::is_integral_v<Result>, "Result must be integral");
+    static_assert(std::is_unsigned_v<Result>, "Result must be unsigned");
     DCHECK(IsAligned<sizeof(Result)>(data_));
     DCHECK_LE(bit_offset, bit_size_);
     DCHECK_LE(bit_length, bit_size_ - bit_offset);
diff --git a/libartbase/base/bit_string.h b/libartbase/base/bit_string.h
index d995f8d..cce6fe0 100644
--- a/libartbase/base/bit_string.h
+++ b/libartbase/base/bit_string.h
@@ -38,7 +38,7 @@
  */
 struct BitStringChar {
   using StorageType = uint32_t;
-  static_assert(std::is_unsigned<StorageType>::value, "BitStringChar::StorageType must be unsigned");
+  static_assert(std::is_unsigned_v<StorageType>, "BitStringChar::StorageType must be unsigned");
 
   // BitStringChars are always zero-initialized by default. Equivalent to BitStringChar(0,0).
   BitStringChar() : data_(0u), bitlength_(0u) { }
diff --git a/libartbase/base/bit_struct.h b/libartbase/base/bit_struct.h
index 4cc222c..599e12a 100644
--- a/libartbase/base/bit_struct.h
+++ b/libartbase/base/bit_struct.h
@@ -112,7 +112,7 @@
           size_t kBitWidth,
           typename StorageType>
 struct BitStructField {
-  static_assert(std::is_standard_layout<T>::value, "T must be standard layout");
+  static_assert(std::is_standard_layout_v<T>, "T must be standard layout");
 
   operator T() const {
     return Get();
@@ -120,7 +120,7 @@
 
   // Exclude overload when T==StorageType.
   template <typename _ = void,
-            typename = std::enable_if_t<std::is_same<T, StorageType>::value, _>>
+            typename = std::enable_if_t<std::is_same_v<T, StorageType>, _>>
   explicit operator StorageType() const {
     return BitFieldExtract(storage_, kBitOffset, kBitWidth);
   }
@@ -151,7 +151,7 @@
     // Since C++ doesn't allow the type of operator= to change out
     // in the subclass, reimplement operator= in each subclass
     // manually and call this helper function.
-    static_assert(std::is_base_of<BitStructField, T2>::value, "T2 must inherit BitStructField");
+    static_assert(std::is_base_of_v<BitStructField, T2>, "T2 must inherit BitStructField");
     what.Set(value);
     return what;
   }
@@ -172,10 +172,9 @@
  private:
   using ValueConverter = detail::ValueConverter<T>;
   using ConversionType = typename ValueConverter::StorageType;
-  using ExtractionType =
-      typename std::conditional<std::is_signed_v<ConversionType>,
-                                std::make_signed_t<StorageType>,
-                                StorageType>::type;
+  using ExtractionType = std::conditional_t<std::is_signed_v<ConversionType>,
+                                            std::make_signed_t<StorageType>,
+                                            StorageType>;
 
   StorageType storage_;
 };
diff --git a/libartbase/base/bit_struct_detail.h b/libartbase/base/bit_struct_detail.h
index ad7c0f4..0b891ba 100644
--- a/libartbase/base/bit_struct_detail.h
+++ b/libartbase/base/bit_struct_detail.h
@@ -35,13 +35,12 @@
 template <size_t kBitSize>
 struct MinimumTypeUnsignedHelper {
   using type =
-    typename std::conditional<kBitSize == 0, void,       // NOLINT [whitespace/operators] [3]
-    typename std::conditional<kBitSize <= 8, uint8_t,    // NOLINT [whitespace/operators] [3]
-    typename std::conditional<kBitSize <= 16, uint16_t,  // NOLINT [whitespace/operators] [3]
-    typename std::conditional<kBitSize <= 32, uint32_t,
-    typename std::conditional<kBitSize <= 64, uint64_t,
-    typename std::conditional<kBitSize <= BitSizeOf<uintmax_t>(), uintmax_t,
-                              void>::type>::type>::type>::type>::type>::type;
+    std::conditional_t<kBitSize == 0, void,       // NOLINT [whitespace/operators] [3]
+    std::conditional_t<kBitSize <= 8, uint8_t,    // NOLINT [whitespace/operators] [3]
+    std::conditional_t<kBitSize <= 16, uint16_t,  // NOLINT [whitespace/operators] [3]
+    std::conditional_t<kBitSize <= 32, uint32_t,
+    std::conditional_t<kBitSize <= 64, uint64_t,
+    std::conditional_t<kBitSize <= BitSizeOf<uintmax_t>(), uintmax_t, void>>>>>>;
 };
 
 // Select the smallest [u]intX_t that will fit kBitSize bits.
@@ -51,9 +50,9 @@
   using type_unsigned = typename MinimumTypeUnsignedHelper<kBitSize>::type;
 
   using type =
-    typename std::conditional</* if */   std::is_signed<T>::value,
-                              /* then */ typename std::make_signed<type_unsigned>::type,
-                              /* else */ type_unsigned>::type;
+    std::conditional_t</* if */   std::is_signed_v<T>,
+                       /* then */ std::make_signed_t<type_unsigned>,
+                       /* else */ type_unsigned>;
 };
 
 // Helper for converting to and from T to an integral type.
@@ -119,8 +118,8 @@
 template <typename T>
 struct HasUnderscoreField {
  private:
-  using TrueT = std::integral_constant<bool, true>::type;
-  using FalseT = std::integral_constant<bool, false>::type;
+  using TrueT = std::bool_constant<true>::type;
+  using FalseT = std::bool_constant<false>::type;
 
   template <typename C>
   static constexpr auto Test(void*) -> decltype(std::declval<C>()._, TrueT{});
@@ -140,13 +139,13 @@
 // Nominally used by the BITSTRUCT_DEFINE_END macro.
 template <typename T>
 static constexpr bool ValidateBitStructSize() {
-  static_assert(std::is_union<T>::value, "T must be union");
-  static_assert(std::is_standard_layout<T>::value, "T must be standard-layout");
+  static_assert(std::is_union_v<T>, "T must be union");
+  static_assert(std::is_standard_layout_v<T>, "T must be standard-layout");
   static_assert(HasUnderscoreField<T>::value, "T must have the _ DefineBitStructSize");
 
   const size_t kBitStructSizeOf = BitStructSizeOf<T>();
-  static_assert(std::is_same<decltype(GetMemberType(&T::_)),
-                             DefineBitStructSize<kBitStructSizeOf>>::value,
+  static_assert(std::is_same_v<decltype(GetMemberType(&T::_)),
+                               DefineBitStructSize<kBitStructSizeOf>>,
                 "T::_ must be a DefineBitStructSize of the same size");
 
   const size_t kExpectedSize = (BitStructSizeOf<T>() < kBitsPerByte)
diff --git a/libartbase/base/bit_utils.h b/libartbase/base/bit_utils.h
index 58cc78c..ffaffc3 100644
--- a/libartbase/base/bit_utils.h
+++ b/libartbase/base/bit_utils.h
@@ -30,8 +30,8 @@
 // Like sizeof, but count how many bits a type takes. Pass type explicitly.
 template <typename T>
 constexpr size_t BitSizeOf() {
-  static_assert(std::is_integral<T>::value, "T must be integral");
-  using unsigned_type = typename std::make_unsigned<T>::type;
+  static_assert(std::is_integral_v<T>, "T must be integral");
+  using unsigned_type = std::make_unsigned_t<T>;
   static_assert(sizeof(T) == sizeof(unsigned_type), "Unexpected type size mismatch!");
   static_assert(std::numeric_limits<unsigned_type>::radix == 2, "Unexpected radix!");
   return std::numeric_limits<unsigned_type>::digits;
@@ -45,8 +45,8 @@
 
 template<typename T>
 constexpr int CLZ(T x) {
-  static_assert(std::is_integral<T>::value, "T must be integral");
-  static_assert(std::is_unsigned<T>::value, "T must be unsigned");
+  static_assert(std::is_integral_v<T>, "T must be integral");
+  static_assert(std::is_unsigned_v<T>, "T must be unsigned");
   static_assert(std::numeric_limits<T>::radix == 2, "Unexpected radix!");
   static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t),
                 "Unsupported sizeof(T)");
@@ -60,14 +60,14 @@
 // Similar to CLZ except that on zero input it returns bitwidth and supports signed integers.
 template<typename T>
 constexpr int JAVASTYLE_CLZ(T x) {
-  static_assert(std::is_integral<T>::value, "T must be integral");
-  using unsigned_type = typename std::make_unsigned<T>::type;
+  static_assert(std::is_integral_v<T>, "T must be integral");
+  using unsigned_type = std::make_unsigned_t<T>;
   return (x == 0) ? BitSizeOf<T>() : CLZ(static_cast<unsigned_type>(x));
 }
 
 template<typename T>
 constexpr int CTZ(T x) {
-  static_assert(std::is_integral<T>::value, "T must be integral");
+  static_assert(std::is_integral_v<T>, "T must be integral");
   // It is not unreasonable to ask for trailing zeros in a negative number. As such, do not check
   // that T is an unsigned type.
   static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t),
@@ -79,8 +79,8 @@
 // Similar to CTZ except that on zero input it returns bitwidth and supports signed integers.
 template<typename T>
 constexpr int JAVASTYLE_CTZ(T x) {
-  static_assert(std::is_integral<T>::value, "T must be integral");
-  using unsigned_type = typename std::make_unsigned<T>::type;
+  static_assert(std::is_integral_v<T>, "T must be integral");
+  using unsigned_type = std::make_unsigned_t<T>;
   return (x == 0) ? BitSizeOf<T>() : CTZ(static_cast<unsigned_type>(x));
 }
 
@@ -105,8 +105,8 @@
 // Find the bit position of the most significant bit (0-based), or -1 if there were no bits set.
 template <typename T>
 constexpr ssize_t MostSignificantBit(T value) {
-  static_assert(std::is_integral<T>::value, "T must be integral");
-  static_assert(std::is_unsigned<T>::value, "T must be unsigned");
+  static_assert(std::is_integral_v<T>, "T must be integral");
+  static_assert(std::is_unsigned_v<T>, "T must be unsigned");
   static_assert(std::numeric_limits<T>::radix == 2, "Unexpected radix!");
   return (value == 0) ? -1 : std::numeric_limits<T>::digits - 1 - CLZ(value);
 }
@@ -114,8 +114,8 @@
 // Find the bit position of the least significant bit (0-based), or -1 if there were no bits set.
 template <typename T>
 constexpr ssize_t LeastSignificantBit(T value) {
-  static_assert(std::is_integral<T>::value, "T must be integral");
-  static_assert(std::is_unsigned<T>::value, "T must be unsigned");
+  static_assert(std::is_integral_v<T>, "T must be integral");
+  static_assert(std::is_unsigned_v<T>, "T must be unsigned");
   return (value == 0) ? -1 : CTZ(value);
 }
 
@@ -127,8 +127,8 @@
 
 template <typename T>
 constexpr T RoundUpToPowerOfTwo(T x) {
-  static_assert(std::is_integral<T>::value, "T must be integral");
-  static_assert(std::is_unsigned<T>::value, "T must be unsigned");
+  static_assert(std::is_integral_v<T>, "T must be integral");
+  static_assert(std::is_unsigned_v<T>, "T must be unsigned");
   // NOTE: Undefined if x > (1 << (std::numeric_limits<T>::digits - 1)).
   return (x < 2u) ? x : static_cast<T>(1u) << (std::numeric_limits<T>::digits - CLZ(x - 1u));
 }
@@ -136,21 +136,21 @@
 // Return highest possible N - a power of two - such that val >= N.
 template <typename T>
 constexpr T TruncToPowerOfTwo(T val) {
-  static_assert(std::is_integral<T>::value, "T must be integral");
-  static_assert(std::is_unsigned<T>::value, "T must be unsigned");
+  static_assert(std::is_integral_v<T>, "T must be integral");
+  static_assert(std::is_unsigned_v<T>, "T must be unsigned");
   return (val != 0) ? static_cast<T>(1u) << (BitSizeOf<T>() - CLZ(val) - 1u) : 0;
 }
 
 template<typename T>
 constexpr bool IsPowerOfTwo(T x) {
-  static_assert(std::is_integral<T>::value, "T must be integral");
+  static_assert(std::is_integral_v<T>, "T must be integral");
   // TODO: assert unsigned. There is currently many uses with signed values.
   return (x & (x - 1)) == 0;
 }
 
 template<typename T>
 constexpr int WhichPowerOf2(T x) {
-  static_assert(std::is_integral<T>::value, "T must be integral");
+  static_assert(std::is_integral_v<T>, "T must be integral");
   // TODO: assert unsigned. There is currently many uses with signed values.
   DCHECK((x != 0) && IsPowerOfTwo(x));
   return CTZ(x);
@@ -168,10 +168,10 @@
 }
 
 template<typename T>
-constexpr T RoundUp(T x, typename std::remove_reference<T>::type n) WARN_UNUSED;
+constexpr T RoundUp(T x, std::remove_reference_t<T> n) WARN_UNUSED;
 
 template<typename T>
-constexpr T RoundUp(T x, typename std::remove_reference<T>::type n) {
+constexpr T RoundUp(T x, std::remove_reference_t<T> n) {
   return RoundDown(x + n - 1, n);
 }
 
@@ -265,7 +265,7 @@
 constexpr bool IsInt(T value) {
   static_assert(kBits > 0, "kBits cannot be zero.");
   static_assert(kBits <= BitSizeOf<T>(), "kBits must be <= max.");
-  static_assert(std::is_signed<T>::value, "Needs a signed type.");
+  static_assert(std::is_signed_v<T>, "Needs a signed type.");
   // Corner case for "use all bits." Can't use the limits, as they would overflow, but it is
   // trivially true.
   return (kBits == BitSizeOf<T>()) ?
@@ -277,12 +277,12 @@
 constexpr bool IsUint(T value) {
   static_assert(kBits > 0, "kBits cannot be zero.");
   static_assert(kBits <= BitSizeOf<T>(), "kBits must be <= max.");
-  static_assert(std::is_integral<T>::value, "Needs an integral type.");
+  static_assert(std::is_integral_v<T>, "Needs an integral type.");
   // Corner case for "use all bits." Can't use the limits, as they would overflow, but it is
   // trivially true.
   // NOTE: To avoid triggering assertion in GetIntLimit(kBits+1) if kBits+1==BitSizeOf<T>(),
   // use GetIntLimit(kBits)*2u. The unsigned arithmetic works well for us if it overflows.
-  using unsigned_type = typename std::make_unsigned<T>::type;
+  using unsigned_type = std::make_unsigned_t<T>;
   return (0 <= value) &&
       (kBits == BitSizeOf<T>() ||
           (static_cast<unsigned_type>(value) <= GetIntLimit<unsigned_type>(kBits) * 2u - 1u));
@@ -291,8 +291,8 @@
 template <size_t kBits, typename T>
 constexpr bool IsAbsoluteUint(T value) {
   static_assert(kBits <= BitSizeOf<T>(), "kBits must be <= max.");
-  static_assert(std::is_integral<T>::value, "Needs an integral type.");
-  using unsigned_type = typename std::make_unsigned<T>::type;
+  static_assert(std::is_integral_v<T>, "Needs an integral type.");
+  using unsigned_type = std::make_unsigned_t<T>;
   return (kBits == BitSizeOf<T>())
       ? true
       : IsUint<kBits>(value < 0
@@ -303,23 +303,23 @@
 // Generate maximum/minimum values for signed/unsigned n-bit integers
 template <typename T>
 constexpr T MaxInt(size_t bits) {
-  DCHECK(std::is_unsigned<T>::value || bits > 0u) << "bits cannot be zero for signed.";
+  DCHECK(std::is_unsigned_v<T> || bits > 0u) << "bits cannot be zero for signed.";
   DCHECK_LE(bits, BitSizeOf<T>());
-  using unsigned_type = typename std::make_unsigned<T>::type;
+  using unsigned_type = std::make_unsigned_t<T>;
   return bits == BitSizeOf<T>()
       ? std::numeric_limits<T>::max()
-      : std::is_signed<T>::value
+      : std::is_signed_v<T>
           ? ((bits == 1u) ? 0 : static_cast<T>(MaxInt<unsigned_type>(bits - 1)))
           : static_cast<T>(UINT64_C(1) << bits) - static_cast<T>(1);
 }
 
 template <typename T>
 constexpr T MinInt(size_t bits) {
-  DCHECK(std::is_unsigned<T>::value || bits > 0) << "bits cannot be zero for signed.";
+  DCHECK(std::is_unsigned_v<T> || bits > 0) << "bits cannot be zero for signed.";
   DCHECK_LE(bits, BitSizeOf<T>());
   return bits == BitSizeOf<T>()
       ? std::numeric_limits<T>::min()
-      : std::is_signed<T>::value
+      : std::is_signed_v<T>
           ? ((bits == 1u) ? -1 : static_cast<T>(-1) - MaxInt<T>(bits))
           : static_cast<T>(0);
 }
@@ -334,7 +334,7 @@
 // Returns value with bit set in hightest one-bit position or 0 if 0.  (java.lang.X.highestOneBit).
 template <typename T>
 inline static T HighestOneBitValue(T opnd) {
-  using unsigned_type = typename std::make_unsigned<T>::type;
+  using unsigned_type = std::make_unsigned_t<T>;
   T res;
   if (opnd == 0) {
     res = 0;
@@ -351,7 +351,7 @@
   int mask = BitSizeOf<T>() - 1;
   int unsigned_right_shift = left ? (-distance & mask) : (distance & mask);
   int signed_left_shift = left ? (distance & mask) : (-distance & mask);
-  using unsigned_type = typename std::make_unsigned<T>::type;
+  using unsigned_type = std::make_unsigned_t<T>;
   return (static_cast<unsigned_type>(opnd) >> unsigned_right_shift) | (opnd << signed_left_shift);
 }
 
@@ -485,7 +485,7 @@
 
   const T bitfield_unsigned =
       static_cast<T>((val >> lsb) & MaskLeastSignificant<T>(width));
-  if (std::is_signed<T>::value) {
+  if (std::is_signed_v<T>) {
     // Perform sign extension
     if (width == 0) {  // Avoid underflow.
       return static_cast<T>(0);
diff --git a/libartbase/base/bit_utils_iterator.h b/libartbase/base/bit_utils_iterator.h
index 4975ebf..bfcff86 100644
--- a/libartbase/base/bit_utils_iterator.h
+++ b/libartbase/base/bit_utils_iterator.h
@@ -34,8 +34,8 @@
 template <typename T, typename Iter>
 class BitIteratorBase
     : public std::iterator<std::forward_iterator_tag, uint32_t, ptrdiff_t, void, void> {
-  static_assert(std::is_integral<T>::value, "T must be integral");
-  static_assert(std::is_unsigned<T>::value, "T must be unsigned");
+  static_assert(std::is_integral_v<T>, "T must be integral");
+  static_assert(std::is_unsigned_v<T>, "T must be unsigned");
 
   static_assert(sizeof(T) == sizeof(uint32_t) || sizeof(T) == sizeof(uint64_t), "Unsupported size");
 
diff --git a/libartbase/base/casts.h b/libartbase/base/casts.h
index cedd624..c88f589 100644
--- a/libartbase/base/casts.h
+++ b/libartbase/base/casts.h
@@ -71,7 +71,7 @@
 
 template<typename To, typename From>     // use like this: down_cast<T*>(foo);
 inline To down_cast(From* f) {                   // so we only accept pointers
-  static_assert(std::is_base_of<From, typename std::remove_pointer<To>::type>::value,
+  static_assert(std::is_base_of_v<From, std::remove_pointer_t<To>>,
                 "down_cast unsafe as To is not a subtype of From");
 
   return static_cast<To>(f);
@@ -79,7 +79,7 @@
 
 template<typename To, typename From>     // use like this: down_cast<T&>(foo);
 inline To down_cast(From& f) {           // so we only accept references
-  static_assert(std::is_base_of<From, typename std::remove_reference<To>::type>::value,
+  static_assert(std::is_base_of_v<From, std::remove_reference_t<To>>,
                 "down_cast unsafe as To is not a subtype of From");
 
   return static_cast<To>(f);
@@ -121,24 +121,22 @@
 
 template <typename Dest, typename Source>
 constexpr
-typename std::enable_if<!std::is_enum<Source>::value, Dest>::type
-enum_cast(Source value) {
-  return static_cast<Dest>(
-      dchecked_integral_cast<typename std::underlying_type<Dest>::type>(value));
+std::enable_if_t<!std::is_enum_v<Source>, Dest> enum_cast(Source value) {
+  return static_cast<Dest>(dchecked_integral_cast<std::underlying_type_t<Dest>>(value));
 }
 
 template <typename Dest = void, typename Source>
 constexpr
-typename std::enable_if<std::is_enum<Source>::value,
-                        typename std::conditional<std::is_same<Dest, void>::value,
-                                                  std::underlying_type<Source>,
-                                                  Identity<Dest>>::type>::type::type
+typename std::enable_if_t<std::is_enum_v<Source>,
+                          std::conditional_t<std::is_same_v<Dest, void>,
+                                             std::underlying_type<Source>,
+                                             Identity<Dest>>>::type
 enum_cast(Source value) {
-  using return_type = typename std::conditional<std::is_same<Dest, void>::value,
-                                                std::underlying_type<Source>,
-                                                Identity<Dest>>::type::type;
+  using return_type = typename std::conditional_t<std::is_same_v<Dest, void>,
+                                                  std::underlying_type<Source>,
+                                                  Identity<Dest>>::type;
   return dchecked_integral_cast<return_type>(
-      static_cast<typename std::underlying_type<Source>::type>(value));
+      static_cast<std::underlying_type_t<Source>>(value));
 }
 
 // A version of reinterpret_cast<>() between pointers and int64_t/uint64_t
@@ -147,9 +145,9 @@
 template <typename Dest, typename Source>
 inline Dest reinterpret_cast64(Source source) {
   // This is the overload for casting from int64_t/uint64_t to a pointer.
-  static_assert(std::is_same<Source, int64_t>::value || std::is_same<Source, uint64_t>::value,
+  static_assert(std::is_same_v<Source, int64_t> || std::is_same_v<Source, uint64_t>,
                 "Source must be int64_t or uint64_t.");
-  static_assert(std::is_pointer<Dest>::value, "Dest must be a pointer.");
+  static_assert(std::is_pointer_v<Dest>, "Dest must be a pointer.");
   // Check that we don't lose any non-0 bits here.
   DCHECK_EQ(static_cast<Source>(static_cast<uintptr_t>(source)), source);
   return reinterpret_cast<Dest>(static_cast<uintptr_t>(source));
@@ -158,7 +156,7 @@
 template <typename Dest, typename Source>
 inline Dest reinterpret_cast64(Source* ptr) {
   // This is the overload for casting from a pointer to int64_t/uint64_t.
-  static_assert(std::is_same<Dest, int64_t>::value || std::is_same<Dest, uint64_t>::value,
+  static_assert(std::is_same_v<Dest, int64_t> || std::is_same_v<Dest, uint64_t>,
                 "Dest must be int64_t or uint64_t.");
   static_assert(sizeof(uintptr_t) <= sizeof(Dest), "Expecting at most 64-bit pointers.");
   return static_cast<Dest>(reinterpret_cast<uintptr_t>(ptr));
@@ -170,9 +168,9 @@
 template <typename Dest, typename Source>
 inline Dest reinterpret_cast32(Source source) {
   // This is the overload for casting from int32_t/uint32_t to a pointer.
-  static_assert(std::is_same<Source, int32_t>::value || std::is_same<Source, uint32_t>::value,
+  static_assert(std::is_same_v<Source, int32_t> || std::is_same_v<Source, uint32_t>,
                 "Source must be int32_t or uint32_t.");
-  static_assert(std::is_pointer<Dest>::value, "Dest must be a pointer.");
+  static_assert(std::is_pointer_v<Dest>, "Dest must be a pointer.");
   // Check that we don't lose any non-0 bits here.
   static_assert(sizeof(uintptr_t) >= sizeof(Source), "Expecting at least 32-bit pointers.");
   return reinterpret_cast<Dest>(static_cast<uintptr_t>(static_cast<uint32_t>(source)));
@@ -181,7 +179,7 @@
 template <typename Dest, typename Source>
 inline Dest reinterpret_cast32(Source* ptr) {
   // This is the overload for casting from a pointer to int32_t/uint32_t.
-  static_assert(std::is_same<Dest, int32_t>::value || std::is_same<Dest, uint32_t>::value,
+  static_assert(std::is_same_v<Dest, int32_t> || std::is_same_v<Dest, uint32_t>,
                 "Dest must be int32_t or uint32_t.");
   static_assert(sizeof(uintptr_t) >= sizeof(Dest), "Expecting at least 32-bit pointers.");
   return static_cast<Dest>(dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(ptr)));
diff --git a/libartbase/base/dchecked_vector.h b/libartbase/base/dchecked_vector.h
index 66e9638..ecb4d2a 100644
--- a/libartbase/base/dchecked_vector.h
+++ b/libartbase/base/dchecked_vector.h
@@ -37,7 +37,7 @@
 class dchecked_vector : private std::vector<T, Alloc> {
  private:
   // std::vector<> has a slightly different specialization for bool. We don't provide that.
-  static_assert(!std::is_same<T, bool>::value, "Not implemented for bool.");
+  static_assert(!std::is_same_v<T, bool>, "Not implemented for bool.");
   using Base = std::vector<T, Alloc>;
 
  public:
diff --git a/libartbase/base/hash_set.h b/libartbase/base/hash_set.h
index 7ed8c61..c4af1b6 100644
--- a/libartbase/base/hash_set.h
+++ b/libartbase/base/hash_set.h
@@ -50,9 +50,9 @@
   // Conversion from iterator to const_iterator.
   template <class OtherElem,
             class OtherHashSetType,
-            typename = typename std::enable_if<
-                std::is_same<Elem, const OtherElem>::value &&
-                std::is_same<HashSetType, const OtherHashSetType>::value>::type>
+            typename = std::enable_if_t<
+                std::is_same_v<Elem, const OtherElem> &&
+                std::is_same_v<HashSetType, const OtherHashSetType>>>
   HashSetIterator(const HashSetIterator<OtherElem, OtherHashSetType>& other)
       : index_(other.index_), hash_set_(other.hash_set_) {}
 
@@ -102,10 +102,10 @@
 bool operator==(const HashSetIterator<Elem1, HashSetType1>& lhs,
                 const HashSetIterator<Elem2, HashSetType2>& rhs) {
   static_assert(
-      std::is_convertible<HashSetIterator<Elem1, HashSetType1>,
-                          HashSetIterator<Elem2, HashSetType2>>::value ||
-      std::is_convertible<HashSetIterator<Elem2, HashSetType2>,
-                          HashSetIterator<Elem1, HashSetType1>>::value, "Bad iterator types.");
+      std::is_convertible_v<HashSetIterator<Elem1, HashSetType1>,
+                            HashSetIterator<Elem2, HashSetType2>> ||
+      std::is_convertible_v<HashSetIterator<Elem2, HashSetType2>,
+                            HashSetIterator<Elem1, HashSetType1>>, "Bad iterator types.");
   DCHECK_EQ(lhs.hash_set_, rhs.hash_set_);
   return lhs.index_ == rhs.index_;
 }
@@ -140,9 +140,7 @@
 };
 
 template <class T>
-using DefaultHashFn = typename std::conditional<std::is_same<T, std::string>::value,
-                                                DataHash,
-                                                std::hash<T>>::type;
+using DefaultHashFn = std::conditional_t<std::is_same_v<T, std::string>, DataHash, std::hash<T>>;
 
 struct DefaultStringEquals {
   // Allow comparison with anything that can be compared to std::string,
@@ -154,9 +152,8 @@
 };
 
 template <class T>
-using DefaultPred = typename std::conditional<std::is_same<T, std::string>::value,
-                                              DefaultStringEquals,
-                                              std::equal_to<T>>::type;
+using DefaultPred =
+    std::conditional_t<std::is_same_v<T, std::string>, DefaultStringEquals, std::equal_to<T>>;
 
 // Low memory version of a hash set, uses less memory than std::unordered_multiset since elements
 // aren't boxed. Uses linear probing to resolve collisions.
@@ -509,7 +506,7 @@
     return InsertWithHash(std::move(element), hashfn_(element));
   }
 
-  template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
+  template <typename U, typename = std::enable_if_t<std::is_convertible_v<U, T>>>
   std::pair<iterator, bool> InsertWithHash(U&& element, size_t hash) {
     DCHECK_EQ(hash, hashfn_(element));
     if (num_elements_ >= elements_until_expand_) {
@@ -537,7 +534,7 @@
     return PutWithHash(std::move(element), hashfn_(element));
   }
 
-  template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
+  template <typename U, typename = std::enable_if_t<std::is_convertible_v<U, T>>>
   void PutWithHash(U&& element, size_t hash) {
     DCHECK_EQ(hash, hashfn_(element));
     if (num_elements_ >= elements_until_expand_) {
diff --git a/libartbase/base/intrusive_forward_list.h b/libartbase/base/intrusive_forward_list.h
index 29db64d..2e66f3e 100644
--- a/libartbase/base/intrusive_forward_list.h
+++ b/libartbase/base/intrusive_forward_list.h
@@ -55,8 +55,7 @@
 class IntrusiveForwardListBaseHookTraits;
 
 template <typename T,
-          typename HookTraits =
-              IntrusiveForwardListBaseHookTraits<typename std::remove_const<T>::type>>
+          typename HookTraits = IntrusiveForwardListBaseHookTraits<std::remove_const_t<T>>>
 class IntrusiveForwardList;
 
 template <typename T, typename HookTraits>
@@ -69,7 +68,7 @@
 
   // Conversion from iterator to const_iterator.
   template <typename OtherT,
-            typename = typename std::enable_if<std::is_same<T, const OtherT>::value>::type>
+            typename = std::enable_if_t<std::is_same_v<T, const OtherT>>>
   IntrusiveForwardListIterator(const IntrusiveForwardListIterator<OtherT, HookTraits>& src)  // NOLINT, implicit
       : hook_(src.hook_) { }
 
@@ -106,20 +105,20 @@
   friend class IntrusiveForwardList;
 
   template <typename OtherT1, typename OtherT2, typename OtherTraits>
-  friend typename std::enable_if<std::is_same<const OtherT1, const OtherT2>::value, bool>::type
+  friend std::enable_if_t<std::is_same_v<const OtherT1, const OtherT2>, bool>
   operator==(const IntrusiveForwardListIterator<OtherT1, OtherTraits>& lhs,
              const IntrusiveForwardListIterator<OtherT2, OtherTraits>& rhs);
 };
 
 template <typename T, typename OtherT, typename HookTraits>
-typename std::enable_if<std::is_same<const T, const OtherT>::value, bool>::type operator==(
+std::enable_if_t<std::is_same_v<const T, const OtherT>, bool> operator==(
     const IntrusiveForwardListIterator<T, HookTraits>& lhs,
     const IntrusiveForwardListIterator<OtherT, HookTraits>& rhs) {
   return lhs.hook_ == rhs.hook_;
 }
 
 template <typename T, typename OtherT, typename HookTraits>
-typename std::enable_if<std::is_same<const T, const OtherT>::value, bool>::type operator!=(
+std::enable_if_t<std::is_same_v<const T, const OtherT>, bool> operator!=(
     const IntrusiveForwardListIterator<T, HookTraits>& lhs,
     const IntrusiveForwardListIterator<OtherT, HookTraits>& rhs) {
   return !(lhs == rhs);
diff --git a/libartbase/base/intrusive_forward_list_test.cc b/libartbase/base/intrusive_forward_list_test.cc
index e97c304..595210b 100644
--- a/libartbase/base/intrusive_forward_list_test.cc
+++ b/libartbase/base/intrusive_forward_list_test.cc
@@ -731,7 +731,7 @@
   ModifyValue<IFLTestValueList>();
   // Does not compile with ConstIFLTestValueList because LHS of the assignment is const.
   // ModifyValue<ConstIFLTestValueList>();
-  static_assert(std::is_const<ConstIFLTestValueList::iterator::value_type>::value, "Const check.");
+  static_assert(std::is_const_v<ConstIFLTestValueList::iterator::value_type>);
   ModifyValue<IFLTestValue2List>();
 }
 
diff --git a/libartbase/base/leb128.h b/libartbase/base/leb128.h
index b866d37..4f0f975 100644
--- a/libartbase/base/leb128.h
+++ b/libartbase/base/leb128.h
@@ -216,7 +216,7 @@
 // (2) there is another Leb128 value before this one.
 template <typename T>
 static inline T* ReverseSearchUnsignedLeb128(T* end_ptr) {
-  static_assert(std::is_same<typename std::remove_const<T>::type, uint8_t>::value,
+  static_assert(std::is_same_v<std::remove_const_t<T>, uint8_t>,
                 "T must be a uint8_t");
   T* ptr = end_ptr;
 
@@ -257,7 +257,7 @@
 
 template <typename Vector>
 static inline void EncodeUnsignedLeb128(Vector* dest, uint32_t value) {
-  static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
+  static_assert(std::is_same_v<typename Vector::value_type, uint8_t>, "Invalid value type");
   uint8_t out = value & 0x7f;
   value >>= 7;
   while (value != 0) {
@@ -296,7 +296,7 @@
 
 template<typename Vector>
 static inline void EncodeSignedLeb128(Vector* dest, int32_t value) {
-  static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
+  static_assert(std::is_same_v<typename Vector::value_type, uint8_t>, "Invalid value type");
   uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6;
   uint8_t out = value & 0x7f;
   while (extra_bits != 0u) {
@@ -311,7 +311,7 @@
 // An encoder that pushes int32_t/uint32_t data onto the given std::vector.
 template <typename Vector = std::vector<uint8_t>>
 class Leb128Encoder {
-  static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
+  static_assert(std::is_same_v<typename Vector::value_type, uint8_t>, "Invalid value type");
 
  public:
   explicit Leb128Encoder(Vector* data) : data_(data) {
@@ -359,7 +359,7 @@
 template <typename Vector = std::vector<uint8_t>>
 class Leb128EncodingVector final : private Vector,
                                    public Leb128Encoder<Vector> {
-  static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
+  static_assert(std::is_same_v<typename Vector::value_type, uint8_t>, "Invalid value type");
 
  public:
   Leb128EncodingVector() : Leb128Encoder<Vector>(this) { }
diff --git a/libartbase/base/safe_map.h b/libartbase/base/safe_map.h
index 0d7a8c1..7ae85d4 100644
--- a/libartbase/base/safe_map.h
+++ b/libartbase/base/safe_map.h
@@ -149,7 +149,7 @@
 
   template <typename CreateFn>
   V& GetOrCreate(const K& k, CreateFn create) {
-    static_assert(std::is_same<V, typename std::result_of<CreateFn()>::type>::value,
+    static_assert(std::is_same_v<V, std::result_of_t<CreateFn()>>,
                   "Argument `create` should return a value of type V.");
     auto lb = lower_bound(k);
     if (lb != end() && !key_comp()(k, lb->first)) {
diff --git a/libartbase/base/scoped_arena_containers.h b/libartbase/base/scoped_arena_containers.h
index 882fdcc..5f0cfe6 100644
--- a/libartbase/base/scoped_arena_containers.h
+++ b/libartbase/base/scoped_arena_containers.h
@@ -274,7 +274,7 @@
 class ArenaDelete<T[]> {
  public:
   void operator()(T* ptr ATTRIBUTE_UNUSED) const {
-    static_assert(std::is_trivially_destructible<T>::value,
+    static_assert(std::is_trivially_destructible_v<T>,
                   "ArenaUniquePtr does not support non-trivially-destructible arrays.");
     // TODO: Implement debug checks, and MEMORY_TOOL support.
   }
diff --git a/libartbase/base/stl_util.h b/libartbase/base/stl_util.h
index dfe994e..0ae4fd2 100644
--- a/libartbase/base/stl_util.h
+++ b/libartbase/base/stl_util.h
@@ -290,7 +290,7 @@
 template <typename Val>
 struct NonNullFilter {
  public:
-  static_assert(std::is_pointer<Val>::value, "Must be pointer type!");
+  static_assert(std::is_pointer_v<Val>, "Must be pointer type!");
   constexpr bool operator()(Val v) const {
     return v != nullptr;
   }
diff --git a/libartbase/base/transform_array_ref.h b/libartbase/base/transform_array_ref.h
index 2f56e9b..9413b22 100644
--- a/libartbase/base/transform_array_ref.h
+++ b/libartbase/base/transform_array_ref.h
@@ -36,7 +36,7 @@
   using FallbackConstIter = std::iterator<std::random_access_iterator_tag, void, void, void, void>;
   using PreferredConstIter =
       TransformIterator<typename ArrayRef<BaseType>::const_iterator, Function>;
-  template <typename F, typename = typename std::result_of<F(const BaseType&)>::type>
+  template <typename F, typename = std::result_of_t<F(const BaseType&)>>
   static PreferredConstIter ConstIterHelper(int&);
   template <typename F>
   static FallbackConstIter ConstIterHelper(const int&);
@@ -50,15 +50,12 @@
   using pointer = typename Iter::pointer;
   using const_pointer = typename ConstIter::pointer;
   using iterator = Iter;
-  using const_iterator = typename std::conditional<
-      std::is_same<ConstIter, FallbackConstIter>::value,
-      void,
-      ConstIter>::type;
+  using const_iterator =
+      std::conditional_t<std::is_same_v<ConstIter, FallbackConstIter>, void, ConstIter>;
   using reverse_iterator = std::reverse_iterator<Iter>;
-  using const_reverse_iterator = typename std::conditional<
-      std::is_same<ConstIter, FallbackConstIter>::value,
-      void,
-      std::reverse_iterator<ConstIter>>::type;
+  using const_reverse_iterator = std::conditional_t<std::is_same_v<ConstIter, FallbackConstIter>,
+                                                    void,
+                                                    std::reverse_iterator<ConstIter>>;
   using difference_type = typename ArrayRef<BaseType>::difference_type;
   using size_type = typename ArrayRef<BaseType>::size_type;
 
@@ -71,7 +68,7 @@
       : data_(base, fn) { }
 
   template <typename OtherBT,
-            typename = typename std::enable_if<std::is_same<BaseType, const OtherBT>::value>::type>
+            typename = std::enable_if_t<std::is_same_v<BaseType, const OtherBT>>>
   TransformArrayRef(const TransformArrayRef<OtherBT, Function>& other)
       : TransformArrayRef(other.base(), other.GetFunction()) { }
 
@@ -80,7 +77,7 @@
   TransformArrayRef& operator=(const TransformArrayRef& other) = default;
 
   template <typename OtherBT,
-            typename = typename std::enable_if<std::is_same<BaseType, const OtherBT>::value>::type>
+            typename = std::enable_if_t<std::is_same_v<BaseType, const OtherBT>>>
   TransformArrayRef& operator=(const TransformArrayRef<OtherBT, Function>& other) {
     return *this = TransformArrayRef(other.base(), other.GetFunction());
   }
diff --git a/libartbase/base/transform_array_ref_test.cc b/libartbase/base/transform_array_ref_test.cc
index 896209b..4ac6978 100644
--- a/libartbase/base/transform_array_ref_test.cc
+++ b/libartbase/base/transform_array_ref_test.cc
@@ -44,12 +44,11 @@
   auto taref = MakeTransformArrayRef(input, add1);
   using TarefIter = decltype(taref)::iterator;
   using ConstTarefIter = decltype(taref)::const_iterator;
-  static_assert(std::is_same<int, decltype(taref)::value_type>::value, "value_type");
-  static_assert(std::is_same<TarefIter, decltype(taref)::pointer>::value, "pointer");
-  static_assert(std::is_same<int, decltype(taref)::reference>::value, "reference");
-  static_assert(std::is_same<ConstTarefIter, decltype(taref)::const_pointer>::value,
-                "const_pointer");
-  static_assert(std::is_same<int, decltype(taref)::const_reference>::value, "const_reference");
+  static_assert(std::is_same_v<int, decltype(taref)::value_type>);
+  static_assert(std::is_same_v<TarefIter, decltype(taref)::pointer>);
+  static_assert(std::is_same_v<int, decltype(taref)::reference>);
+  static_assert(std::is_same_v<ConstTarefIter, decltype(taref)::const_pointer>);
+  static_assert(std::is_same_v<int, decltype(taref)::const_reference>);
 
   std::copy(taref.begin(), taref.end(), std::back_inserter(output));
   ASSERT_EQ(std::vector<int>({ 8, 7, 5, 1 }), output);
@@ -84,12 +83,12 @@
 
   auto taref = MakeTransformArrayRef(input, sub1);
   using TarefIter = decltype(taref)::iterator;
-  static_assert(std::is_same<void, decltype(taref)::const_iterator>::value, "const_iterator");
-  static_assert(std::is_same<int, decltype(taref)::value_type>::value, "value_type");
-  static_assert(std::is_same<TarefIter, decltype(taref)::pointer>::value, "pointer");
-  static_assert(std::is_same<int, decltype(taref)::reference>::value, "reference");
-  static_assert(std::is_same<void, decltype(taref)::const_pointer>::value, "const_pointer");
-  static_assert(std::is_same<void, decltype(taref)::const_reference>::value, "const_reference");
+  static_assert(std::is_same_v<void, decltype(taref)::const_iterator>);
+  static_assert(std::is_same_v<int, decltype(taref)::value_type>);
+  static_assert(std::is_same_v<TarefIter, decltype(taref)::pointer>);
+  static_assert(std::is_same_v<int, decltype(taref)::reference>);
+  static_assert(std::is_same_v<void, decltype(taref)::const_pointer>);
+  static_assert(std::is_same_v<void, decltype(taref)::const_reference>);
 
   std::copy(taref.begin(), taref.end(), std::back_inserter(output));
   ASSERT_EQ(std::vector<int>({ 3, 3, 4, 6, 9 }), output);
@@ -119,12 +118,11 @@
   std::vector<int> output;
 
   auto taref = MakeTransformArrayRef(input, ref);
-  static_assert(std::is_same<int, decltype(taref)::value_type>::value, "value_type");
-  static_assert(std::is_same<int*, decltype(taref)::pointer>::value, "pointer");
-  static_assert(std::is_same<int&, decltype(taref)::reference>::value, "reference");
-  static_assert(std::is_same<const int*, decltype(taref)::const_pointer>::value, "const_pointer");
-  static_assert(std::is_same<const int&, decltype(taref)::const_reference>::value,
-                "const_reference");
+  static_assert(std::is_same_v<int, decltype(taref)::value_type>);
+  static_assert(std::is_same_v<int*, decltype(taref)::pointer>);
+  static_assert(std::is_same_v<int&, decltype(taref)::reference>);
+  static_assert(std::is_same_v<const int*, decltype(taref)::const_pointer>);
+  static_assert(std::is_same_v<const int&, decltype(taref)::const_reference>);
 
   std::copy(taref.begin(), taref.end(), std::back_inserter(output));
   ASSERT_EQ(std::vector<int>({ 1, 0, 1, 0, 3, 1 }), output);
@@ -163,12 +161,11 @@
   const std::vector<ValueHolder>& cinput = input;
 
   auto ctaref = MakeTransformArrayRef(cinput, ref);
-  static_assert(std::is_same<int, decltype(ctaref)::value_type>::value, "value_type");
-  static_assert(std::is_same<const int*, decltype(ctaref)::pointer>::value, "pointer");
-  static_assert(std::is_same<const int&, decltype(ctaref)::reference>::value, "reference");
-  static_assert(std::is_same<const int*, decltype(ctaref)::const_pointer>::value, "const_pointer");
-  static_assert(std::is_same<const int&, decltype(ctaref)::const_reference>::value,
-                "const_reference");
+  static_assert(std::is_same_v<int, decltype(ctaref)::value_type>);
+  static_assert(std::is_same_v<const int*, decltype(ctaref)::pointer>);
+  static_assert(std::is_same_v<const int&, decltype(ctaref)::reference>);
+  static_assert(std::is_same_v<const int*, decltype(ctaref)::const_pointer>);
+  static_assert(std::is_same_v<const int&, decltype(ctaref)::const_reference>);
 
   std::copy(ctaref.begin(), ctaref.end(), std::back_inserter(output));
   ASSERT_EQ(std::vector<int>({ 1, 0, 1, 0, 3, 1 }), output);
diff --git a/libartbase/base/transform_iterator.h b/libartbase/base/transform_iterator.h
index 5b0574d..062c88b 100644
--- a/libartbase/base/transform_iterator.h
+++ b/libartbase/base/transform_iterator.h
@@ -39,23 +39,20 @@
 template <typename BaseIterator, typename Function>
 class TransformIterator {
  private:
-  static_assert(std::is_base_of<
-                    std::input_iterator_tag,
-                    typename std::iterator_traits<BaseIterator>::iterator_category>::value,
+  static_assert(std::is_base_of_v<std::input_iterator_tag,
+                                  typename std::iterator_traits<BaseIterator>::iterator_category>,
                 "Transform iterator base must be an input iterator.");
 
   using InputType = typename std::iterator_traits<BaseIterator>::reference;
-  using ResultType = typename std::result_of<Function(InputType)>::type;
+  using ResultType = std::result_of_t<Function(InputType)>;
 
  public:
   using iterator_category = typename std::iterator_traits<BaseIterator>::iterator_category;
-  using value_type =
-      typename std::remove_const<typename std::remove_reference<ResultType>::type>::type;
+  using value_type = std::remove_const_t<std::remove_reference_t<ResultType>>;
   using difference_type = typename std::iterator_traits<BaseIterator>::difference_type;
-  using pointer = typename std::conditional<
-      std::is_reference<ResultType>::value,
-      typename std::add_pointer<typename std::remove_reference<ResultType>::type>::type,
-      TransformIterator>::type;
+  using pointer = std::conditional_t<std::is_reference_v<ResultType>,
+                                     std::add_pointer_t<std::remove_reference_t<ResultType>>,
+                                     TransformIterator>;
   using reference = ResultType;
 
   TransformIterator(BaseIterator base, Function fn)
@@ -78,10 +75,9 @@
   }
 
   TransformIterator& operator--() {
-    static_assert(
-        std::is_base_of<std::bidirectional_iterator_tag,
-                        typename std::iterator_traits<BaseIterator>::iterator_category>::value,
-        "BaseIterator must be bidirectional iterator to use operator--()");
+    static_assert(std::is_base_of_v<std::bidirectional_iterator_tag,
+                                    typename std::iterator_traits<BaseIterator>::iterator_category>,
+                  "BaseIterator must be bidirectional iterator to use operator--()");
     --data_.base_;
     return *this;
   }
@@ -97,34 +93,30 @@
   }
 
   reference operator[](difference_type n) const {
-    static_assert(
-        std::is_base_of<std::random_access_iterator_tag,
-                        typename std::iterator_traits<BaseIterator>::iterator_category>::value,
-        "BaseIterator must be random access iterator to use operator[]");
+    static_assert(std::is_base_of_v<std::random_access_iterator_tag,
+                                    typename std::iterator_traits<BaseIterator>::iterator_category>,
+                 "BaseIterator must be random access iterator to use operator[]");
     return GetFunction()(base()[n]);
   }
 
   TransformIterator operator+(difference_type n) const {
-    static_assert(
-        std::is_base_of<std::random_access_iterator_tag,
-                        typename std::iterator_traits<BaseIterator>::iterator_category>::value,
-        "BaseIterator must be random access iterator to use operator+");
+    static_assert(std::is_base_of_v<std::random_access_iterator_tag,
+                                    typename std::iterator_traits<BaseIterator>::iterator_category>,
+                  "BaseIterator must be random access iterator to use operator+");
     return TransformIterator(base() + n, GetFunction());
   }
 
   TransformIterator operator-(difference_type n) const {
-    static_assert(
-        std::is_base_of<std::random_access_iterator_tag,
-                        typename std::iterator_traits<BaseIterator>::iterator_category>::value,
-        "BaseIterator must be random access iterator to use operator-");
+    static_assert(std::is_base_of_v<std::random_access_iterator_tag,
+                                    typename std::iterator_traits<BaseIterator>::iterator_category>,
+                  "BaseIterator must be random access iterator to use operator-");
     return TransformIterator(base() - n, GetFunction());
   }
 
   difference_type operator-(const TransformIterator& other) const {
-    static_assert(
-        std::is_base_of<std::random_access_iterator_tag,
-                        typename std::iterator_traits<BaseIterator>::iterator_category>::value,
-        "BaseIterator must be random access iterator to use operator-");
+    static_assert(std::is_base_of_v<std::random_access_iterator_tag,
+                                    typename std::iterator_traits<BaseIterator>::iterator_category>,
+                  "BaseIterator must be random access iterator to use operator-");
     return base() - other.base();
   }
 
diff --git a/libartbase/base/transform_iterator_test.cc b/libartbase/base/transform_iterator_test.cc
index 5a5c37d..3d16465 100644
--- a/libartbase/base/transform_iterator_test.cc
+++ b/libartbase/base/transform_iterator_test.cc
@@ -45,32 +45,28 @@
   std::vector<int> output;
 
   using vector_titer = decltype(MakeTransformIterator(input.begin(), add1));
-  static_assert(std::is_same<std::random_access_iterator_tag,
-                             vector_titer::iterator_category>::value, "category");
-  static_assert(std::is_same<int, vector_titer::value_type>::value, "value_type");
-  static_assert(std::is_same<vector_titer, vector_titer::pointer>::value, "pointer");
-  static_assert(std::is_same<int, vector_titer::reference>::value, "reference");
+  static_assert(std::is_same_v<std::random_access_iterator_tag, vector_titer::iterator_category>);
+  static_assert(std::is_same_v<int, vector_titer::value_type>);
+  static_assert(std::is_same_v<vector_titer, vector_titer::pointer>);
+  static_assert(std::is_same_v<int, vector_titer::reference>);
 
   using vector_ctiter = decltype(MakeTransformIterator(input.cbegin(), add1));
-  static_assert(std::is_same<std::random_access_iterator_tag,
-                             vector_ctiter::iterator_category>::value, "category");
-  static_assert(std::is_same<int, vector_ctiter::value_type>::value, "value_type");
-  static_assert(std::is_same<vector_ctiter, vector_ctiter::pointer>::value, "pointer");
-  static_assert(std::is_same<int, vector_ctiter::reference>::value, "reference");
+  static_assert(std::is_same_v<std::random_access_iterator_tag, vector_ctiter::iterator_category>);
+  static_assert(std::is_same_v<int, vector_ctiter::value_type>);
+  static_assert(std::is_same_v<vector_ctiter, vector_ctiter::pointer>);
+  static_assert(std::is_same_v<int, vector_ctiter::reference>);
 
   using vector_rtiter = decltype(MakeTransformIterator(input.rbegin(), add1));
-  static_assert(std::is_same<std::random_access_iterator_tag,
-                             vector_rtiter::iterator_category>::value, "category");
-  static_assert(std::is_same<int, vector_rtiter::value_type>::value, "value_type");
-  static_assert(std::is_same<vector_rtiter, vector_rtiter::pointer>::value, "pointer");
-  static_assert(std::is_same<int, vector_rtiter::reference>::value, "reference");
+  static_assert(std::is_same_v<std::random_access_iterator_tag, vector_rtiter::iterator_category>);
+  static_assert(std::is_same_v<int, vector_rtiter::value_type>);
+  static_assert(std::is_same_v<vector_rtiter, vector_rtiter::pointer>);
+  static_assert(std::is_same_v<int, vector_rtiter::reference>);
 
   using vector_crtiter = decltype(MakeTransformIterator(input.crbegin(), add1));
-  static_assert(std::is_same<std::random_access_iterator_tag,
-                             vector_crtiter::iterator_category>::value, "category");
-  static_assert(std::is_same<int, vector_crtiter::value_type>::value, "value_type");
-  static_assert(std::is_same<vector_crtiter, vector_crtiter::pointer>::value, "pointer");
-  static_assert(std::is_same<int, vector_crtiter::reference>::value, "reference");
+  static_assert(std::is_same_v<std::random_access_iterator_tag, vector_crtiter::iterator_category>);
+  static_assert(std::is_same_v<int, vector_crtiter::value_type>);
+  static_assert(std::is_same_v<vector_crtiter, vector_crtiter::pointer>);
+  static_assert(std::is_same_v<int, vector_crtiter::reference>);
 
   std::copy(MakeTransformIterator(input.begin(), add1),
             MakeTransformIterator(input.end(), add1),
@@ -134,11 +130,11 @@
   // Test iterator->const_iterator conversion and comparison.
   auto it = MakeTransformIterator(input.begin(), add1);
   decltype(MakeTransformIterator(input.cbegin(), add1)) cit = it;
-  static_assert(!std::is_same<decltype(it), decltype(cit)>::value, "Types must be different");
+  static_assert(!std::is_same_v<decltype(it), decltype(cit)>, "Types must be different");
   ASSERT_EQ(it, cit);
   auto rit = MakeTransformIterator(input.rbegin(), add1);
   decltype(MakeTransformIterator(input.crbegin(), add1)) crit(rit);
-  static_assert(!std::is_same<decltype(rit), decltype(crit)>::value, "Types must be different");
+  static_assert(!std::is_same_v<decltype(rit), decltype(crit)>, "Types must be different");
   ASSERT_EQ(rit, crit);
 }
 
@@ -148,32 +144,28 @@
   std::vector<int> output;
 
   using list_titer = decltype(MakeTransformIterator(input.begin(), sub1));
-  static_assert(std::is_same<std::bidirectional_iterator_tag,
-                             list_titer::iterator_category>::value, "category");
-  static_assert(std::is_same<int, list_titer::value_type>::value, "value_type");
-  static_assert(std::is_same<list_titer, list_titer::pointer>::value, "pointer");
-  static_assert(std::is_same<int, list_titer::reference>::value, "reference");
+  static_assert(std::is_same_v<std::bidirectional_iterator_tag, list_titer::iterator_category>);
+  static_assert(std::is_same_v<int, list_titer::value_type>);
+  static_assert(std::is_same_v<list_titer, list_titer::pointer>);
+  static_assert(std::is_same_v<int, list_titer::reference>);
 
   using list_ctiter = decltype(MakeTransformIterator(input.cbegin(), sub1));
-  static_assert(std::is_same<std::bidirectional_iterator_tag,
-                             list_ctiter::iterator_category>::value, "category");
-  static_assert(std::is_same<int, list_ctiter::value_type>::value, "value_type");
-  static_assert(std::is_same<list_ctiter, list_ctiter::pointer>::value, "pointer");
-  static_assert(std::is_same<int, list_ctiter::reference>::value, "reference");
+  static_assert(std::is_same_v<std::bidirectional_iterator_tag, list_ctiter::iterator_category>);
+  static_assert(std::is_same_v<int, list_ctiter::value_type>);
+  static_assert(std::is_same_v<list_ctiter, list_ctiter::pointer>);
+  static_assert(std::is_same_v<int, list_ctiter::reference>);
 
   using list_rtiter = decltype(MakeTransformIterator(input.rbegin(), sub1));
-  static_assert(std::is_same<std::bidirectional_iterator_tag,
-                             list_rtiter::iterator_category>::value, "category");
-  static_assert(std::is_same<int, list_rtiter::value_type>::value, "value_type");
-  static_assert(std::is_same<list_rtiter, list_rtiter::pointer>::value, "pointer");
-  static_assert(std::is_same<int, list_rtiter::reference>::value, "reference");
+  static_assert(std::is_same_v<std::bidirectional_iterator_tag, list_rtiter::iterator_category>);
+  static_assert(std::is_same_v<int, list_rtiter::value_type>);
+  static_assert(std::is_same_v<list_rtiter, list_rtiter::pointer>);
+  static_assert(std::is_same_v<int, list_rtiter::reference>);
 
   using list_crtiter = decltype(MakeTransformIterator(input.crbegin(), sub1));
-  static_assert(std::is_same<std::bidirectional_iterator_tag,
-                             list_crtiter::iterator_category>::value, "category");
-  static_assert(std::is_same<int, list_crtiter::value_type>::value, "value_type");
-  static_assert(std::is_same<list_crtiter, list_crtiter::pointer>::value, "pointer");
-  static_assert(std::is_same<int, list_crtiter::reference>::value, "reference");
+  static_assert(std::is_same_v<std::bidirectional_iterator_tag, list_crtiter::iterator_category>);
+  static_assert(std::is_same_v<int, list_crtiter::value_type>);
+  static_assert(std::is_same_v<list_crtiter, list_crtiter::pointer>);
+  static_assert(std::is_same_v<int, list_crtiter::reference>);
 
   std::copy(MakeTransformIterator(input.begin(), sub1),
             MakeTransformIterator(input.end(), sub1),
@@ -202,7 +194,7 @@
   // Test iterator->const_iterator conversion and comparison.
   auto it = MakeTransformIterator(input.begin(), sub1);
   decltype(MakeTransformIterator(input.cbegin(), sub1)) cit = it;
-  static_assert(!std::is_same<decltype(it), decltype(cit)>::value, "Types must be different");
+  static_assert(!std::is_same_v<decltype(it), decltype(cit)>, "Types must be different");
   ASSERT_EQ(it, cit);
 }
 
@@ -212,18 +204,16 @@
   std::vector<int> output;
 
   using flist_titer = decltype(MakeTransformIterator(input.begin(), mul3));
-  static_assert(std::is_same<std::forward_iterator_tag,
-                             flist_titer::iterator_category>::value, "category");
-  static_assert(std::is_same<int, flist_titer::value_type>::value, "value_type");
-  static_assert(std::is_same<flist_titer, flist_titer::pointer>::value, "pointer");
-  static_assert(std::is_same<int, flist_titer::reference>::value, "reference");
+  static_assert(std::is_same_v<std::forward_iterator_tag, flist_titer::iterator_category>);
+  static_assert(std::is_same_v<int, flist_titer::value_type>);
+  static_assert(std::is_same_v<flist_titer, flist_titer::pointer>);
+  static_assert(std::is_same_v<int, flist_titer::reference>);
 
   using flist_ctiter = decltype(MakeTransformIterator(input.cbegin(), mul3));
-  static_assert(std::is_same<std::forward_iterator_tag,
-                             flist_ctiter::iterator_category>::value, "category");
-  static_assert(std::is_same<int, flist_ctiter::value_type>::value, "value_type");
-  static_assert(std::is_same<flist_ctiter, flist_ctiter::pointer>::value, "pointer");
-  static_assert(std::is_same<int, flist_ctiter::reference>::value, "reference");
+  static_assert(std::is_same_v<std::forward_iterator_tag, flist_ctiter::iterator_category>);
+  static_assert(std::is_same_v<int, flist_ctiter::value_type>);
+  static_assert(std::is_same_v<flist_ctiter, flist_ctiter::pointer>);
+  static_assert(std::is_same_v<int, flist_ctiter::reference>);
 
   std::copy(MakeTransformIterator(input.begin(), mul3),
             MakeTransformIterator(input.end(), mul3),
@@ -240,7 +230,7 @@
   // Test iterator->const_iterator conversion and comparison.
   auto it = MakeTransformIterator(input.begin(), mul3);
   decltype(MakeTransformIterator(input.cbegin(), mul3)) cit = it;
-  static_assert(!std::is_same<decltype(it), decltype(cit)>::value, "Types must be different");
+  static_assert(!std::is_same_v<decltype(it), decltype(cit)>, "Types must be different");
   ASSERT_EQ(it, cit);
 }
 
@@ -250,32 +240,28 @@
   std::vector<int> output;
 
   using vector_titer = decltype(MakeTransformIterator(input.begin(), ref));
-  static_assert(std::is_same<std::random_access_iterator_tag,
-                             vector_titer::iterator_category>::value, "category");
-  static_assert(std::is_same<int, vector_titer::value_type>::value, "value_type");
-  static_assert(std::is_same<const int*, vector_titer::pointer>::value, "pointer");
-  static_assert(std::is_same<const int&, vector_titer::reference>::value, "reference");
+  static_assert(std::is_same_v<std::random_access_iterator_tag, vector_titer::iterator_category>);
+  static_assert(std::is_same_v<int, vector_titer::value_type>);
+  static_assert(std::is_same_v<const int*, vector_titer::pointer>);
+  static_assert(std::is_same_v<const int&, vector_titer::reference>);
 
   using vector_ctiter = decltype(MakeTransformIterator(input.cbegin(), ref));
-  static_assert(std::is_same<std::random_access_iterator_tag,
-                             vector_ctiter::iterator_category>::value, "category");
-  static_assert(std::is_same<int, vector_ctiter::value_type>::value, "value_type");
-  static_assert(std::is_same<const int*, vector_ctiter::pointer>::value, "pointer");
-  static_assert(std::is_same<const int&, vector_ctiter::reference>::value, "reference");
+  static_assert(std::is_same_v<std::random_access_iterator_tag, vector_ctiter::iterator_category>);
+  static_assert(std::is_same_v<int, vector_ctiter::value_type>);
+  static_assert(std::is_same_v<const int*, vector_ctiter::pointer>);
+  static_assert(std::is_same_v<const int&, vector_ctiter::reference>);
 
   using vector_rtiter = decltype(MakeTransformIterator(input.rbegin(), ref));
-  static_assert(std::is_same<std::random_access_iterator_tag,
-                             vector_rtiter::iterator_category>::value, "category");
-  static_assert(std::is_same<int, vector_rtiter::value_type>::value, "value_type");
-  static_assert(std::is_same<const int*, vector_rtiter::pointer>::value, "pointer");
-  static_assert(std::is_same<const int&, vector_rtiter::reference>::value, "reference");
+  static_assert(std::is_same_v<std::random_access_iterator_tag, vector_rtiter::iterator_category>);
+  static_assert(std::is_same_v<int, vector_rtiter::value_type>);
+  static_assert(std::is_same_v<const int*, vector_rtiter::pointer>);
+  static_assert(std::is_same_v<const int&, vector_rtiter::reference>);
 
   using vector_crtiter = decltype(MakeTransformIterator(input.crbegin(), ref));
-  static_assert(std::is_same<std::random_access_iterator_tag,
-                             vector_crtiter::iterator_category>::value, "category");
-  static_assert(std::is_same<int, vector_crtiter::value_type>::value, "value_type");
-  static_assert(std::is_same<const int*, vector_crtiter::pointer>::value, "pointer");
-  static_assert(std::is_same<const int&, vector_crtiter::reference>::value, "reference");
+  static_assert(std::is_same_v<std::random_access_iterator_tag, vector_crtiter::iterator_category>);
+  static_assert(std::is_same_v<int, vector_crtiter::value_type>);
+  static_assert(std::is_same_v<const int*, vector_crtiter::pointer>);
+  static_assert(std::is_same_v<const int&, vector_crtiter::reference>);
 
   std::copy(MakeTransformIterator(input.begin(), ref),
             MakeTransformIterator(input.end(), ref),
@@ -343,18 +329,16 @@
   std::vector<int> output;
 
   using vector_titer = decltype(MakeTransformIterator(input.begin(), ref));
-  static_assert(std::is_same<std::random_access_iterator_tag,
-                             vector_titer::iterator_category>::value, "category");
-  static_assert(std::is_same<int, vector_titer::value_type>::value, "value_type");
-  static_assert(std::is_same<int*, vector_titer::pointer>::value, "pointer");
-  static_assert(std::is_same<int&, vector_titer::reference>::value, "reference");
+  static_assert(std::is_same_v<std::random_access_iterator_tag, vector_titer::iterator_category>);
+  static_assert(std::is_same_v<int, vector_titer::value_type>);
+  static_assert(std::is_same_v<int*, vector_titer::pointer>);
+  static_assert(std::is_same_v<int&, vector_titer::reference>);
 
   using vector_rtiter = decltype(MakeTransformIterator(input.rbegin(), ref));
-  static_assert(std::is_same<std::random_access_iterator_tag,
-                             vector_rtiter::iterator_category>::value, "category");
-  static_assert(std::is_same<int, vector_rtiter::value_type>::value, "value_type");
-  static_assert(std::is_same<int*, vector_rtiter::pointer>::value, "pointer");
-  static_assert(std::is_same<int&, vector_rtiter::reference>::value, "reference");
+  static_assert(std::is_same_v<std::random_access_iterator_tag, vector_rtiter::iterator_category>);
+  static_assert(std::is_same_v<int, vector_rtiter::value_type>);
+  static_assert(std::is_same_v<int*, vector_rtiter::pointer>);
+  static_assert(std::is_same_v<int&, vector_rtiter::reference>);
 
   std::copy(MakeTransformIterator(input.begin(), ref),
             MakeTransformIterator(input.end(), ref),
@@ -411,32 +395,28 @@
   std::vector<int> output;
 
   using vector_titer = decltype(MakeTransformIterator(input.begin(), ref));
-  static_assert(std::is_same<std::random_access_iterator_tag,
-                             vector_titer::iterator_category>::value, "category");
-  static_assert(std::is_same<int, vector_titer::value_type>::value, "value_type");
-  static_assert(std::is_same<int*, vector_titer::pointer>::value, "pointer");
-  static_assert(std::is_same<int&, vector_titer::reference>::value, "reference");
+  static_assert(std::is_same_v<std::random_access_iterator_tag, vector_titer::iterator_category>);
+  static_assert(std::is_same_v<int, vector_titer::value_type>);
+  static_assert(std::is_same_v<int*, vector_titer::pointer>);
+  static_assert(std::is_same_v<int&, vector_titer::reference>);
 
   using vector_ctiter = decltype(MakeTransformIterator(input.cbegin(), ref));
-  static_assert(std::is_same<std::random_access_iterator_tag,
-                             vector_ctiter::iterator_category>::value, "category");
-  // static_assert(std::is_same<int, vector_ctiter::value_type>::value, "value_type");
-  static_assert(std::is_same<const int*, vector_ctiter::pointer>::value, "pointer");
-  static_assert(std::is_same<const int&, vector_ctiter::reference>::value, "reference");
+  static_assert(std::is_same_v<std::random_access_iterator_tag, vector_ctiter::iterator_category>);
+  static_assert(std::is_same_v<int, vector_ctiter::value_type>);
+  static_assert(std::is_same_v<const int*, vector_ctiter::pointer>);
+  static_assert(std::is_same_v<const int&, vector_ctiter::reference>);
 
   using vector_rtiter = decltype(MakeTransformIterator(input.rbegin(), ref));
-  static_assert(std::is_same<std::random_access_iterator_tag,
-                             vector_rtiter::iterator_category>::value, "category");
-  static_assert(std::is_same<int, vector_rtiter::value_type>::value, "value_type");
-  static_assert(std::is_same<int*, vector_rtiter::pointer>::value, "pointer");
-  static_assert(std::is_same<int&, vector_rtiter::reference>::value, "reference");
+  static_assert(std::is_same_v<std::random_access_iterator_tag, vector_rtiter::iterator_category>);
+  static_assert(std::is_same_v<int, vector_rtiter::value_type>);
+  static_assert(std::is_same_v<int*, vector_rtiter::pointer>);
+  static_assert(std::is_same_v<int&, vector_rtiter::reference>);
 
   using vector_crtiter = decltype(MakeTransformIterator(input.crbegin(), ref));
-  static_assert(std::is_same<std::random_access_iterator_tag,
-                             vector_crtiter::iterator_category>::value, "category");
-  // static_assert(std::is_same<int, vector_crtiter::value_type>::value, "value_type");
-  static_assert(std::is_same<const int*, vector_crtiter::pointer>::value, "pointer");
-  static_assert(std::is_same<const int&, vector_crtiter::reference>::value, "reference");
+  static_assert(std::is_same_v<std::random_access_iterator_tag, vector_crtiter::iterator_category>);
+  static_assert(std::is_same_v<int, vector_crtiter::value_type>);
+  static_assert(std::is_same_v<const int*, vector_crtiter::pointer>);
+  static_assert(std::is_same_v<const int&, vector_crtiter::reference>);
 
   std::copy(MakeTransformIterator(input.begin(), ref),
             MakeTransformIterator(input.end(), ref),
@@ -500,11 +480,11 @@
   // Test iterator->const_iterator conversion and comparison.
   auto it = MakeTransformIterator(input.begin(), ref);
   decltype(MakeTransformIterator(input.cbegin(), ref)) cit = it;
-  static_assert(!std::is_same<decltype(it), decltype(cit)>::value, "Types must be different");
+  static_assert(!std::is_same_v<decltype(it), decltype(cit)>, "Types must be different");
   ASSERT_EQ(it, cit);
   auto rit = MakeTransformIterator(input.rbegin(), ref);
   decltype(MakeTransformIterator(input.crbegin(), ref)) crit(rit);
-  static_assert(!std::is_same<decltype(rit), decltype(crit)>::value, "Types must be different");
+  static_assert(!std::is_same_v<decltype(rit), decltype(crit)>, "Types must be different");
   ASSERT_EQ(rit, crit);
 
   // Test writing through the transform iterator.
diff --git a/libartbase/base/variant_map.h b/libartbase/base/variant_map.h
index 4244c9f..8d416e4 100644
--- a/libartbase/base/variant_map.h
+++ b/libartbase/base/variant_map.h
@@ -395,8 +395,7 @@
 
   template <typename TK, typename TValue, typename ... Rest>
   void InitializeParameters(const TK& key, const TValue& value, const Rest& ... rest) {
-    static_assert(
-        std::is_same<TK, TKey<TValue>>::value, "The 0th/2nd/4th/etc parameters must be a key");
+    static_assert(std::is_same_v<TK, TKey<TValue>>, "The 0th/2nd/4th/etc parameters must be a key");
 
     const TKey<TValue>& key_refined = key;
 
@@ -459,7 +458,7 @@
 
   template <typename TValue>
   static void StaticAssertKeyType() {
-    static_assert(std::is_base_of<VariantMapKey<TValue>, TKey<TValue>>::value,
+    static_assert(std::is_base_of_v<VariantMapKey<TValue>, TKey<TValue>>,
                   "The provided key type (TKey) must be a subclass of VariantMapKey");
   }
 
diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc
index b5cc6b7..f135805 100644
--- a/libprofile/profile/profile_compilation_info.cc
+++ b/libprofile/profile/profile_compilation_info.cc
@@ -2020,9 +2020,11 @@
     os << " [num_method_ids=" << dex_data->num_method_ids << "]";
     const DexFile* dex_file = nullptr;
     for (const DexFile* current : dex_files) {
-      if (GetBaseKeyViewFromAugmentedKey(dex_data->profile_key) == current->GetLocation() &&
-          dex_data->checksum == current->GetLocationChecksum()) {
+      if (GetBaseKeyViewFromAugmentedKey(dex_data->profile_key) ==
+          GetProfileDexFileBaseKeyView(current->GetLocation()) &&
+          ChecksumMatch(dex_data->checksum, current->GetLocationChecksum())) {
         dex_file = current;
+        break;
       }
     }
     os << "\n\thot methods: ";
diff --git a/runtime/class_table.cc b/runtime/class_table.cc
index acfbcb0..a4bc1fb 100644
--- a/runtime/class_table.cc
+++ b/runtime/class_table.cc
@@ -135,7 +135,7 @@
 ObjPtr<mirror::Class> ClassTable::Lookup(const char* descriptor, size_t hash) {
   DescriptorHashPair pair(descriptor, hash);
   ReaderMutexLock mu(Thread::Current(), lock_);
-  for (ClassSet& class_set : classes_) {
+  for (ClassSet& class_set : ReverseRange(classes_)) {
     auto it = class_set.FindWithHash(pair, hash);
     if (it != class_set.end()) {
       return it->Read();
@@ -153,19 +153,6 @@
   classes_.back().InsertWithHash(TableSlot(klass, hash), hash);
 }
 
-bool ClassTable::Remove(const char* descriptor) {
-  DescriptorHashPair pair(descriptor, ComputeModifiedUtf8Hash(descriptor));
-  WriterMutexLock mu(Thread::Current(), lock_);
-  for (ClassSet& class_set : classes_) {
-    auto it = class_set.find(pair);
-    if (it != class_set.end()) {
-      class_set.erase(it);
-      return true;
-    }
-  }
-  return false;
-}
-
 bool ClassTable::InsertStrongRoot(ObjPtr<mirror::Object> obj) {
   WriterMutexLock mu(Thread::Current(), lock_);
   DCHECK(obj != nullptr);
@@ -209,7 +196,12 @@
 
 void ClassTable::AddClassSet(ClassSet&& set) {
   WriterMutexLock mu(Thread::Current(), lock_);
-  classes_.insert(classes_.begin(), std::move(set));
+  // Insert before the last (unfrozen) table since we add new classes into the back.
+  // Keep the order of previous frozen tables unchanged, so that we can can remember
+  // the number of searched frozen tables and not search them again.
+  // TODO: Make use of this in `ClassLinker::FindClass()`.
+  DCHECK(!classes_.empty());
+  classes_.insert(classes_.end() - 1, std::move(set));
 }
 
 void ClassTable::ClearStrongRoots() {
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 4c5fc62..3377f14 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -220,11 +220,6 @@
       REQUIRES(!lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Returns true if the class was found and removed, false otherwise.
-  bool Remove(const char* descriptor)
-      REQUIRES(!lock_)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Return true if we inserted the strong root, false if it already exists.
   bool InsertStrongRoot(ObjPtr<mirror::Object> obj)
       REQUIRES(!lock_)
diff --git a/runtime/class_table_test.cc b/runtime/class_table_test.cc
index d9f53ed..7dbeba5 100644
--- a/runtime/class_table_test.cc
+++ b/runtime/class_table_test.cc
@@ -137,12 +137,7 @@
   });
   EXPECT_EQ(classes.size(), 1u);
 
-  // Test remove.
-  table.Remove(descriptor_x);
-  EXPECT_TRUE(table.LookupByDescriptor(h_X.Get()) == nullptr);
-
   // Test that reading a class set from memory works.
-  table.Insert(h_X.Get());
   ClassTable::ClassSet temp_set;
   table.Visit([&temp_set](ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
     temp_set.insert(ClassTable::TableSlot(klass));
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 14b8315..7f29655 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -337,13 +337,13 @@
                                                 size_t num_searched_frozen_tables) {
   Locks::intern_table_lock_->AssertHeld(Thread::Current());
   auto mid = tables_.begin() + num_searched_frozen_tables;
-  for (auto it = tables_.begin(); it != mid; ++it) {
-    DCHECK(it->set_.FindWithHash(GcRoot<mirror::String>(s), hash) == it->set_.end());
+  for (Table::InternalTable& table : MakeIterationRange(tables_.begin(), mid)) {
+    DCHECK(table.set_.FindWithHash(GcRoot<mirror::String>(s), hash) == table.set_.end());
   }
-  for (auto it = mid, end = tables_.end(); it != end; ++it) {
-    auto set_it = it->set_.FindWithHash(GcRoot<mirror::String>(s), hash);
-    if (set_it != it->set_.end()) {
-      return set_it->Read();
+  for (Table::InternalTable& table : ReverseRange(MakeIterationRange(mid, tables_.end()))) {
+    auto it = table.set_.FindWithHash(GcRoot<mirror::String>(s), hash);
+    if (it != table.set_.end()) {
+      return it->Read();
     }
   }
   return nullptr;
@@ -352,7 +352,7 @@
 FLATTEN
 ObjPtr<mirror::String> InternTable::Table::Find(const Utf8String& string, uint32_t hash) {
   Locks::intern_table_lock_->AssertHeld(Thread::Current());
-  for (InternalTable& table : tables_) {
+  for (InternalTable& table : ReverseRange(tables_)) {
     auto it = table.set_.FindWithHash(string, hash);
     if (it != table.set_.end()) {
       return it->Read();
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index 8463bc2..7a25b4b 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -899,6 +899,8 @@
       // going quite the way they expect.
       LOG(WARNING) << "JNI WARNING: DeleteLocalRef(" << obj << ") "
                    << "failed to find entry";
+      // Investigating b/228295454: Scudo ERROR: internal map failure (NO MEMORY).
+      soa.Self()->DumpJavaStack(LOG_STREAM(WARNING));
     }
   }
 
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 74c286f..ec0ac73 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3636,8 +3636,14 @@
     const RegType& referrer = GetDeclaringClass();
     if ((IsSdkVersionSetAndAtLeast(api_level_, SdkVersion::kP) || !referrer.IsUnresolvedTypes()) &&
         !referrer.CanAccess(*result)) {
-      Fail(VERIFY_ERROR_ACCESS_CLASS) << "(possibly) illegal class access: '"
-                                      << referrer << "' -> '" << *result << "'";
+      if (IsAotMode()) {
+        Fail(VERIFY_ERROR_ACCESS_CLASS);
+        VLOG(verifier)
+            << "(possibly) illegal class access: '" << referrer << "' -> '" << *result << "'";
+      } else {
+        Fail(VERIFY_ERROR_ACCESS_CLASS)
+            << "(possibly) illegal class access: '" << referrer << "' -> '" << *result << "'";
+      }
     }
   }
   return *result;
diff --git a/test/530-checker-loops-try-catch/expected-stderr.txt b/test/530-checker-loops-try-catch/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/530-checker-loops-try-catch/expected-stderr.txt
diff --git a/test/530-checker-loops-try-catch/expected-stdout.txt b/test/530-checker-loops-try-catch/expected-stdout.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/530-checker-loops-try-catch/expected-stdout.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/530-checker-loops-try-catch/info.txt b/test/530-checker-loops-try-catch/info.txt
new file mode 100644
index 0000000..5ff3c22
--- /dev/null
+++ b/test/530-checker-loops-try-catch/info.txt
@@ -0,0 +1 @@
+Test on loop optimizations, in particular with try catches.
diff --git a/test/530-checker-loops-try-catch/src/Main.java b/test/530-checker-loops-try-catch/src/Main.java
new file mode 100644
index 0000000..8d44b65
--- /dev/null
+++ b/test/530-checker-loops-try-catch/src/Main.java
@@ -0,0 +1,1558 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Test on loop optimizations, in particular with try catches.
+//
+public class Main {
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$geo1(int) loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$geo1(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Mul loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$geo1(int) loop_optimization (after)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue         loop:none
+  /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0          loop:none
+  /// CHECK-DAG: <<Int:i\d+>> IntConstant 1410065408 loop:none
+  /// CHECK-DAG: <<Mul:i\d+>> Mul [<<Par>>,<<Int>>]  loop:none
+  /// CHECK-DAG: <<Add:i\d+>> Add [<<Mul>>,<<Zer>>]  loop:none
+  /// CHECK-DAG:              Return [<<Add>>]       loop:none
+
+  /// CHECK-START: int Main.$noinline$geo1(int) loop_optimization (after)
+  /// CHECK-NOT: Phi
+  private static int $noinline$geo1(int a) {
+    for (int i = 0; i < 10; i++) {
+      a *= 10;
+    }
+
+    // Outer try catch does not block loop optimizations.
+    try {
+      if (doThrow) {
+        $noinline$unreachable();
+      }
+    } catch (Error e) {
+      System.out.println("Not expected");
+    }
+    return a;
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$geo1_Blocking(int) loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$geo1_Blocking(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Mul loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$geo1_Blocking(int) loop_optimization (after)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Mul loop:<<Loop>>
+  private static int $noinline$geo1_Blocking(int a) {
+    for (int i = 0; i < 10; i++) {
+      a *= 10;
+
+      // Try catch blocks optimizations.
+      try {
+        if (doThrow) {
+          $noinline$unreachable();
+        }
+      } catch (Error e) {
+        System.out.println("Not expected");
+      }
+    }
+    return a;
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$geo2(int) loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$geo2(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Shl loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$geo2(int) loop_optimization (after)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue        loop:none
+  /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0         loop:none
+  /// CHECK-DAG: <<Int:i\d+>> IntConstant 1024      loop:none
+  /// CHECK-DAG: <<Mul:i\d+>> Mul [<<Par>>,<<Int>>] loop:none
+  /// CHECK-DAG: <<Add:i\d+>> Add [<<Mul>>,<<Zer>>] loop:none
+  /// CHECK-DAG:              Return [<<Add>>]      loop:none
+
+  /// CHECK-START: int Main.$noinline$geo2(int) loop_optimization (after)
+  /// CHECK-NOT: Phi
+  private static int $noinline$geo2(int a) {
+    for (int i = 0; i < 10; i++) {
+      a <<= 1;
+    }
+
+    // Outer try catch does not block loop optimizations.
+    try {
+      if (doThrow) {
+        $noinline$unreachable();
+      }
+    } catch (Error e) {
+      System.out.println("Not expected");
+    }
+    return a;
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$geo2_Blocking(int) loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$geo2_Blocking(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Shl loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$geo2_Blocking(int) loop_optimization (after)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Shl loop:<<Loop>>
+  private static int $noinline$geo2_Blocking(int a) {
+    for (int i = 0; i < 10; i++) {
+      a <<= 1;
+
+      // Try catch blocks optimizations.
+      try {
+        if (doThrow) {
+          $noinline$unreachable();
+        }
+      } catch (Error e) {
+        System.out.println("Not expected");
+      }
+    }
+    return a;
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$geo3(int) loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$geo3(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Div loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$geo3(int) loop_optimization (after)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue        loop:none
+  /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0         loop:none
+  /// CHECK-DAG: <<Int:i\d+>> IntConstant 59049     loop:none
+  /// CHECK-DAG: <<Div:i\d+>> Div [<<Par>>,<<Int>>] loop:none
+  /// CHECK-DAG: <<Add:i\d+>> Add [<<Div>>,<<Zer>>] loop:none
+  /// CHECK-DAG:              Return [<<Add>>]      loop:none
+
+  /// CHECK-START: int Main.$noinline$geo3(int) loop_optimization (after)
+  /// CHECK-NOT: Phi
+  private static int $noinline$geo3(int a) {
+    for (int i = 0; i < 10; i++) {
+      a /= 3;
+    }
+
+    // Outer try catch does not block loop optimizations.
+    try {
+      if (doThrow) {
+        $noinline$unreachable();
+      }
+    } catch (Error e) {
+      System.out.println("Not expected");
+    }
+    return a;
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$geo3_Blocking(int) loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$geo3_Blocking(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Div loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$geo3_Blocking(int) loop_optimization (after)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Div loop:<<Loop>>
+  private static int $noinline$geo3_Blocking(int a) {
+    for (int i = 0; i < 10; i++) {
+      a /= 3;
+
+      // Try catch blocks optimizations.
+      try {
+        if (doThrow) {
+          $noinline$unreachable();
+        }
+      } catch (Error e) {
+        System.out.println("Not expected");
+      }
+    }
+    return a;
+  }
+
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$geo4(int) loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$geo4(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Rem loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$geo4(int) loop_optimization (after)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue        loop:none
+  /// CHECK-DAG: <<Int:i\d+>> IntConstant 7         loop:none
+  /// CHECK-DAG: <<Rem:i\d+>> Rem [<<Par>>,<<Int>>] loop:none
+  /// CHECK-DAG:              Return [<<Rem>>]      loop:none
+
+  /// CHECK-START: int Main.$noinline$geo4(int) loop_optimization (after)
+  /// CHECK-NOT: Phi
+  private static int $noinline$geo4(int a) {
+    for (int i = 0; i < 10; i++) {
+      a %= 7; // a wrap-around induction
+    }
+
+    // Outer try catch does not block loop optimizations.
+    try {
+      if (doThrow) {
+        $noinline$unreachable();
+      }
+    } catch (Error e) {
+      System.out.println("Not expected");
+    }
+    return a;
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$geo4_Blocking(int) loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$geo4_Blocking(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Rem loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$geo4_Blocking(int) loop_optimization (after)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Rem loop:<<Loop>>
+  private static int $noinline$geo4_Blocking(int a) {
+    for (int i = 0; i < 10; i++) {
+      a %= 7; // a wrap-around induction
+
+      // Try catch blocks optimizations.
+      try {
+        if (doThrow) {
+          $noinline$unreachable();
+        }
+      } catch (Error e) {
+        System.out.println("Not expected");
+      }
+    }
+    return a;
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$geo5() loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$geo5() loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Shr loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$geo5() loop_optimization (after)
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0          loop:none
+  /// CHECK-DAG: <<Int1:i\d+>> IntConstant 2147483647 loop:none
+  /// CHECK-DAG: <<Int2:i\d+>> IntConstant 1024       loop:none
+  /// CHECK-DAG: <<Div:i\d+>>  Div [<<Int1>>,<<Int2>>] loop:none
+  /// CHECK-DAG: <<Add:i\d+>>  Add [<<Div>>,<<Zero>>]  loop:none
+  /// CHECK-DAG:               Return [<<Add>>]        loop:none
+
+  /// CHECK-START: int Main.$noinline$geo5() loop_optimization (after)
+  /// CHECK-NOT: Phi
+  private static int $noinline$geo5() {
+    int a = 0x7fffffff;
+    for (int i = 0; i < 10; i++) {
+      a >>= 1;
+    }
+
+    // Outer try catch does not block loop optimizations.
+    try {
+      if (doThrow) {
+        $noinline$unreachable();
+      }
+    } catch (Error e) {
+      System.out.println("Not expected");
+    }
+    return a;
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$geo5_Blocking() loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$geo5_Blocking() loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Shr loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$geo5_Blocking() loop_optimization (after)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Shr loop:<<Loop>>
+  private static int $noinline$geo5_Blocking() {
+    int a = 0x7fffffff;
+    for (int i = 0; i < 10; i++) {
+      a >>= 1;
+
+      // Try catch blocks optimizations.
+      try {
+        if (doThrow) {
+          $noinline$unreachable();
+        }
+      } catch (Error e) {
+        System.out.println("Not expected");
+      }
+    }
+    return a;
+  }
+
+  // Tests taken from 530-checker-loops4
+  private static void $noinline$loops4Tests() {
+    int m = 1410065408;
+    for (int i = -100; i <= 100; i++) {
+      expectEquals(m * i, $noinline$geo1(i));
+      expectEquals(m * i, $noinline$geo1_Blocking(i));
+    }
+    for (int i = 1; i <= 1000000000; i *= 10) {
+      expectEquals(m * i, $noinline$geo1(i));
+      expectEquals(m * i, $noinline$geo1_Blocking(i));
+      expectEquals(-m * i, $noinline$geo1(-i));
+      expectEquals(-m * i, $noinline$geo1_Blocking(-i));
+    }
+
+    for (int i = -100; i <= 100; i++) {
+      expectEquals(i << 10, $noinline$geo2(i));
+      expectEquals(i << 10, $noinline$geo2_Blocking(i));
+    }
+    for (int i = 0; i < 22; i++) {
+      expectEquals(1 << (i + 10), $noinline$geo2(1 << i));
+      expectEquals(1 << (i + 10), $noinline$geo2_Blocking(1 << i));
+    }
+    expectEquals(0x80000400, $noinline$geo2(0x00200001));
+    expectEquals(0x80000400, $noinline$geo2_Blocking(0x00200001));
+    expectEquals(0x00000000, $noinline$geo2(0x00400000));
+    expectEquals(0x00000000, $noinline$geo2_Blocking(0x00400000));
+    expectEquals(0x00000400, $noinline$geo2(0x00400001));
+    expectEquals(0x00000400, $noinline$geo2_Blocking(0x00400001));
+
+    int d = 59049;
+    for (int i = -100; i <= 100; i++) {
+      expectEquals(0, $noinline$geo3(i));
+      expectEquals(0, $noinline$geo3_Blocking(i));
+    }
+    for (int i = 1; i <= 100; i++) {
+      expectEquals(i, $noinline$geo3(i * d));
+      expectEquals(i, $noinline$geo3_Blocking(i * d));
+      expectEquals(i, $noinline$geo3(i * d + 1));
+      expectEquals(i, $noinline$geo3_Blocking(i * d + 1));
+      expectEquals(-i, $noinline$geo3(-i * d));
+      expectEquals(-i, $noinline$geo3_Blocking(-i * d));
+      expectEquals(-i, $noinline$geo3(-i * d - 1));
+      expectEquals(-i, $noinline$geo3_Blocking(-i * d - 1));
+    }
+
+    for (int i = -100; i <= 100; i++) {
+      expectEquals(i % 7, $noinline$geo4(i));
+      expectEquals(i % 7, $noinline$geo4_Blocking(i));
+    }
+
+    expectEquals(0x1fffff, $noinline$geo5());
+    expectEquals(0x1fffff, $noinline$geo5_Blocking());
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$poly1() loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$poly1() loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$poly1() loop_optimization (after)
+  /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0         loop:none
+  /// CHECK-DAG: <<Int:i\d+>> IntConstant 55        loop:none
+  /// CHECK-DAG: <<Add:i\d+>> Add [<<Int>>,<<Zer>>] loop:none
+  /// CHECK-DAG:              Return [<<Add>>]      loop:none
+
+  /// CHECK-START: int Main.$noinline$poly1() instruction_simplifier$after_bce (after)
+  /// CHECK-DAG: <<Int:i\d+>>  IntConstant 55 loop:none
+  /// CHECK-DAG:               Return [<<Int>>]  loop:none
+
+  /// CHECK-START: int Main.$noinline$poly1() loop_optimization (after)
+  /// CHECK-NOT: Phi
+  private static int $noinline$poly1() {
+    int a = 0;
+    for (int i = 0; i <= 10; i++) {
+      a += i;
+    }
+
+    // Outer try catch does not block loop optimizations.
+    try {
+      if (doThrow) {
+        $noinline$unreachable();
+      }
+    } catch (Error e) {
+      System.out.println("Not expected");
+    }
+
+    return a;
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$poly1_Blocking() loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$poly1_Blocking() loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$poly1_Blocking() loop_optimization (after)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  private static int $noinline$poly1_Blocking() {
+    int a = 0;
+    for (int i = 0; i <= 10; i++) {
+      a += i;
+
+      // Try catch blocks optimizations.
+      try {
+        if (doThrow) {
+          $noinline$unreachable();
+        }
+      } catch (Error e) {
+        System.out.println("Not expected");
+      }
+    }
+
+    return a;
+  }
+
+  // Multiplication in linear induction has been optimized earlier,
+  // but that does not stop the induction variable recognition
+  // and loop optimizer.
+  //
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$poly2(int) loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$poly2(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Shl loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$poly2(int) loop_optimization (after)
+  /// CHECK-DAG: <<Par:i\d+>> ParameterValue        loop:none
+  /// CHECK-DAG: <<Int:i\d+>> IntConstant 185       loop:none
+  /// CHECK-DAG: <<Add:i\d+>> Add [<<Int>>,<<Par>>] loop:none
+  /// CHECK-DAG:              Return [<<Add>>]      loop:none
+
+  /// CHECK-START: int Main.$noinline$poly2(int) loop_optimization (after)
+  /// CHECK-NOT: Phi
+  private static int $noinline$poly2(int a) {
+    for (int i = 0; i < 10; i++) {
+      int k = 3 * i + 5;
+      a += k;
+    }
+
+    // Outer try catch does not block loop optimizations.
+    try {
+      if (doThrow) {
+        $noinline$unreachable();
+      }
+    } catch (Error e) {
+      System.out.println("Not expected");
+    }
+    return a;
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$poly2_Blocking(int) loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$poly2_Blocking(int) loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Shl loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$poly2_Blocking(int) loop_optimization (after)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Shl loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  private static int $noinline$poly2_Blocking(int a) {
+    for (int i = 0; i < 10; i++) {
+      int k = 3 * i + 5;
+      a += k;
+
+      // Try catch blocks optimizations.
+      try {
+        if (doThrow) {
+          $noinline$unreachable();
+        }
+      } catch (Error e) {
+        System.out.println("Not expected");
+      }
+    }
+
+    return a;
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$poly3() loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$poly3() loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$poly3() loop_optimization (after)
+  /// CHECK-DAG: <<Ini:i\d+>> IntConstant 12345       loop:none
+  /// CHECK-DAG: <<Int:i\d+>> IntConstant -2146736968 loop:none
+  /// CHECK-DAG: <<Add:i\d+>> Add [<<Int>>,<<Ini>>]   loop:none
+  /// CHECK-DAG:              Return [<<Add>>]        loop:none
+
+  /// CHECK-START: int Main.$noinline$poly3() instruction_simplifier$after_bce (after)
+  /// CHECK-DAG: <<Int:i\d+>>  IntConstant -2146724623 loop:none
+  /// CHECK-DAG:               Return [<<Int>>]        loop:none
+
+  /// CHECK-START: int Main.$noinline$poly3() loop_optimization (after)
+  /// CHECK-NOT: Phi
+  private static int $noinline$poly3() {
+    int a = 12345;
+    for (int i = 0; i <= 10; i++) {
+      a += (2147483646 * i + 67890);
+    }
+
+    // Outer try catch does not block loop optimizations.
+    try {
+      if (doThrow) {
+        $noinline$unreachable();
+      }
+    } catch (Error e) {
+      System.out.println("Not expected");
+    }
+    return a;
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: int Main.$noinline$poly3_Blocking() loop_optimization (before)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: int Main.$noinline$poly3_Blocking() loop_optimization (before)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+
+  /// CHECK-START: int Main.$noinline$poly3_Blocking() loop_optimization (after)
+  /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  /// CHECK-DAG: Add loop:<<Loop>>
+  private static int $noinline$poly3_Blocking() {
+    int a = 12345;
+    for (int i = 0; i <= 10; i++) {
+      a += (2147483646 * i + 67890);
+
+      // Try catch blocks optimizations.
+      try {
+        if (doThrow) {
+          $noinline$unreachable();
+        }
+      } catch (Error e) {
+        System.out.println("Not expected");
+      }
+    }
+    return a;
+  }
+
+  // Tests taken from 530-checker-loops5
+  private static void $noinline$loops5Tests() {
+    expectEquals(55, $noinline$poly1());
+    expectEquals(55, $noinline$poly1_Blocking());
+    expectEquals(185, $noinline$poly2(0));
+    expectEquals(185, $noinline$poly2_Blocking(0));
+    expectEquals(192, $noinline$poly2(7));
+    expectEquals(192, $noinline$poly2_Blocking(7));
+    expectEquals(-2146724623, $noinline$poly3());
+    expectEquals(-2146724623, $noinline$poly3_Blocking());
+  }
+
+  // Constants used for peel unroll tests.
+  private static final int LENGTH = 4 * 1024;
+  private static final int RESULT_POS = 4;
+
+  private static final void initIntArray(int[] a) {
+    for (int i = 0; i < a.length; i++) {
+      a[i] = i % 4;
+    }
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: void Main.$noinline$unrollingLoadStoreElimination(int[]) loop_optimization (after)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: void Main.$noinline$unrollingLoadStoreElimination(int[]) loop_optimization (before)
+  /// CHECK-DAG: <<Array:l\d+>>   ParameterValue                            loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>   IntConstant 4094                          loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>     Phi                                       loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Check:z\d+>>   GreaterThanOrEqual [<<Phi>>,<<Limit>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<If:v\d+>>      If [<<Check>>]                            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get0:i\d+>>    ArrayGet [<<Array>>,<<Phi>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<IndAdd:i\d+>>  Add [<<Phi>>,<<Const1>>]                  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:i\d+>>    ArrayGet [<<Array>>,<<IndAdd>>]           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Add:i\d+>>     Add [<<Get0>>,<<Get1>>]                   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  ArraySet [<<Array>>,<<Phi>>,<<Add>>]      loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START: void Main.$noinline$unrollingLoadStoreElimination(int[]) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingLoadStoreElimination(int[]) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+
+  /// CHECK-START: void Main.$noinline$unrollingLoadStoreElimination(int[]) loop_optimization (after)
+  /// CHECK-DAG: <<Array:l\d+>>   ParameterValue                            loop:none
+  /// CHECK-DAG: <<Const0:i\d+>>  IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>   IntConstant 4094                          loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>     Phi                                       loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Check:z\d+>>   GreaterThanOrEqual [<<Phi>>,<<Limit>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<If:v\d+>>      If [<<Check>>]                            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get0:i\d+>>    ArrayGet [<<Array>>,<<Phi>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<IndAdd:i\d+>>  Add [<<Phi>>,<<Const1>>]                  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:i\d+>>    ArrayGet [<<Array>>,<<IndAdd>>]           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Add:i\d+>>     Add [<<Get0>>,<<Get1>>]                   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  ArraySet [<<Array>>,<<Phi>>,<<Add>>]      loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-DAG: <<CheckA:z\d+>>  GreaterThanOrEqual [<<IndAdd>>,<<Limit>>] loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<IfA:v\d+>>     If [<<Const0>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get0A:i\d+>>   ArrayGet [<<Array>>,<<IndAdd>>]           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<IndAddA:i\d+>> Add [<<IndAdd>>,<<Const1>>]               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1A:i\d+>>   ArrayGet [<<Array>>,<<IndAddA>>]          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddA:i\d+>>    Add [<<Get0A>>,<<Get1A>>]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  ArraySet [<<Array>>,<<IndAdd>>,<<AddA>>]  loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START: void Main.$noinline$unrollingLoadStoreElimination(int[]) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingLoadStoreElimination(int[]) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+  private static final void $noinline$unrollingLoadStoreElimination(int[] a) {
+    for (int i = 0; i < LENGTH - 2; i++) {
+      a[i] += a[i + 1];
+    }
+
+    // Outer try catch does not block loop optimizations.
+    try {
+      if (doThrow) {
+        $noinline$unreachable();
+      }
+    } catch (Error e) {
+      System.out.println("Not expected");
+    }
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: void Main.$noinline$unrollingLoadStoreElimination_Blocking(int[]) loop_optimization (after)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: void Main.$noinline$unrollingLoadStoreElimination_Blocking(int[]) loop_optimization (before)
+  /// CHECK-DAG: <<Array:l\d+>>   ParameterValue                            loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>   IntConstant 4094                          loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>     Phi                                       loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Check:z\d+>>   GreaterThanOrEqual [<<Phi>>,<<Limit>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<If:v\d+>>      If [<<Check>>]                            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get0:i\d+>>    ArrayGet [<<Array>>,<<Phi>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<IndAdd:i\d+>>  Add [<<Phi>>,<<Const1>>]                  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:i\d+>>    ArrayGet [<<Array>>,<<IndAdd>>]           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Add:i\d+>>     Add [<<Get0>>,<<Get1>>]                   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  ArraySet [<<Array>>,<<Phi>>,<<Add>>]      loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START: void Main.$noinline$unrollingLoadStoreElimination_Blocking(int[]) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingLoadStoreElimination_Blocking(int[]) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+
+  /// CHECK-START: void Main.$noinline$unrollingLoadStoreElimination_Blocking(int[]) loop_optimization (after)
+  /// CHECK-DAG: <<Array:l\d+>>   ParameterValue                            loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>  IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>   IntConstant 4094                          loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>     Phi                                       loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Check:z\d+>>   GreaterThanOrEqual [<<Phi>>,<<Limit>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<If:v\d+>>      If [<<Check>>]                            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get0:i\d+>>    ArrayGet [<<Array>>,<<Phi>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<IndAdd:i\d+>>  Add [<<Phi>>,<<Const1>>]                  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get1:i\d+>>    ArrayGet [<<Array>>,<<IndAdd>>]           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Add:i\d+>>     Add [<<Get0>>,<<Get1>>]                   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                  ArraySet [<<Array>>,<<Phi>>,<<Add>>]      loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START: void Main.$noinline$unrollingLoadStoreElimination_Blocking(int[]) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingLoadStoreElimination_Blocking(int[]) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+  private static final void $noinline$unrollingLoadStoreElimination_Blocking(int[] a) {
+    for (int i = 0; i < LENGTH - 2; i++) {
+      a[i] += a[i + 1];
+
+      // Try catch blocks optimizations.
+      try {
+        if (doThrow) {
+          $noinline$unreachable();
+        }
+      } catch (Error e) {
+        System.out.println("Not expected");
+      }
+    }
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest(int[], int[], int) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>    IntConstant 128                           loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop1:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Phi3:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop3:B\d+>> outer_loop:<<Loop2>>
+  /// CHECK-DAG: <<Check:z\d+>>    GreaterThanOrEqual [<<Phi3>>,<<Limit>>]   loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   If [<<Check>>]                            loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop2>>
+
+  // Each one of the three `for` loops has an `if`. The try catch has the 4th `if`.
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest(int[], int[], int) loop_optimization (before)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>    IntConstant 128                           loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop1:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Phi3:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop3:B\d+>> outer_loop:<<Loop2>>
+  /// CHECK-DAG: <<Check:z\d+>>    GreaterThanOrEqual [<<Phi3>>,<<Limit>>]   loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   If [<<Check>>]                            loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG: <<AddI:i\d+>>     Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop2>>
+  //
+  /// CHECK-DAG:                   If [<<Const0>>]                           loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+
+  // Loop unrolling adds a 5th `if`. It is the one with `Const0` above.
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+  private static final void $noinline$unrollingInTheNest(int[] a, int[] b, int x) {
+    for (int k = 0; k < 16; k++) {
+      for (int j = 0; j < 16; j++) {
+        for (int i = 0; i < 128; i++) {
+          b[x]++;
+          a[i] = a[i] + 1;
+        }
+      }
+    }
+
+    // Outer try catch does not block loop optimizations.
+    try {
+      if (doThrow) {
+        $noinline$unreachable();
+      }
+    } catch (Error e) {
+      System.out.println("Not expected");
+    }
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_Blocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_Blocking(int[], int[], int) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>    IntConstant 128                           loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop1:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Phi3:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop3:B\d+>> outer_loop:<<Loop2>>
+  /// CHECK-DAG: <<Check:z\d+>>    GreaterThanOrEqual [<<Phi3>>,<<Limit>>]   loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   If [<<Check>>]                            loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  //
+  /// CHECK-DAG:                   Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop2>>
+
+  // Each one of the three `for` loops has an `if`. The try catch has the 4th `if`.
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_Blocking(int[], int[], int) loop_optimization (before)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_Blocking(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_Blocking(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_Blocking(int[], int[], int) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>    IntConstant 128                           loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop1:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Phi3:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop3:B\d+>> outer_loop:<<Loop2>>
+  /// CHECK-DAG: <<Check:z\d+>>    GreaterThanOrEqual [<<Phi3>>,<<Limit>>]   loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   If [<<Check>>]                            loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  //
+  /// CHECK-DAG:                   Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop2>>
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_Blocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_Blocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_Blocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+  private static final void $noinline$unrollingInTheNest_Blocking(int[] a, int[] b, int x) {
+    for (int k = 0; k < 16; k++) {
+      for (int j = 0; j < 16; j++) {
+        for (int i = 0; i < 128; i++) {
+          b[x]++;
+          a[i] = a[i] + 1;
+
+          // Try catch blocks optimizations.
+          try {
+            if (doThrow) {
+              $noinline$unreachable();
+            }
+          } catch (Error e) {
+            System.out.println("Not expected");
+          }
+        }
+      }
+    }
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_TryCatchNotBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_TryCatchNotBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>    IntConstant 128                           loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop1:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Phi3:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop3:B\d+>> outer_loop:<<Loop2>>
+  /// CHECK-DAG: <<Check:z\d+>>    GreaterThanOrEqual [<<Phi3>>,<<Limit>>]   loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   If [<<Check>>]                            loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  //
+  /// CHECK-DAG:                   Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop2>>
+
+  // Each one of the three `for` loops has an `if`. The try catch has the 4th `if`.
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_TryCatchNotBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_TryCatchNotBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_TryCatchNotBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_TryCatchNotBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>    IntConstant 128                           loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop1:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Phi3:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop3:B\d+>> outer_loop:<<Loop2>>
+  /// CHECK-DAG: <<Check:z\d+>>    GreaterThanOrEqual [<<Phi3>>,<<Limit>>]   loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   If [<<Check>>]                            loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG: <<AddI:i\d+>>     Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop2>>
+  //
+  /// CHECK-DAG:                   If [<<Const0>>]                           loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
+
+  // Loop unrolling adds a 5th `if`. It is the one with `Const0` above.
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_TryCatchNotBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_TryCatchNotBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingInTheNest_TryCatchNotBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+  private static final void $noinline$unrollingInTheNest_TryCatchNotBlocking(int[] a, int[] b, int x) {
+    for (int k = 0; k < 16; k++) {
+      for (int j = 0; j < 16; j++) {
+        for (int i = 0; i < 128; i++) {
+          b[x]++;
+          a[i] = a[i] + 1;
+        }
+        // Try catch does not block the optimization in the innermost loop.
+        try {
+          if (doThrow) {
+            $noinline$unreachable();
+          }
+        } catch (Error e) {
+          System.out.println("Not expected");
+        }
+      }
+    }
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>    IntConstant 128                           loop:none
+  /// CHECK-DAG: <<XThres:i\d+>>   IntConstant 100                           loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop1:B\d+>> outer_loop:none
+  //
+  /// CHECK-DAG: <<Phi2:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check2:z\d+>>   GreaterThanOrEqual [<<Phi2>>,<<Limit>>]   loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check2>>]                           loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<AddI2:i\d+>>    Add [<<Phi2>>,<<Const1>>]                 loop:<<Loop2>>      outer_loop:<<Loop1>>
+  //
+  /// CHECK-DAG: <<Phi3:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop3:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check3:z\d+>>   GreaterThanOrEqual [<<Phi3>>,<<Limit>>]   loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check3>>]                           loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<AddI3:i\d+>>    Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop1>>
+
+  // Each one of the three `for` loops has an `if`. Plus an `if` inside the outer `for`. The try catch has the 5th `if`.
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (before)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>    IntConstant 128                           loop:none
+  /// CHECK-DAG: <<XThres:i\d+>>   IntConstant 100                           loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop1:B\d+>> outer_loop:none
+  //
+  /// CHECK-DAG: <<Phi2:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check2:z\d+>>   GreaterThanOrEqual [<<Phi2>>,<<Limit>>]   loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check2>>]                           loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<AddI2:i\d+>>    Add [<<Phi2>>,<<Const1>>]                 loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Const0>>]                           loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   Add [<<AddI2>>,<<Const1>>]                loop:<<Loop2>>      outer_loop:<<Loop1>>
+  //
+  /// CHECK-DAG: <<Phi3:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop3:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check3:z\d+>>   GreaterThanOrEqual [<<Phi3>>,<<Limit>>]   loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check3>>]                           loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<AddI3:i\d+>>    Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Const0>>]                           loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   Add [<<AddI3>>,<<Const1>>]                loop:<<Loop3>>      outer_loop:<<Loop1>>
+
+  // LoopOptimization adds two `if`s. One for each loop unrolling.
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+  private static final void $noinline$unrollingTwoLoopsInTheNest(int[] a, int[] b, int x) {
+    for (int k = 0; k < 128; k++) {
+      if (x > 100) {
+        for (int j = 0; j < 128; j++) {
+          a[x]++;
+        }
+      } else {
+        for (int i = 0; i < 128; i++) {
+          b[x]++;
+        }
+      }
+    }
+
+    // Outer try catch does not block loop optimizations.
+    try {
+      if (doThrow) {
+        $noinline$unreachable();
+      }
+    } catch (Error e) {
+      System.out.println("Not expected");
+    }
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OneBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OneBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>    IntConstant 128                           loop:none
+  /// CHECK-DAG: <<XThres:i\d+>>   IntConstant 100                           loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop1:B\d+>> outer_loop:none
+  //
+  /// CHECK-DAG: <<Phi2:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check2:z\d+>>   GreaterThanOrEqual [<<Phi2>>,<<Limit>>]   loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check2>>]                           loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  //
+  /// CHECK-DAG: <<AddI2:i\d+>>    Add [<<Phi2>>,<<Const1>>]                 loop:<<Loop2>>      outer_loop:<<Loop1>>
+  //
+  /// CHECK-DAG: <<Phi3:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop3:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check3:z\d+>>   GreaterThanOrEqual [<<Phi3>>,<<Limit>>]   loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check3>>]                           loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<AddI3:i\d+>>    Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop1>>
+
+  // Each one of the three `for` loops has an `if`. Plus an `if` inside the outer `for`. The try catch has the 5th `if`.
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OneBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OneBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OneBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OneBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>    IntConstant 128                           loop:none
+  /// CHECK-DAG: <<XThres:i\d+>>   IntConstant 100                           loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop1:B\d+>> outer_loop:none
+  //
+  /// CHECK-DAG: <<Phi2:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check2:z\d+>>   GreaterThanOrEqual [<<Phi2>>,<<Limit>>]   loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check2>>]                           loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  //
+  // Unrelated to the optimization itself, the try catch has an if.
+  /// CHECK-DAG: <<Get:z\d+>>      StaticFieldGet field_name:Main.doThrow
+  /// CHECK-DAG:                   If [<<Get>>]
+  //
+  /// CHECK-DAG: <<AddI2:i\d+>>    Add [<<Phi2>>,<<Const1>>]                 loop:<<Loop2>>      outer_loop:<<Loop1>>
+  //
+  /// CHECK-DAG: <<Phi3:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop3:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check3:z\d+>>   GreaterThanOrEqual [<<Phi3>>,<<Limit>>]   loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check3>>]                           loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<AddI3:i\d+>>    Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Const0>>]                           loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   Add [<<AddI3>>,<<Const1>>]                loop:<<Loop3>>      outer_loop:<<Loop1>>
+
+  // LoopOptimization adds two `if`s. One for each loop unrolling.
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OneBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OneBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OneBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+  private static final void $noinline$unrollingTwoLoopsInTheNest_OneBlocking(int[] a, int[] b, int x) {
+    for (int k = 0; k < 128; k++) {
+      if (x > 100) {
+        for (int j = 0; j < 128; j++) {
+          a[x]++;
+          // Try catch blocks optimizations.
+          try {
+            if (doThrow) {
+              $noinline$unreachable();
+            }
+          } catch (Error e) {
+            System.out.println("Not expected");
+          }
+        }
+      } else {
+        for (int i = 0; i < 128; i++) {
+          b[x]++;
+        }
+      }
+    }
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OtherBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OtherBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>    IntConstant 128                           loop:none
+  /// CHECK-DAG: <<XThres:i\d+>>   IntConstant 100                           loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop1:B\d+>> outer_loop:none
+  //
+  /// CHECK-DAG: <<Phi2:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check2:z\d+>>   GreaterThanOrEqual [<<Phi2>>,<<Limit>>]   loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check2>>]                           loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<AddI2:i\d+>>    Add [<<Phi2>>,<<Const1>>]                 loop:<<Loop2>>      outer_loop:<<Loop1>>
+  //
+  /// CHECK-DAG: <<Phi3:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop3:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check3:z\d+>>   GreaterThanOrEqual [<<Phi3>>,<<Limit>>]   loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check3>>]                           loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  //
+  /// CHECK-DAG: <<AddI3:i\d+>>    Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop1>>
+
+  // Each one of the three `for` loops has an `if`. Plus an `if` inside the outer `for`. The try catch has the 5th `if`.
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OtherBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OtherBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OtherBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OtherBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>    IntConstant 128                           loop:none
+  /// CHECK-DAG: <<XThres:i\d+>>   IntConstant 100                           loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop1:B\d+>> outer_loop:none
+  //
+  /// CHECK-DAG: <<Phi2:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check2:z\d+>>   GreaterThanOrEqual [<<Phi2>>,<<Limit>>]   loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check2>>]                           loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<AddI2:i\d+>>    Add [<<Phi2>>,<<Const1>>]                 loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Const0>>]                           loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   Add [<<AddI2>>,<<Const1>>]                loop:<<Loop2>>      outer_loop:<<Loop1>>
+  //
+  /// CHECK-DAG: <<Phi3:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop3:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check3:z\d+>>   GreaterThanOrEqual [<<Phi3>>,<<Limit>>]   loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check3>>]                           loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  //
+  /// CHECK-DAG: <<AddI3:i\d+>>    Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop1>>
+
+  // LoopOptimization adds two `if`s. One for each loop unrolling.
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OtherBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OtherBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_OtherBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+  private static final void $noinline$unrollingTwoLoopsInTheNest_OtherBlocking(int[] a, int[] b, int x) {
+    for (int k = 0; k < 128; k++) {
+      if (x > 100) {
+        for (int j = 0; j < 128; j++) {
+          a[x]++;
+        }
+      } else {
+        for (int i = 0; i < 128; i++) {
+          b[x]++;
+          // Try catch blocks optimizations.
+          try {
+            if (doThrow) {
+              $noinline$unreachable();
+            }
+          } catch (Error e) {
+            System.out.println("Not expected");
+          }
+        }
+      }
+    }
+  }
+
+  // Consistency check to see we haven't eliminated the try/catch.
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_BothBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:     TryBoundary
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_BothBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>    IntConstant 128                           loop:none
+  /// CHECK-DAG: <<XThres:i\d+>>   IntConstant 100                           loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop1:B\d+>> outer_loop:none
+  //
+  /// CHECK-DAG: <<Phi2:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check2:z\d+>>   GreaterThanOrEqual [<<Phi2>>,<<Limit>>]   loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check2>>]                           loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  //
+  /// CHECK-DAG: <<AddI2:i\d+>>    Add [<<Phi2>>,<<Const1>>]                 loop:<<Loop2>>      outer_loop:<<Loop1>>
+  //
+  /// CHECK-DAG: <<Phi3:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop3:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check3:z\d+>>   GreaterThanOrEqual [<<Phi3>>,<<Limit>>]   loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check3>>]                           loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  //
+  /// CHECK-DAG: <<AddI3:i\d+>>    Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop1>>
+
+  // Each one of the three `for` loops has an `if`. Plus an `if` inside the outer `for`. The try catchs have the 5th and 6th `if`.
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_BothBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_BothBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_BothBlocking(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_BothBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
+  /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
+  /// CHECK-DAG: <<Limit:i\d+>>    IntConstant 128                           loop:none
+  /// CHECK-DAG: <<XThres:i\d+>>   IntConstant 100                           loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop1:B\d+>> outer_loop:none
+  //
+  /// CHECK-DAG: <<Phi2:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check2:z\d+>>   GreaterThanOrEqual [<<Phi2>>,<<Limit>>]   loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check2>>]                           loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop2>>      outer_loop:<<Loop1>>
+  //
+  // Unrelated to the optimization itself, the try catch has an if.
+  /// CHECK-DAG: <<Get1:z\d+>>     StaticFieldGet field_name:Main.doThrow
+  /// CHECK-DAG:                   If [<<Get1>>]
+  //
+  /// CHECK-DAG: <<AddI2:i\d+>>    Add [<<Phi2>>,<<Const1>>]                 loop:<<Loop2>>      outer_loop:<<Loop1>>
+  //
+  /// CHECK-DAG: <<Phi3:i\d+>>     Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop3:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Check3:z\d+>>   GreaterThanOrEqual [<<Phi3>>,<<Limit>>]   loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   If [<<Check3>>]                           loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
+  //
+  /// CHECK-DAG: <<AddI3:i\d+>>    Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop1>>
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_BothBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_BothBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.$noinline$unrollingTwoLoopsInTheNest_BothBlocking(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+  private static final void $noinline$unrollingTwoLoopsInTheNest_BothBlocking(int[] a, int[] b, int x) {
+    for (int k = 0; k < 128; k++) {
+      if (x > 100) {
+        for (int j = 0; j < 128; j++) {
+          a[x]++;
+          // Try catch blocks optimizations.
+          try {
+            if (doThrow) {
+              $noinline$unreachable();
+            }
+          } catch (Error e) {
+            System.out.println("Not expected");
+          }
+        }
+      } else {
+        for (int i = 0; i < 128; i++) {
+          b[x]++;
+          // Try catch blocks optimizations.
+          try {
+            if (doThrow) {
+              $noinline$unreachable();
+            }
+          } catch (Error e) {
+            System.out.println("Not expected");
+          }
+        }
+      }
+    }
+  }
+
+  // Tests taken from 530-checker-peel-unroll
+  private static void $noinline$peelUnrollTests() {
+    int[] a = new int[LENGTH];
+    int[] b = new int[LENGTH];
+    initIntArray(a);
+    initIntArray(b);
+
+    $noinline$unrollingLoadStoreElimination(a);
+    $noinline$unrollingLoadStoreElimination_Blocking(a);
+    $noinline$unrollingInTheNest(a, b, RESULT_POS);
+    $noinline$unrollingInTheNest_Blocking(a, b, RESULT_POS);
+    $noinline$unrollingInTheNest_TryCatchNotBlocking(a, b, RESULT_POS);
+    $noinline$unrollingTwoLoopsInTheNest(a, b, RESULT_POS);
+    $noinline$unrollingTwoLoopsInTheNest_OneBlocking(a, b, RESULT_POS);
+    $noinline$unrollingTwoLoopsInTheNest_OtherBlocking(a, b, RESULT_POS);
+    $noinline$unrollingTwoLoopsInTheNest_BothBlocking(a, b, RESULT_POS);
+  }
+
+  public static void main(String[] args) {
+    // Use existing tests to show that the difference between having a try catch inside or outside
+    // the loop.
+    $noinline$loops4Tests();
+    $noinline$loops5Tests();
+    $noinline$peelUnrollTests();
+
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  private static void $noinline$unreachable() {
+    throw new Error("Unreachable");
+  }
+
+  private static boolean doThrow = false;
+}
diff --git a/test/530-checker-peel-unroll/src/Main.java b/test/530-checker-peel-unroll/src/Main.java
index aee32b7..f909af3 100644
--- a/test/530-checker-peel-unroll/src/Main.java
+++ b/test/530-checker-peel-unroll/src/Main.java
@@ -78,9 +78,15 @@
   /// CHECK-DAG: <<Get1:i\d+>>    ArrayGet [<<Array>>,<<IndAdd>>]           loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Add:i\d+>>     Add [<<Get0>>,<<Get1>>]                   loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                  ArraySet [<<Array>>,<<Phi>>,<<Add>>]      loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                  ArrayGet                                  loop:<<Loop>>      outer_loop:none
-  /// CHECK-NOT:                  ArraySet                                  loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START: void Main.unrollingLoadStoreElimination(int[]) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.unrollingLoadStoreElimination(int[]) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
 
   /// CHECK-START: void Main.unrollingLoadStoreElimination(int[]) loop_optimization (after)
   /// CHECK-DAG: <<Array:l\d+>>   ParameterValue                            loop:none
@@ -103,9 +109,18 @@
   /// CHECK-DAG: <<Get1A:i\d+>>   ArrayGet [<<Array>>,<<IndAddA>>]          loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<AddA:i\d+>>    Add [<<Get0A>>,<<Get1A>>]                 loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                  ArraySet [<<Array>>,<<IndAdd>>,<<AddA>>]  loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                  ArrayGet                                  loop:<<Loop>>      outer_loop:none
-  /// CHECK-NOT:                  ArraySet                                  loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START: void Main.unrollingLoadStoreElimination(int[]) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.unrollingLoadStoreElimination(int[]) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
   private static final void unrollingLoadStoreElimination(int[] a) {
     for (int i = 0; i < LENGTH - 2; i++) {
       a[i] += a[i + 1];
@@ -124,9 +139,14 @@
   /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   ArraySet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   Add [<<PhiI>>,<<Const1>>]                 loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                   ArrayGet                                  loop:<<Loop>>      outer_loop:none
-  /// CHECK-NOT:                   ArraySet                                  loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START: void Main.unrollingSwitch(int[]) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.unrollingSwitch(int[]) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
 
   /// CHECK-START: void Main.unrollingSwitch(int[]) loop_optimization (after)
   /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
@@ -144,9 +164,16 @@
   /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   ArraySet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   Add [<<AddI>>,<<Const1>>]                 loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                   ArrayGet                                  loop:<<Loop>>      outer_loop:none
-  /// CHECK-NOT:                   ArraySet                                  loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START: void Main.unrollingSwitch(int[]) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.unrollingSwitch(int[]) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
   private static final void unrollingSwitch(int[] a) {
     for (int i = 0; i < LENGTH; i++) {
       switch (i % 3) {
@@ -173,9 +200,16 @@
   /// CHECK-DAG:                   ArraySet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   ArraySet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   Add [<<PhiI>>,<<Const1>>]                 loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                   ArrayGet                                  loop:<<Loop>>      outer_loop:none
-  /// CHECK-NOT:                   ArraySet                                  loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START: void Main.unrollingSwapElements(int[]) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.unrollingSwapElements(int[]) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
 
   /// CHECK-START: void Main.unrollingSwapElements(int[]) loop_optimization (after)
   /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
@@ -197,9 +231,20 @@
   /// CHECK-DAG:                   ArraySet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   ArraySet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   Add [<<AddI>>,<<Const1>>]                 loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                   ArrayGet                                  loop:<<Loop>>      outer_loop:none
-  /// CHECK-NOT:                   ArraySet                                  loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START: void Main.unrollingSwapElements(int[]) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.unrollingSwapElements(int[]) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
   private static final void unrollingSwapElements(int[] array) {
     for (int i = 0; i < LENGTH - 2; i++) {
       if (array[i] > array[i + 1]) {
@@ -225,9 +270,21 @@
   /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   ArraySet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   Add [<<PhiI>>,<<Const1>>]                 loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                   ArrayGet                                  loop:<<Loop>>      outer_loop:none
-  /// CHECK-NOT:                   ArraySet                                  loop:<<Loop>>      outer_loop:none
+
+  // We have two other `ArrayGet` before the `If` that appears in the CHECK-DAG above.
+  /// CHECK-START: void Main.unrollingRInnerproduct(double[][], double[][], double[][], int, int) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.unrollingRInnerproduct(double[][], double[][], double[][], int, int) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
 
   /// CHECK-START: void Main.unrollingRInnerproduct(double[][], double[][], double[][], int, int) loop_optimization (after)
   /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
@@ -251,9 +308,25 @@
   /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   ArraySet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   Add [<<AddI>>,<<Const1>>]                 loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                   ArrayGet                                  loop:<<Loop>>      outer_loop:none
-  /// CHECK-NOT:                   ArraySet                                  loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START: void Main.unrollingRInnerproduct(double[][], double[][], double[][], int, int) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.unrollingRInnerproduct(double[][], double[][], double[][], int, int) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
   private static final void unrollingRInnerproduct(double[][] result,
                                                    double[][] a,
                                                    double[][] b,
@@ -284,11 +357,24 @@
   /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
   /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
   /// CHECK-DAG:                   Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop2>>
-  //
-  /// CHECK-NOT:                   ArrayGet
-  /// CHECK-NOT:                   ArraySet
+
+  // Each one of the three `for` loops has an `if`.
+  /// CHECK-START: void Main.unrollingInTheNest(int[], int[], int) loop_optimization (before)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
   /// CHECK-NOT:                   If
 
+  /// CHECK-START: void Main.unrollingInTheNest(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.unrollingInTheNest(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+
   /// CHECK-START: void Main.unrollingInTheNest(int[], int[], int) loop_optimization (after)
   /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
   /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
@@ -309,10 +395,28 @@
   /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
   /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
   /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop2>>
-  //
-  /// CHECK-NOT:                   ArrayGet
-  /// CHECK-NOT:                   ArraySet
+
+  // Loop unrolling adds a 4th `if`. It is the one with `Const0` above.
+  /// CHECK-START: void Main.unrollingInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
   /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.unrollingInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.unrollingInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
   private static final void unrollingInTheNest(int[] a, int[] b, int x) {
     for (int k = 0; k < 16; k++) {
       for (int j = 0; j < 16; j++) {
@@ -349,11 +453,25 @@
   /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
   /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
   /// CHECK-DAG: <<AddI3:i\d+>>    Add [<<Phi3>>,<<Const1>>]                 loop:<<Loop3>>      outer_loop:<<Loop1>>
-  //
-  /// CHECK-NOT:                   ArrayGet
-  /// CHECK-NOT:                   ArraySet
+
+  // Each one of the three `for` loops has an `if`. Plus an `if` inside the outer `for`.
+  /// CHECK-START: void Main.unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (before)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
   /// CHECK-NOT:                   If
 
+  /// CHECK-START: void Main.unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
+
   /// CHECK-START: void Main.unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (after)
   /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
   /// CHECK-DAG: <<Const1:i\d+>>   IntConstant 1                             loop:none
@@ -382,10 +500,30 @@
   /// CHECK-DAG:                   ArrayGet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
   /// CHECK-DAG:                   ArraySet                                  loop:<<Loop3>>      outer_loop:<<Loop1>>
   /// CHECK-DAG:                   Add [<<AddI3>>,<<Const1>>]                loop:<<Loop3>>      outer_loop:<<Loop1>>
-  //
-  /// CHECK-NOT:                   ArrayGet
-  /// CHECK-NOT:                   ArraySet
+
+  // LoopOptimization adds two `if`s. One for each loop unrolling.
+  /// CHECK-START: void Main.unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
+  /// CHECK:                       If
   /// CHECK-NOT:                   If
+
+  /// CHECK-START: void Main.unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.unrollingTwoLoopsInTheNest(int[], int[], int) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
   private static final void unrollingTwoLoopsInTheNest(int[] a, int[] b, int x) {
     for (int k = 0; k < 128; k++) {
       if (x > 100) {
@@ -423,7 +561,14 @@
   /// CHECK-DAG: <<ZCheck:i\d+>>  DivZeroCheck [<<STAdd>>] env:[[<<PhiS>>,<<PhiT>>,<<STAdd>>,<<Const1>>,_,<<Array>>]] loop:none
   /// CHECK-DAG: <<Div:i\d+>>     Div [<<Const1>>,<<ZCheck>>]               loop:none
   /// CHECK-DAG:                  Return [<<Div>>]                          loop:none
+
+  /// CHECK-START: int Main.unrollingSimpleLiveOuts(int[]) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
   /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: int Main.unrollingSimpleLiveOuts(int[]) loop_optimization (before)
+  /// CHECK:                      ArraySet
   /// CHECK-NOT:                  ArraySet
 
   /// CHECK-START: int Main.unrollingSimpleLiveOuts(int[]) loop_optimization (after)
@@ -461,8 +606,17 @@
   /// CHECK-DAG: <<ZCheck:i\d+>>  DivZeroCheck [<<STAdd>>] env:[[<<RetPhiS>>,<<RetPhiT>>,<<STAdd>>,<<Const1>>,_,<<Array>>]] loop:none
   /// CHECK-DAG: <<Div:i\d+>>     Div [<<Const1>>,<<ZCheck>>]               loop:none
   /// CHECK-DAG:                  Return [<<Div>>]                          loop:none
-  //
+
+  /// CHECK-START: int Main.unrollingSimpleLiveOuts(int[]) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
   /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: int Main.unrollingSimpleLiveOuts(int[]) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
   /// CHECK-NOT:                  ArraySet
   private static final int unrollingSimpleLiveOuts(int[] a) {
     int s = 1;
@@ -502,8 +656,14 @@
   /// CHECK-DAG:                  ArraySet [<<Array>>,<<PhiI>>,<<AddArr>>]  loop:<<Loop1>>      outer_loop:<<Loop0>>
   //
   /// CHECK-DAG:                  Add [<<OutPhiJ>>,<<Const1>>]              loop:<<Loop0>>      outer_loop:none
-  //
+
+  /// CHECK-START: int Main.unrollingLiveOutsNested(int[]) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
   /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: int Main.unrollingLiveOutsNested(int[]) loop_optimization (before)
+  /// CHECK:                      ArraySet
   /// CHECK-NOT:                  ArraySet
 
   /// CHECK-START: int Main.unrollingLiveOutsNested(int[]) loop_optimization (after)
@@ -545,8 +705,17 @@
   //
   /// CHECK-DAG: <<RetAdd:i\d+>>  Add [<<OutPhiS>>,<<OutPhiT>>]             loop:none
   /// CHECK-DAG:                  Return [<<RetAdd>>]                       loop:none
-  //
+
+  /// CHECK-START: int Main.unrollingLiveOutsNested(int[]) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
   /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: int Main.unrollingLiveOutsNested(int[]) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
   /// CHECK-NOT:                  ArraySet
   private static final int unrollingLiveOutsNested(int[] a) {
     int s = 1;
@@ -566,16 +735,21 @@
   /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
   /// CHECK-DAG:                   Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG:                   InstanceOf                                loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                   InstanceOf
+
+  /// CHECK-START: void Main.unrollingInstanceOf(int[], java.lang.Object[]) loop_optimization (before)
+  /// CHECK:                      InstanceOf
+  /// CHECK-NOT:                  InstanceOf
 
   /// CHECK-START: void Main.unrollingInstanceOf(int[], java.lang.Object[]) loop_optimization (after)
   /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
   /// CHECK-DAG:                   Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG:                   InstanceOf                                loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   InstanceOf                                loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                   InstanceOf
+
+  /// CHECK-START: void Main.unrollingInstanceOf(int[], java.lang.Object[]) loop_optimization (after)
+  /// CHECK:                      InstanceOf
+  /// CHECK:                      InstanceOf
+  /// CHECK-NOT:                  InstanceOf
   public void unrollingInstanceOf(int[] a, Object[] obj_array) {
     for (int i = 0; i < LENGTH_B; i++) {
       if (obj_array[i] instanceof Integer) {
@@ -588,16 +762,21 @@
   /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
   /// CHECK-DAG:                   Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG:                   DivZeroCheck                              loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                   DivZeroCheck
+
+  /// CHECK-START: void Main.unrollingDivZeroCheck(int[], int) loop_optimization (before)
+  /// CHECK:                      DivZeroCheck
+  /// CHECK-NOT:                  DivZeroCheck
 
   /// CHECK-START: void Main.unrollingDivZeroCheck(int[], int) loop_optimization (after)
   /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
   /// CHECK-DAG:                   Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG:                   DivZeroCheck                              loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   DivZeroCheck                              loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                   DivZeroCheck
+
+  /// CHECK-START: void Main.unrollingDivZeroCheck(int[], int) loop_optimization (after)
+  /// CHECK:                      DivZeroCheck
+  /// CHECK:                      DivZeroCheck
+  /// CHECK-NOT:                  DivZeroCheck
   public void unrollingDivZeroCheck(int[] a, int r) {
     for (int i = 0; i < LENGTH_B; i++) {
       a[i] += a[i] / r;
@@ -608,16 +787,21 @@
   /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
   /// CHECK-DAG:                   Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG:                   TypeConversion                            loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                   TypeConversion
+
+  /// CHECK-START: void Main.unrollingTypeConversion(int[], double[]) loop_optimization (before)
+  /// CHECK:                      TypeConversion
+  /// CHECK-NOT:                  TypeConversion
 
   /// CHECK-START: void Main.unrollingTypeConversion(int[], double[]) loop_optimization (after)
   /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
   /// CHECK-DAG:                   Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG:                   TypeConversion                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   TypeConversion                            loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                   TypeConversion
+
+  /// CHECK-START: void Main.unrollingTypeConversion(int[], double[]) loop_optimization (after)
+  /// CHECK:                      TypeConversion
+  /// CHECK:                      TypeConversion
+  /// CHECK-NOT:                  TypeConversion
   public void unrollingTypeConversion(int[] a, double[] b) {
     for (int i = 0; i < LENGTH_B; i++) {
       a[i] = (int) b[i];
@@ -634,16 +818,21 @@
   /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
   /// CHECK-DAG:                   Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG:                   CheckCast                                 loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                   CheckCast
+
+  /// CHECK-START: void Main.unrollingCheckCast(int[], java.lang.Object) loop_optimization (before)
+  /// CHECK:                      CheckCast
+  /// CHECK-NOT:                  CheckCast
 
   /// CHECK-START: void Main.unrollingCheckCast(int[], java.lang.Object) loop_optimization (after)
   /// CHECK-DAG: <<Const0:i\d+>>   IntConstant 0                             loop:none
   /// CHECK-DAG:                   Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG:                   CheckCast                                 loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                   CheckCast                                 loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                   CheckCast
+
+  /// CHECK-START: void Main.unrollingCheckCast(int[], java.lang.Object) loop_optimization (after)
+  /// CHECK:                      CheckCast
+  /// CHECK:                      CheckCast
+  /// CHECK-NOT:                  CheckCast
   public void unrollingCheckCast(int[] a, Object o) {
     for (int i = 0; i < LENGTH_B; i++) {
       if (((SubMain)o) == o) {
@@ -664,10 +853,22 @@
   /// CHECK-DAG: <<Get1:i\d+>>    ArrayGet [<<Array>>,<<IndAdd>>]           loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Add:i\d+>>     Add [<<Get0>>,<<Get1>>]                   loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                  ArraySet [<<Array>>,<<Phi>>,<<Add>>]      loop:<<Loop>>      outer_loop:none
-  //
+
+  /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (before)
+  /// CHECK:                      Phi
   /// CHECK-NOT:                  Phi
+
+  /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (before)
+  /// CHECK:                      If
   /// CHECK-NOT:                  If
+
+  /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
   /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (before)
+  /// CHECK:                      ArraySet
   /// CHECK-NOT:                  ArraySet
 
   /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (after)
@@ -676,10 +877,22 @@
   /// CHECK-DAG:                  ArrayGet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                  ArrayGet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                  ArraySet                                  loop:<<Loop>>      outer_loop:none
-  //
+
+  /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (after)
+  /// CHECK:                      Phi
   /// CHECK-NOT:                  Phi
+
+  /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (after)
+  /// CHECK:                      If
   /// CHECK-NOT:                  If
+
+  /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
   /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (after)
+  /// CHECK:                      ArraySet
   /// CHECK-NOT:                  ArraySet
   private static final void noUnrollingOddTripCount(int[] a) {
     for (int i = 0; i < LENGTH - 1; i++) {
@@ -699,9 +912,24 @@
   /// CHECK-DAG: <<Get1:i\d+>>    ArrayGet [<<Array>>,<<IndAdd>>]           loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<Add:i\d+>>     Add [<<Get0>>,<<Get1>>]                   loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                  ArraySet [<<Array>>,<<Phi>>,<<Add>>]      loop:<<Loop>>      outer_loop:none
-  //
+
+  /// CHECK-START: void Main.noUnrollingNotKnownTripCount(int[], int) loop_optimization (before)
+  /// CHECK:                      Phi
   /// CHECK-NOT:                  Phi
+
+  // One `if` for the `for` loop, and another one for a deoptimize.
+  /// CHECK-START: void Main.noUnrollingNotKnownTripCount(int[], int) loop_optimization (before)
+  /// CHECK:                      If
+  /// CHECK:                      If
+  /// CHECK-NOT:                  If
+
+  /// CHECK-START: void Main.noUnrollingNotKnownTripCount(int[], int) loop_optimization (before)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
   /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.noUnrollingNotKnownTripCount(int[], int) loop_optimization (before)
+  /// CHECK:                      ArraySet
   /// CHECK-NOT:                  ArraySet
 
   /// CHECK-START: void Main.noUnrollingNotKnownTripCount(int[], int) loop_optimization (after)
@@ -710,9 +938,23 @@
   /// CHECK-DAG:                  ArrayGet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                  ArrayGet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                  ArraySet                                  loop:<<Loop>>      outer_loop:none
-  //
+
+  /// CHECK-START: void Main.noUnrollingNotKnownTripCount(int[], int) loop_optimization (after)
+  /// CHECK:                      Phi
   /// CHECK-NOT:                  Phi
+
+  /// CHECK-START: void Main.noUnrollingNotKnownTripCount(int[], int) loop_optimization (after)
+  /// CHECK:                      If
+  /// CHECK:                      If
+  /// CHECK-NOT:                  If
+
+  /// CHECK-START: void Main.noUnrollingNotKnownTripCount(int[], int) loop_optimization (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
   /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.noUnrollingNotKnownTripCount(int[], int) loop_optimization (after)
+  /// CHECK:                      ArraySet
   /// CHECK-NOT:                  ArraySet
   private static final void noUnrollingNotKnownTripCount(int[] a, int n) {
     for (int i = 0; i < n; i++) {
@@ -732,9 +974,15 @@
   /// CHECK-DAG:                    ArrayGet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                    ArraySet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<IndAdd:i\d+>>    Add [<<Phi>>,<<Const1>>]                  loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                    If
-  /// CHECK-NOT:                    ArraySet
+
+  /// CHECK-START: void Main.peelingSimple(int[], boolean) loop_optimization (before)
+  /// CHECK:                      If
+  /// CHECK:                      If
+  /// CHECK-NOT:                  If
+
+  /// CHECK-START: void Main.peelingSimple(int[], boolean) loop_optimization (before)
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
 
   /// CHECK-START: void Main.peelingSimple(int[], boolean) loop_optimization (after)
   /// CHECK-DAG: <<Param:z\d+>>     ParameterValue                            loop:none
@@ -754,9 +1002,18 @@
   /// CHECK-DAG:                    ArrayGet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                    ArraySet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<IndAdd:i\d+>>    Add [<<Phi>>,<<Const1>>]                  loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                    If
-  /// CHECK-NOT:                    ArraySet
+
+  /// CHECK-START: void Main.peelingSimple(int[], boolean) loop_optimization (after)
+  /// CHECK:                      If
+  /// CHECK:                      If
+  /// CHECK:                      If
+  /// CHECK:                      If
+  /// CHECK-NOT:                  If
+
+  /// CHECK-START: void Main.peelingSimple(int[], boolean) loop_optimization (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
 
   /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after)
   /// CHECK-DAG: <<Param:z\d+>>     ParameterValue                            loop:none
@@ -772,11 +1029,25 @@
   /// CHECK-DAG:                    ArrayGet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                    ArraySet                                  loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<IndAdd:i\d+>>    Add [<<Phi>>,<<Const1>>]                  loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                    GreaterThanOrEqual
-  /// CHECK-NOT:                    If
-  /// CHECK-NOT:                    ArrayGet
-  /// CHECK-NOT:                    ArraySet
+
+  /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after)
+  /// CHECK:                      GreaterThanOrEqual
+  /// CHECK-NOT:                  GreaterThanOrEqual
+
+  /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after)
+  /// CHECK:                      If
+  /// CHECK:                      If
+  /// CHECK-NOT:                  If
+
+  /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after)
+  /// CHECK:                      ArrayGet
+  /// CHECK:                      ArrayGet
+  /// CHECK-NOT:                  ArrayGet
+
+  /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after)
+  /// CHECK:                      ArraySet
+  /// CHECK:                      ArraySet
+  /// CHECK-NOT:                  ArraySet
   private static final void peelingSimple(int[] a, boolean f) {
     for (int i = 0; i < LENGTH; i++) {
       if (f) {
@@ -808,8 +1079,13 @@
   /// CHECK-DAG: <<Check:z\d+>>     GreaterThanOrEqual [<<Phi>>,{{i\d+}}] loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                    If [<<Check>>]                        loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                    ArraySet                              loop:<<Loop>>      outer_loop:none
-  //
-  /// CHECK-NOT:                    If [<<Eq>>]                           loop:<<Loop>>      outer_loop:none
+
+  // There's a 3rd `if` due to bounds checks.
+  /// CHECK-START: void Main.peelingAddInts(int[]) dead_code_elimination$final (after)
+  /// CHECK:                        If
+  /// CHECK:                        If
+  /// CHECK:                        If
+  /// CHECK-NOT:                    If
   private static final void peelingAddInts(int[] a) {
     for (int i = 0; a != null && i < a.length; i++) {
       a[i] += 1;
@@ -830,8 +1106,16 @@
   /// CHECK-DAG:                    ArraySet                                loop:<<Loop1>>      outer_loop:<<Loop0>>
   /// CHECK-DAG: <<IndAdd1:i\d+>>   Add [<<Phi1>>,<<Const1>>]               loop:<<Loop1>>      outer_loop:<<Loop0>>
   /// CHECK-DAG: <<IndAdd0:i\d+>>   Add [<<Phi0>>,<<Const1>>]               loop:<<Loop0>>      outer_loop:none
-  //
+
+  // The two loops have an `if`. The 3rd `if` is the explicit one in the innermost loop.
+  /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) loop_optimization (before)
+  /// CHECK:                        If
+  /// CHECK:                        If
+  /// CHECK:                        If
   /// CHECK-NOT:                    If
+
+  /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) loop_optimization (before)
+  /// CHECK:                        ArraySet
   /// CHECK-NOT:                    ArraySet
 
   /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$final (after)
@@ -850,8 +1134,16 @@
   /// CHECK-DAG:                    ArraySet                                loop:<<Loop1>>      outer_loop:<<Loop0>>
   /// CHECK-DAG: <<IndAdd1:i\d+>>   Add [<<Phi1>>,<<Const1>>]               loop:<<Loop1>>      outer_loop:<<Loop0>>
   /// CHECK-DAG: <<IndAdd0:i\d+>>   Add [<<Phi0>>,<<Const1>>]               loop:<<Loop0>>      outer_loop:none
-  //
+
+  /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$final (after)
+  /// CHECK:                        If
+  /// CHECK:                        If
+  /// CHECK:                        If
   /// CHECK-NOT:                    If
+
+  /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$final (after)
+  /// CHECK:                        ArraySet
+  /// CHECK:                        ArraySet
   /// CHECK-NOT:                    ArraySet
   private static final void peelingBreakFromNest(int[] a, boolean f) {
     outer:
@@ -873,7 +1165,9 @@
   /// CHECK-DAG: <<Phi:i\d+>>       Phi [<<Const0>>,{{i\d+}}]                 loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG:                    If [<<Check>>]                            loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: <<IndAdd:i\d+>>    Add [<<Phi>>,<<Const1>>]                  loop:<<Loop>>      outer_loop:none
-  //
+
+  /// CHECK-START: int Main.peelingHoistOneControl(int) loop_optimization (before)
+  /// CHECK:                        If
   /// CHECK-NOT:                    If
 
   /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$final (after)
@@ -886,8 +1180,15 @@
   //
   //  Check that the loop has no instruction except SuspendCheck and Goto (indefinite loop).
   /// CHECK-NOT:                                                              loop:<<Loop>>      outer_loop:none
+
+  /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$final (after)
+  /// CHECK:                        If
   /// CHECK-NOT:                    If
+
+  /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$final (after)
   /// CHECK-NOT:                    Phi
+
+  /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$final (after)
   /// CHECK-NOT:                    Add
   private static final int peelingHoistOneControl(int x) {
     int i = 0;
@@ -902,11 +1203,17 @@
   /// CHECK-DAG: <<Phi:i\d+>> Phi  loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG:              If   loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:              If   loop:<<Loop>>      outer_loop:none
-  //
+
   /// CHECK-START: int Main.peelingHoistOneControl(int, int) dead_code_elimination$final (after)
   /// CHECK-DAG: <<Phi:i\d+>> Phi  loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG:              If   loop:<<Loop>>      outer_loop:none
-  /// CHECK-NOT:              If   loop:<<Loop>>      outer_loop:none
+
+  // One `if` inside the loop (the one no longer invariant), two outside of it.
+  /// CHECK-START: int Main.peelingHoistOneControl(int, int) dead_code_elimination$final (after)
+  /// CHECK:                  If
+  /// CHECK:                  If
+  /// CHECK:                  If
+  /// CHECK-NOT:              If
   private static final int peelingHoistOneControl(int x, int y) {
     while (true) {
       if (x == 0)
@@ -922,11 +1229,18 @@
   /// CHECK-DAG:              If   loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:              If   loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:              If   loop:<<Loop>>      outer_loop:none
-  //
+
   /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) dead_code_elimination$final (after)
   /// CHECK-DAG: <<Phi:i\d+>> Phi  loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-DAG:              If   loop:<<Loop>>      outer_loop:none
-  /// CHECK-NOT:              If   loop:<<Loop>>      outer_loop:none
+
+  // One `if` inside the loop (the one no longer invariant), three outside of it.
+  /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) dead_code_elimination$final (after)
+  /// CHECK:                  If
+  /// CHECK:                  If
+  /// CHECK:                  If
+  /// CHECK:                  If
+  /// CHECK-NOT:              If
   private static final int peelingHoistTwoControl(int x, int y, int z) {
     while (true) {
       if (x == 0)
@@ -948,8 +1262,14 @@
   /// CHECK-DAG:                    ArrayGet                                loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                    ArrayGet                                loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                    ArraySet                                loop:<<Loop>>      outer_loop:none
-  //
+
+  /// CHECK-START: void Main.unrollingFull(int[]) loop_optimization (before)
+  /// CHECK:                        ArrayGet
+  /// CHECK:                        ArrayGet
   /// CHECK-NOT:                    ArrayGet
+
+  /// CHECK-START: void Main.unrollingFull(int[]) loop_optimization (before)
+  /// CHECK:                        ArraySet
   /// CHECK-NOT:                    ArraySet
 
   /// CHECK-START: void Main.unrollingFull(int[]) loop_optimization (after)
@@ -970,8 +1290,20 @@
   /// CHECK-DAG:                    ArrayGet                                loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                    ArraySet                                loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG:                    If [<<Const1>>]                         loop:<<Loop>>      outer_loop:none
-  //
+
+  /// CHECK-START: void Main.unrollingFull(int[]) loop_optimization (after)
+  /// CHECK:                        ArrayGet
+  /// CHECK:                        ArrayGet
+  /// CHECK:                        ArrayGet
+  /// CHECK:                        ArrayGet
+  /// CHECK:                        ArrayGet
+  /// CHECK:                        ArrayGet
   /// CHECK-NOT:                    ArrayGet
+
+  /// CHECK-START: void Main.unrollingFull(int[]) loop_optimization (after)
+  /// CHECK:                        ArraySet
+  /// CHECK:                        ArraySet
+  /// CHECK:                        ArraySet
   /// CHECK-NOT:                    ArraySet
   private static final void unrollingFull(int[] a) {
     for (int i = 0; i < 2; i++) {
diff --git a/test/731-bounds-check-slow-path/Android.bp b/test/731-bounds-check-slow-path/Android.bp
new file mode 100644
index 0000000..bc9e84b
--- /dev/null
+++ b/test/731-bounds-check-slow-path/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `731-bounds-check-slow-path`.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "art_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+    name: "art-run-test-731-bounds-check-slow-path",
+    defaults: ["art-run-test-defaults"],
+    test_config_template: ":art-run-test-target-template",
+    srcs: ["src/**/*.java"],
+    data: [
+        ":art-run-test-731-bounds-check-slow-path-expected-stdout",
+        ":art-run-test-731-bounds-check-slow-path-expected-stderr",
+    ],
+}
+
+// Test's expected standard output.
+genrule {
+    name: "art-run-test-731-bounds-check-slow-path-expected-stdout",
+    out: ["art-run-test-731-bounds-check-slow-path-expected-stdout.txt"],
+    srcs: ["expected-stdout.txt"],
+    cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+    name: "art-run-test-731-bounds-check-slow-path-expected-stderr",
+    out: ["art-run-test-731-bounds-check-slow-path-expected-stderr.txt"],
+    srcs: ["expected-stderr.txt"],
+    cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/731-bounds-check-slow-path/expected-stderr.txt b/test/731-bounds-check-slow-path/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/731-bounds-check-slow-path/expected-stderr.txt
diff --git a/test/731-bounds-check-slow-path/expected-stdout.txt b/test/731-bounds-check-slow-path/expected-stdout.txt
new file mode 100644
index 0000000..f3ed3fd
--- /dev/null
+++ b/test/731-bounds-check-slow-path/expected-stdout.txt
@@ -0,0 +1,3 @@
+i17 i18 b = 297,33580744,0
+l i19 i20 = 146,172,60
+i16 b i17 = -848052580,0,401
diff --git a/test/731-bounds-check-slow-path/info.txt b/test/731-bounds-check-slow-path/info.txt
new file mode 100644
index 0000000..0129f67
--- /dev/null
+++ b/test/731-bounds-check-slow-path/info.txt
@@ -0,0 +1,3 @@
+Regression tests for x86-64 bug in bounds check slow path that resulted
+in a live register being clobbered when throwing AIOOBE. The value must
+be correctly preserved for a catch handler in the same method.
diff --git a/test/731-bounds-check-slow-path/src/Main.java b/test/731-bounds-check-slow-path/src/Main.java
new file mode 100644
index 0000000..76b5723
--- /dev/null
+++ b/test/731-bounds-check-slow-path/src/Main.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+    public static void main(String args[]) {
+        Test227365246 test227365246 = new Test227365246();
+        test227365246.$noinline$mainTest(args);
+
+        Test216608614 test216608614 = new Test216608614();
+        test216608614.$noinline$mainTest(args);
+
+        Test216629762 test216629762 = new Test216629762();
+        test216629762.$noinline$mainTest(args);
+    }
+}
+
+class Test227365246 {
+    int N = 400;
+    int iFld;
+
+    void $noinline$mainTest(String[] strArr1) {
+        int i17, i18 = 5788, i19, i21, i22 = 127, i23;
+        byte[] byArr = new byte[N];
+        for (i17 = 14; 297 > i17; ++i17)
+            for (int ax$2 = 151430; ax$2 < 235417; ax$2 += 2) {}
+        try {
+            for (i19 = 4; 179 > i19; ++i19) {
+                i18 *= i18;
+                for (i21 = 1; i21 < 58; i21++)
+                    for (i23 = i21; 1 + 400 > i23; i23++) {
+                        byArr[i23] -= i22;
+                        i18 += i23;
+                        switch (i19 % 5) {
+                            case 107:
+                                i19 >>= iFld;
+                        }
+                    }
+            }
+        } catch (ArrayIndexOutOfBoundsException exc1) {
+        }
+        System.out.println("i17 i18 b = " + i17 + "," + i18 + "," + 0);
+    }
+}
+
+class Test216608614 {
+    int N = 400;
+    long lFld;
+    double dFld;
+    int iArrFld[]=new int[N];
+    void $noinline$mainTest(String[] strArr1) {
+        // Note: The original bug report started with `l=-1213929899L` but this took
+        // too long when running with interpreter without JIT and we want to allow
+        // this test to run for all configurations. Starting with `l=-1000000L` was
+        // enough to allow JIT to compile the method for OSR and trigger the bug on host.
+        long l=-1000000L;
+        int i19= 46, i20=100, i21, i22=13, i25;
+        try {
+            do
+                for (; i19 < 172; ++i19)
+                    lFld = (long) dFld;
+            while (++l < 146);
+            for (i21 = 8;; ++i21)
+                for (i25 = 1; i25 < 2; i25++) {
+                    i20 = i22 % 1650388388;
+                    i20 = iArrFld[i21];
+                    i22 = 60;
+                }
+        } catch (ArrayIndexOutOfBoundsException exc1) {
+        } finally {
+        }
+        System.out.println("l i19 i20 = " + l + "," + i19 + "," + i20);
+    }
+}
+
+class Test216629762 {
+    static int N = 400;
+    int iFld=29275;
+    volatile double dFld;
+    static long lArrFld[][]=new long[N][N];
+
+    void $noinline$mainTest(String[] strArr1) {
+        int i8, i10=181, i11, i12=-57574, i13=69, i15= 6, i16= 186, i17= 227;
+        try {
+            for (i11 = 6; i11 < 278 + 400; ++i11)
+                i12 *= iFld;
+            for (;; i13++) {
+                i10 /= i10;
+                i16 += i15;
+                lArrFld[i13][i15] >>= 31616;
+                for (i17 = 1; i17 <  1 + 400; i17++)
+                dFld += dFld;
+            }
+        }
+        catch (ArrayIndexOutOfBoundsException exc2) {
+            i16 += i12;
+        }
+        System.out.println("i16 b i17 = " + i16 + "," + 0  + "," + i17);
+    }
+}
diff --git a/test/odsign/Android.bp b/test/odsign/Android.bp
index c3cccd3..511f5a1 100644
--- a/test/odsign/Android.bp
+++ b/test/odsign/Android.bp
@@ -16,14 +16,7 @@
     default_applicable_licenses: ["art_license"],
 }
 
-soong_config_module_type_import {
-    from: "art/build/SoongConfig.bp",
-    module_types: [
-        "art_module_java_defaults",
-    ],
-}
-
-art_module_java_defaults {
+java_defaults {
     name: "odsign_e2e_tests_defaults",
     defaults: ["art_module_source_build_java_defaults"],
     srcs: ["test-src/**/*.java"],
@@ -32,14 +25,16 @@
         "cts-install-lib-host",
         "frameworks-base-hostutils",
     ],
-    data: [
-        ":odsign_e2e_test_app",
-    ],
 }
 
 java_test_host {
     name: "odsign_e2e_tests",
     defaults: ["odsign_e2e_tests_defaults"],
+    // TODO(b/228838581): Do not add `data` to the defaults unless the bug is
+    // fixed.
+    data: [
+        ":odsign_e2e_test_app",
+    ],
     test_config: "odsign-e2e-tests.xml",
     test_suites: [
         "general-tests",
@@ -50,6 +45,11 @@
 java_test_host {
     name: "odsign_e2e_tests_full",
     defaults: ["odsign_e2e_tests_defaults"],
+    // TODO(b/228838581): Do not add `data` to the defaults unless the bug is
+    // fixed.
+    data: [
+        ":odsign_e2e_test_app",
+    ],
     test_config: "odsign-e2e-tests-full.xml",
     test_suites: [
         "general-tests",