Use iterators "before" the use node in HUserRecord<>.

Create a new template class IntrusiveForwardList<> that
mimicks std::forward_list<> except that all allocations
are handled externally. This is essentially the same as
boost::intrusive::slist<> but since we're not using Boost
we have to reinvent the wheel.

Use the new container to replace the HUseList and use the
iterators to "before" use nodes in HUserRecord<> to avoid
the extra pointer to the previous node which was used
exclusively for removing nodes from the list. This reduces
the size of the HUseListNode by 25%, 32B to 24B in 64-bit
compiler, 16B to 12B in 32-bit compiler. This translates
directly to overall memory savings for the 64-bit compiler
but due to rounding up of the arena allocations to 8B, we
do not get any improvement in the 32-bit compiler.

Compiling the Nexus 5 boot image with the 64-bit dex2oat
on host this CL reduces the memory used for compiling the
most hungry method, BatteryStats.dumpLocked(), by ~3.3MiB:

Before:
  MEM: used: 47829200, allocated: 48769120, lost: 939920
  Number of arenas allocated: 345,
  Number of allocations: 815492, avg size: 58
  ...
  UseListNode    13744640
  ...
After:
  MEM: used: 44393040, allocated: 45361248, lost: 968208
  Number of arenas allocated: 319,
  Number of allocations: 815492, avg size: 54
  ...
  UseListNode    10308480
  ...

Note that while we do not ship the 64-bit dex2oat to the
device, the JIT compilation for 64-bit processes is using
the 64-bit libart-compiler.

Bug: 28173563
Change-Id: I985eabd4816f845372d8aaa825a1489cf9569208
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 8309bd8..c3275cd 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -275,6 +275,7 @@
   compiler/optimizing/suspend_check_test.cc \
   compiler/utils/arena_allocator_test.cc \
   compiler/utils/dedupe_set_test.cc \
+  compiler/utils/intrusive_forward_list_test.cc \
   compiler/utils/swap_space_test.cc \
   compiler/utils/test_dex_file_builder_test.cc \
 
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 084360f..659c6f8 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -1206,9 +1206,9 @@
           GetGraph()->GetArena()->Adapter(kArenaAllocBoundsCheckElimination));
       ArenaVector<HBoundsCheck*> standby(
           GetGraph()->GetArena()->Adapter(kArenaAllocBoundsCheckElimination));
-      for (HUseIterator<HInstruction*> it2(array_length->GetUses()); !it2.Done(); it2.Advance()) {
+      for (const HUseListNode<HInstruction*>& use : array_length->GetUses()) {
         // Another bounds check in same or dominated block?
-        HInstruction* user = it2.Current()->GetUser();
+        HInstruction* user = use.GetUser();
         HBasicBlock* other_block = user->GetBlock();
         if (user->IsBoundsCheck() && block->Dominates(other_block)) {
           HBoundsCheck* other_bounds_check = user->AsBoundsCheck();
@@ -1635,29 +1635,33 @@
         Primitive::Type type = instruction->GetType();
         HPhi* phi = nullptr;
         // Scan all uses of an instruction and replace each later use with a phi node.
-        for (HUseIterator<HInstruction*> it2(instruction->GetUses());
-             !it2.Done();
-             it2.Advance()) {
-          HInstruction* user = it2.Current()->GetUser();
+        const HUseList<HInstruction*>& uses = instruction->GetUses();
+        for (auto it2 = uses.begin(), end2 = uses.end(); it2 != end2; /* ++it2 below */) {
+          HInstruction* user = it2->GetUser();
+          size_t index = it2->GetIndex();
+          // Increment `it2` now because `*it2` may disappear thanks to user->ReplaceInput().
+          ++it2;
           if (user->GetBlock() != true_block) {
             if (phi == nullptr) {
               phi = NewPhi(new_preheader, instruction, type);
             }
-            user->ReplaceInput(phi, it2.Current()->GetIndex());
+            user->ReplaceInput(phi, index);  // Removes the use node from the list.
           }
         }
         // Scan all environment uses of an instruction and replace each later use with a phi node.
-        for (HUseIterator<HEnvironment*> it2(instruction->GetEnvUses());
-             !it2.Done();
-             it2.Advance()) {
-          HEnvironment* user = it2.Current()->GetUser();
+        const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses();
+        for (auto it2 = env_uses.begin(), end2 = env_uses.end(); it2 != end2; /* ++it2 below */) {
+          HEnvironment* user = it2->GetUser();
+          size_t index = it2->GetIndex();
+          // Increment `it2` now because `*it2` may disappear thanks to user->RemoveAsUserOfInput().
+          ++it2;
           if (user->GetHolder()->GetBlock() != true_block) {
             if (phi == nullptr) {
               phi = NewPhi(new_preheader, instruction, type);
             }
-            user->RemoveAsUserOfInput(it2.Current()->GetIndex());
-            user->SetRawEnvAt(it2.Current()->GetIndex(), phi);
-            phi->AddEnvUseAt(user, it2.Current()->GetIndex());
+            user->RemoveAsUserOfInput(index);
+            user->SetRawEnvAt(index, phi);
+            phi->AddEnvUseAt(user, index);
           }
         }
       }
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index 6412b24..a849448 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -199,7 +199,7 @@
 
   // For single uses we let VIXL handle the constant generation since it will
   // use registers that are not managed by the register allocator (wip0, wip1).
-  if (constant->GetUses().HasOnlyOneUse()) {
+  if (constant->GetUses().HasExactlyOneElement()) {
     return true;
   }
 
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 9ea4b2d..96837a8 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -342,36 +342,34 @@
 
   // Ensure the uses of `instruction` are defined in a block of the graph,
   // and the entry in the use list is consistent.
-  for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
-       !use_it.Done(); use_it.Advance()) {
-    HInstruction* use = use_it.Current()->GetUser();
-    const HInstructionList& list = use->IsPhi()
-        ? use->GetBlock()->GetPhis()
-        : use->GetBlock()->GetInstructions();
-    if (!list.Contains(use)) {
+  for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
+    HInstruction* user = use.GetUser();
+    const HInstructionList& list = user->IsPhi()
+        ? user->GetBlock()->GetPhis()
+        : user->GetBlock()->GetInstructions();
+    if (!list.Contains(user)) {
       AddError(StringPrintf("User %s:%d of instruction %d is not defined "
                             "in a basic block of the control-flow graph.",
-                            use->DebugName(),
-                            use->GetId(),
+                            user->DebugName(),
+                            user->GetId(),
                             instruction->GetId()));
     }
-    size_t use_index = use_it.Current()->GetIndex();
-    if ((use_index >= use->InputCount()) || (use->InputAt(use_index) != instruction)) {
+    size_t use_index = use.GetIndex();
+    if ((use_index >= user->InputCount()) || (user->InputAt(use_index) != instruction)) {
       AddError(StringPrintf("User %s:%d of instruction %s:%d has a wrong "
                             "UseListNode index.",
-                            use->DebugName(),
-                            use->GetId(),
+                            user->DebugName(),
+                            user->GetId(),
                             instruction->DebugName(),
                             instruction->GetId()));
     }
   }
 
   // Ensure the environment uses entries are consistent.
-  for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses());
-       !use_it.Done(); use_it.Advance()) {
-    HEnvironment* use = use_it.Current()->GetUser();
-    size_t use_index = use_it.Current()->GetIndex();
-    if ((use_index >= use->Size()) || (use->GetInstructionAt(use_index) != instruction)) {
+  for (const HUseListNode<HEnvironment*>& use : instruction->GetEnvUses()) {
+    HEnvironment* user = use.GetUser();
+    size_t use_index = use.GetIndex();
+    if ((use_index >= user->Size()) || (user->GetInstructionAt(use_index) != instruction)) {
       AddError(StringPrintf("Environment user of %s:%d has a wrong "
                             "UseListNode index.",
                             instruction->DebugName(),
@@ -383,13 +381,11 @@
   for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
     HUserRecord<HInstruction*> input_record = instruction->InputRecordAt(i);
     HInstruction* input = input_record.GetInstruction();
-    HUseListNode<HInstruction*>* use_node = input_record.GetUseNode();
-    size_t use_index = use_node->GetIndex();
-    if ((use_node == nullptr)
-        || !input->GetUses().Contains(use_node)
-        || (use_index >= e)
-        || (use_index != i)) {
-      AddError(StringPrintf("Instruction %s:%d has an invalid pointer to use entry "
+    if ((input_record.GetBeforeUseNode() == input->GetUses().end()) ||
+        (input_record.GetUseNode() == input->GetUses().end()) ||
+        !input->GetUses().ContainsNode(*input_record.GetUseNode()) ||
+        (input_record.GetUseNode()->GetIndex() != i)) {
+      AddError(StringPrintf("Instruction %s:%d has an invalid iterator before use entry "
                             "at input %u (%s:%d).",
                             instruction->DebugName(),
                             instruction->GetId(),
@@ -400,18 +396,17 @@
   }
 
   // Ensure an instruction dominates all its uses.
-  for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
-       !use_it.Done(); use_it.Advance()) {
-    HInstruction* use = use_it.Current()->GetUser();
-    if (!use->IsPhi() && !instruction->StrictlyDominates(use)) {
+  for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
+    HInstruction* user = use.GetUser();
+    if (!user->IsPhi() && !instruction->StrictlyDominates(user)) {
       AddError(StringPrintf("Instruction %s:%d in block %d does not dominate "
                             "use %s:%d in block %d.",
                             instruction->DebugName(),
                             instruction->GetId(),
                             current_block_->GetBlockId(),
-                            use->DebugName(),
-                            use->GetId(),
-                            use->GetBlock()->GetBlockId()));
+                            user->DebugName(),
+                            user->GetId(),
+                            user->GetBlock()->GetBlockId()));
     }
   }
 
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index fe47f7d..9efc13f 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -608,12 +608,7 @@
     for (HInstructionIterator it(list); !it.Done(); it.Advance()) {
       HInstruction* instruction = it.Current();
       int bci = 0;
-      size_t num_uses = 0;
-      for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
-           !use_it.Done();
-           use_it.Advance()) {
-        ++num_uses;
-      }
+      size_t num_uses = instruction->GetUses().SizeSlow();
       AddIndent();
       output_ << bci << " " << num_uses << " "
               << GetTypeId(instruction->GetType()) << instruction->GetId() << " ";
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 1f66db7..d7b3856 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -409,9 +409,9 @@
     return true;
   }
 
-  for (HUseIterator<HInstruction*> it(input->GetUses()); !it.Done(); it.Advance()) {
-    HInstruction* use = it.Current()->GetUser();
-    if (use->IsNullCheck() && use->StrictlyDominates(at)) {
+  for (const HUseListNode<HInstruction*>& use : input->GetUses()) {
+    HInstruction* user = use.GetUser();
+    if (user->IsNullCheck() && user->StrictlyDominates(at)) {
       return true;
     }
   }
@@ -1070,12 +1070,12 @@
   }
 
   // Is the Compare only used for this purpose?
-  if (!left->GetUses().HasOnlyOneUse()) {
+  if (!left->GetUses().HasExactlyOneElement()) {
     // Someone else also wants the result of the compare.
     return;
   }
 
-  if (!left->GetEnvUses().IsEmpty()) {
+  if (!left->GetEnvUses().empty()) {
     // There is a reference to the compare result in an environment. Do we really need it?
     if (GetGraph()->IsDebuggable()) {
       return;
diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc
index f00d960..e4a711e 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.cc
+++ b/compiler/optimizing/instruction_simplifier_arm64.cc
@@ -140,7 +140,7 @@
                                                                  shift_amount,
                                                                  use->GetDexPc());
     use->GetBlock()->ReplaceAndRemoveInstructionWith(use, alu_with_op);
-    if (bitfield_op->GetUses().IsEmpty()) {
+    if (bitfield_op->GetUses().empty()) {
       bitfield_op->GetBlock()->RemoveInstruction(bitfield_op);
     }
     RecordSimplification();
@@ -160,20 +160,22 @@
   const HUseList<HInstruction*>& uses = bitfield_op->GetUses();
 
   // Check whether we can merge the instruction in all its users' shifter operand.
-  for (HUseIterator<HInstruction*> it_use(uses); !it_use.Done(); it_use.Advance()) {
-    HInstruction* use = it_use.Current()->GetUser();
-    if (!HasShifterOperand(use)) {
+  for (const HUseListNode<HInstruction*>& use : uses) {
+    HInstruction* user = use.GetUser();
+    if (!HasShifterOperand(user)) {
       return false;
     }
-    if (!CanMergeIntoShifterOperand(use, bitfield_op)) {
+    if (!CanMergeIntoShifterOperand(user, bitfield_op)) {
       return false;
     }
   }
 
   // Merge the instruction into its uses.
-  for (HUseIterator<HInstruction*> it_use(uses); !it_use.Done(); it_use.Advance()) {
-    HInstruction* use = it_use.Current()->GetUser();
-    bool merged = MergeIntoShifterOperand(use, bitfield_op);
+  for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
+    HInstruction* user = it->GetUser();
+    // Increment `it` now because `*it` will disappear thanks to MergeIntoShifterOperand().
+    ++it;
+    bool merged = MergeIntoShifterOperand(user, bitfield_op);
     DCHECK(merged);
   }
 
diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc
index a11b5bd..dab1ebc 100644
--- a/compiler/optimizing/instruction_simplifier_shared.cc
+++ b/compiler/optimizing/instruction_simplifier_shared.cc
@@ -103,13 +103,10 @@
       return false;
   }
 
-  HInstruction* use = mul->HasNonEnvironmentUses()
-      ? mul->GetUses().GetFirst()->GetUser()
-      : nullptr;
-
   ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena();
 
   if (mul->HasOnlyOneNonEnvironmentUse()) {
+    HInstruction* use = mul->GetUses().front().GetUser();
     if (use->IsAdd() || use->IsSub()) {
       // Replace code looking like
       //    MUL tmp, x, y
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index bdaef1d..f9a955f 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -441,7 +441,7 @@
   ASSERT_TRUE(range->GetNext() == nullptr);
 
   HPhi* phi = liveness.GetInstructionFromSsaIndex(4)->AsPhi();
-  ASSERT_TRUE(phi->GetUses().HasOnlyOneUse());
+  ASSERT_TRUE(phi->GetUses().HasExactlyOneElement());
   interval = phi->GetLiveInterval();
   range = interval->GetFirstRange();
   ASSERT_EQ(26u, range->GetStart());
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index ac7ed86..2de4158 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -43,31 +43,29 @@
 
     // Visit all uses to determine if this reference can spread into the heap,
     // a method call, etc.
-    for (HUseIterator<HInstruction*> use_it(reference_->GetUses());
-         !use_it.Done();
-         use_it.Advance()) {
-      HInstruction* use = use_it.Current()->GetUser();
-      DCHECK(!use->IsNullCheck()) << "NullCheck should have been eliminated";
-      if (use->IsBoundType()) {
+    for (const HUseListNode<HInstruction*>& use : reference_->GetUses()) {
+      HInstruction* user = use.GetUser();
+      DCHECK(!user->IsNullCheck()) << "NullCheck should have been eliminated";
+      if (user->IsBoundType()) {
         // BoundType shouldn't normally be necessary for a NewInstance.
         // Just be conservative for the uncommon cases.
         is_singleton_ = false;
         is_singleton_and_not_returned_ = false;
         return;
       }
-      if (use->IsPhi() || use->IsSelect() || use->IsInvoke() ||
-          (use->IsInstanceFieldSet() && (reference_ == use->InputAt(1))) ||
-          (use->IsUnresolvedInstanceFieldSet() && (reference_ == use->InputAt(1))) ||
-          (use->IsStaticFieldSet() && (reference_ == use->InputAt(1))) ||
-          (use->IsUnresolvedStaticFieldSet() && (reference_ == use->InputAt(0))) ||
-          (use->IsArraySet() && (reference_ == use->InputAt(2)))) {
+      if (user->IsPhi() || user->IsSelect() || user->IsInvoke() ||
+          (user->IsInstanceFieldSet() && (reference_ == user->InputAt(1))) ||
+          (user->IsUnresolvedInstanceFieldSet() && (reference_ == user->InputAt(1))) ||
+          (user->IsStaticFieldSet() && (reference_ == user->InputAt(1))) ||
+          (user->IsUnresolvedStaticFieldSet() && (reference_ == user->InputAt(0))) ||
+          (user->IsArraySet() && (reference_ == user->InputAt(2)))) {
         // reference_ is merged to HPhi/HSelect, passed to a callee, or stored to heap.
         // reference_ isn't the only name that can refer to its value anymore.
         is_singleton_ = false;
         is_singleton_and_not_returned_ = false;
         return;
       }
-      if (use->IsReturn()) {
+      if (user->IsReturn()) {
         is_singleton_and_not_returned_ = false;
       }
     }
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 1afa36a..53a10f3 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -681,8 +681,8 @@
     DCHECK_EQ(replacement->GetType(), Primitive::kPrimVoid);
     DCHECK_EQ(initial->GetBlock(), this);
     DCHECK_EQ(initial->GetType(), Primitive::kPrimVoid);
-    DCHECK(initial->GetUses().IsEmpty());
-    DCHECK(initial->GetEnvUses().IsEmpty());
+    DCHECK(initial->GetUses().empty());
+    DCHECK(initial->GetEnvUses().empty());
     replacement->SetBlock(this);
     replacement->SetId(GetGraph()->GetNextInstructionId());
     instructions_.InsertInstructionBefore(replacement, initial);
@@ -774,8 +774,8 @@
   instruction->SetBlock(nullptr);
   instruction_list->RemoveInstruction(instruction);
   if (ensure_safety) {
-    DCHECK(instruction->GetUses().IsEmpty());
-    DCHECK(instruction->GetEnvUses().IsEmpty());
+    DCHECK(instruction->GetUses().empty());
+    DCHECK(instruction->GetEnvUses().empty());
     RemoveAsUser(instruction);
   }
 }
@@ -839,8 +839,11 @@
 }
 
 void HEnvironment::RemoveAsUserOfInput(size_t index) const {
-  const HUserRecord<HEnvironment*>& user_record = vregs_[index];
-  user_record.GetInstruction()->RemoveEnvironmentUser(user_record.GetUseNode());
+  const HUserRecord<HEnvironment*>& env_use = vregs_[index];
+  HInstruction* user = env_use.GetInstruction();
+  auto before_env_use_node = env_use.GetBeforeUseNode();
+  user->env_uses_.erase_after(before_env_use_node);
+  user->FixUpUserRecordsAfterEnvUseRemoval(before_env_use_node);
 }
 
 HInstruction::InstructionKind HInstruction::GetKind() const {
@@ -985,24 +988,22 @@
 
 void HInstruction::ReplaceWith(HInstruction* other) {
   DCHECK(other != nullptr);
-  for (HUseIterator<HInstruction*> it(GetUses()); !it.Done(); it.Advance()) {
-    HUseListNode<HInstruction*>* current = it.Current();
-    HInstruction* user = current->GetUser();
-    size_t input_index = current->GetIndex();
+  for (const HUseListNode<HInstruction*>& use : GetUses()) {
+    HInstruction* user = use.GetUser();
+    size_t input_index = use.GetIndex();
     user->SetRawInputAt(input_index, other);
     other->AddUseAt(user, input_index);
   }
 
-  for (HUseIterator<HEnvironment*> it(GetEnvUses()); !it.Done(); it.Advance()) {
-    HUseListNode<HEnvironment*>* current = it.Current();
-    HEnvironment* user = current->GetUser();
-    size_t input_index = current->GetIndex();
+  for (const HUseListNode<HEnvironment*>& use : GetEnvUses()) {
+    HEnvironment* user = use.GetUser();
+    size_t input_index = use.GetIndex();
     user->SetRawEnvAt(input_index, other);
     other->AddEnvUseAt(user, input_index);
   }
 
-  uses_.Clear();
-  env_uses_.Clear();
+  uses_.clear();
+  env_uses_.clear();
 }
 
 void HInstruction::ReplaceInput(HInstruction* replacement, size_t index) {
@@ -1280,17 +1281,18 @@
   DCHECK_EQ(InputCount(), 0u);
 
   // Find the target block.
-  HUseIterator<HInstruction*> uses_it(GetUses());
-  HBasicBlock* target_block = uses_it.Current()->GetUser()->GetBlock();
-  uses_it.Advance();
-  while (!uses_it.Done() && uses_it.Current()->GetUser()->GetBlock() == target_block) {
-    uses_it.Advance();
+  auto uses_it = GetUses().begin();
+  auto uses_end = GetUses().end();
+  HBasicBlock* target_block = uses_it->GetUser()->GetBlock();
+  ++uses_it;
+  while (uses_it != uses_end && uses_it->GetUser()->GetBlock() == target_block) {
+    ++uses_it;
   }
-  if (!uses_it.Done()) {
+  if (uses_it != uses_end) {
     // This instruction has uses in two or more blocks. Find the common dominator.
     CommonDominator finder(target_block);
-    for (; !uses_it.Done(); uses_it.Advance()) {
-      finder.Update(uses_it.Current()->GetUser()->GetBlock());
+    for (; uses_it != uses_end; ++uses_it) {
+      finder.Update(uses_it->GetUser()->GetBlock());
     }
     target_block = finder.Get();
     DCHECK(target_block != nullptr);
@@ -1303,10 +1305,10 @@
 
   // Find insertion position.
   HInstruction* insert_pos = nullptr;
-  for (HUseIterator<HInstruction*> uses_it2(GetUses()); !uses_it2.Done(); uses_it2.Advance()) {
-    if (uses_it2.Current()->GetUser()->GetBlock() == target_block &&
-        (insert_pos == nullptr || uses_it2.Current()->GetUser()->StrictlyDominates(insert_pos))) {
-      insert_pos = uses_it2.Current()->GetUser();
+  for (const HUseListNode<HInstruction*>& use : GetUses()) {
+    if (use.GetUser()->GetBlock() == target_block &&
+        (insert_pos == nullptr || use.GetUser()->StrictlyDominates(insert_pos))) {
+      insert_pos = use.GetUser();
     }
   }
   if (insert_pos == nullptr) {
@@ -1586,10 +1588,10 @@
 static void RemoveUsesOfDeadInstruction(HInstruction* insn) {
   DCHECK(!insn->HasEnvironmentUses());
   while (insn->HasNonEnvironmentUses()) {
-    HUseListNode<HInstruction*>* use = insn->GetUses().GetFirst();
-    size_t use_index = use->GetIndex();
-    HBasicBlock* user_block =  use->GetUser()->GetBlock();
-    DCHECK(use->GetUser()->IsPhi() && user_block->IsCatchBlock());
+    const HUseListNode<HInstruction*>& use = insn->GetUses().front();
+    size_t use_index = use.GetIndex();
+    HBasicBlock* user_block =  use.GetUser()->GetBlock();
+    DCHECK(use.GetUser()->IsPhi() && user_block->IsCatchBlock());
     for (HInstructionIterator phi_it(user_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
       phi_it.Current()->AsPhi()->RemoveInputAt(use_index);
     }
@@ -2391,12 +2393,11 @@
 }
 
 void HInstruction::RemoveEnvironmentUsers() {
-  for (HUseIterator<HEnvironment*> use_it(GetEnvUses()); !use_it.Done(); use_it.Advance()) {
-    HUseListNode<HEnvironment*>* user_node = use_it.Current();
-    HEnvironment* user = user_node->GetUser();
-    user->SetRawEnvAt(user_node->GetIndex(), nullptr);
+  for (const HUseListNode<HEnvironment*>& use : GetEnvUses()) {
+    HEnvironment* user = use.GetUser();
+    user->SetRawEnvAt(use.GetIndex(), nullptr);
   }
-  env_uses_.Clear();
+  env_uses_.clear();
 }
 
 // Returns an instruction with the opposite Boolean value from 'cond'.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 1ea2247..885abff 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -36,6 +36,7 @@
 #include "offsets.h"
 #include "primitive.h"
 #include "utils/array_ref.h"
+#include "utils/intrusive_forward_list.h"
 
 namespace art {
 
@@ -1334,127 +1335,31 @@
   const H##type* As##type() const { return this; }                      \
   H##type* As##type() { return this; }
 
-template <typename T> class HUseList;
-
 template <typename T>
 class HUseListNode : public ArenaObject<kArenaAllocUseListNode> {
  public:
-  HUseListNode* GetPrevious() const { return prev_; }
-  HUseListNode* GetNext() const { return next_; }
   T GetUser() const { return user_; }
   size_t GetIndex() const { return index_; }
   void SetIndex(size_t index) { index_ = index; }
 
+  // Hook for the IntrusiveForwardList<>.
+  // TODO: Hide this better.
+  IntrusiveForwardListHook hook;
+
  private:
   HUseListNode(T user, size_t index)
-      : user_(user), index_(index), prev_(nullptr), next_(nullptr) {}
+      : user_(user), index_(index) {}
 
   T const user_;
   size_t index_;
-  HUseListNode<T>* prev_;
-  HUseListNode<T>* next_;
 
-  friend class HUseList<T>;
+  friend class HInstruction;
 
   DISALLOW_COPY_AND_ASSIGN(HUseListNode);
 };
 
 template <typename T>
-class HUseList : public ValueObject {
- public:
-  HUseList() : first_(nullptr) {}
-
-  void Clear() {
-    first_ = nullptr;
-  }
-
-  // Adds a new entry at the beginning of the use list and returns
-  // the newly created node.
-  HUseListNode<T>* AddUse(T user, size_t index, ArenaAllocator* arena) {
-    HUseListNode<T>* new_node = new (arena) HUseListNode<T>(user, index);
-    if (IsEmpty()) {
-      first_ = new_node;
-    } else {
-      first_->prev_ = new_node;
-      new_node->next_ = first_;
-      first_ = new_node;
-    }
-    return new_node;
-  }
-
-  HUseListNode<T>* GetFirst() const {
-    return first_;
-  }
-
-  void Remove(HUseListNode<T>* node) {
-    DCHECK(node != nullptr);
-    DCHECK(Contains(node));
-
-    if (node->prev_ != nullptr) {
-      node->prev_->next_ = node->next_;
-    }
-    if (node->next_ != nullptr) {
-      node->next_->prev_ = node->prev_;
-    }
-    if (node == first_) {
-      first_ = node->next_;
-    }
-  }
-
-  bool Contains(const HUseListNode<T>* node) const {
-    if (node == nullptr) {
-      return false;
-    }
-    for (HUseListNode<T>* current = first_; current != nullptr; current = current->GetNext()) {
-      if (current == node) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  bool IsEmpty() const {
-    return first_ == nullptr;
-  }
-
-  bool HasOnlyOneUse() const {
-    return first_ != nullptr && first_->next_ == nullptr;
-  }
-
-  size_t SizeSlow() const {
-    size_t count = 0;
-    for (HUseListNode<T>* current = first_; current != nullptr; current = current->GetNext()) {
-      ++count;
-    }
-    return count;
-  }
-
- private:
-  HUseListNode<T>* first_;
-};
-
-template<typename T>
-class HUseIterator : public ValueObject {
- public:
-  explicit HUseIterator(const HUseList<T>& uses) : current_(uses.GetFirst()) {}
-
-  bool Done() const { return current_ == nullptr; }
-
-  void Advance() {
-    DCHECK(!Done());
-    current_ = current_->GetNext();
-  }
-
-  HUseListNode<T>* Current() const {
-    DCHECK(!Done());
-    return current_;
-  }
-
- private:
-  HUseListNode<T>* current_;
-
-  friend class HValue;
-};
+using HUseList = IntrusiveForwardList<HUseListNode<T>>;
 
 // This class is used by HEnvironment and HInstruction classes to record the
 // instructions they use and pointers to the corresponding HUseListNodes kept
@@ -1462,25 +1367,26 @@
 template <typename T>
 class HUserRecord : public ValueObject {
  public:
-  HUserRecord() : instruction_(nullptr), use_node_(nullptr) {}
-  explicit HUserRecord(HInstruction* instruction) : instruction_(instruction), use_node_(nullptr) {}
+  HUserRecord() : instruction_(nullptr), before_use_node_() {}
+  explicit HUserRecord(HInstruction* instruction) : instruction_(instruction), before_use_node_() {}
 
-  HUserRecord(const HUserRecord<T>& old_record, HUseListNode<T>* use_node)
-    : instruction_(old_record.instruction_), use_node_(use_node) {
+  HUserRecord(const HUserRecord<T>& old_record, typename HUseList<T>::iterator before_use_node)
+      : HUserRecord(old_record.instruction_, before_use_node) {}
+  HUserRecord(HInstruction* instruction, typename HUseList<T>::iterator before_use_node)
+      : instruction_(instruction), before_use_node_(before_use_node) {
     DCHECK(instruction_ != nullptr);
-    DCHECK(use_node_ != nullptr);
-    DCHECK(old_record.use_node_ == nullptr);
   }
 
   HInstruction* GetInstruction() const { return instruction_; }
-  HUseListNode<T>* GetUseNode() const { return use_node_; }
+  typename HUseList<T>::iterator GetBeforeUseNode() const { return before_use_node_; }
+  typename HUseList<T>::iterator GetUseNode() const { return ++GetBeforeUseNode(); }
 
  private:
   // Instruction used by the user.
   HInstruction* instruction_;
 
-  // Corresponding entry in the use list kept by 'instruction_'.
-  HUseListNode<T>* use_node_;
+  // Iterator before the corresponding entry in the use list kept by 'instruction_'.
+  typename HUseList<T>::iterator before_use_node_;
 };
 
 /**
@@ -1805,14 +1711,6 @@
   }
 
  private:
-  // Record instructions' use entries of this environment for constant-time removal.
-  // It should only be called by HInstruction when a new environment use is added.
-  void RecordEnvUse(HUseListNode<HEnvironment*>* env_use) {
-    DCHECK(env_use->GetUser() == this);
-    size_t index = env_use->GetIndex();
-    vregs_[index] = HUserRecord<HEnvironment*>(vregs_[index], env_use);
-  }
-
   ArenaVector<HUserRecord<HEnvironment*>> vregs_;
   ArenaVector<Location> locations_;
   HEnvironment* parent_;
@@ -1921,31 +1819,39 @@
 
   void AddUseAt(HInstruction* user, size_t index) {
     DCHECK(user != nullptr);
-    HUseListNode<HInstruction*>* use =
-        uses_.AddUse(user, index, GetBlock()->GetGraph()->GetArena());
-    user->SetRawInputRecordAt(index, HUserRecord<HInstruction*>(user->InputRecordAt(index), use));
+    // Note: fixup_end remains valid across push_front().
+    auto fixup_end = uses_.empty() ? uses_.begin() : ++uses_.begin();
+    HUseListNode<HInstruction*>* new_node =
+        new (GetBlock()->GetGraph()->GetArena()) HUseListNode<HInstruction*>(user, index);
+    uses_.push_front(*new_node);
+    FixUpUserRecordsAfterUseInsertion(fixup_end);
   }
 
   void AddEnvUseAt(HEnvironment* user, size_t index) {
     DCHECK(user != nullptr);
-    HUseListNode<HEnvironment*>* env_use =
-        env_uses_.AddUse(user, index, GetBlock()->GetGraph()->GetArena());
-    user->RecordEnvUse(env_use);
+    // Note: env_fixup_end remains valid across push_front().
+    auto env_fixup_end = env_uses_.empty() ? env_uses_.begin() : ++env_uses_.begin();
+    HUseListNode<HEnvironment*>* new_node =
+        new (GetBlock()->GetGraph()->GetArena()) HUseListNode<HEnvironment*>(user, index);
+    env_uses_.push_front(*new_node);
+    FixUpUserRecordsAfterEnvUseInsertion(env_fixup_end);
   }
 
   void RemoveAsUserOfInput(size_t input) {
     HUserRecord<HInstruction*> input_use = InputRecordAt(input);
-    input_use.GetInstruction()->uses_.Remove(input_use.GetUseNode());
+    HUseList<HInstruction*>::iterator before_use_node = input_use.GetBeforeUseNode();
+    input_use.GetInstruction()->uses_.erase_after(before_use_node);
+    input_use.GetInstruction()->FixUpUserRecordsAfterUseRemoval(before_use_node);
   }
 
   const HUseList<HInstruction*>& GetUses() const { return uses_; }
   const HUseList<HEnvironment*>& GetEnvUses() const { return env_uses_; }
 
-  bool HasUses() const { return !uses_.IsEmpty() || !env_uses_.IsEmpty(); }
-  bool HasEnvironmentUses() const { return !env_uses_.IsEmpty(); }
-  bool HasNonEnvironmentUses() const { return !uses_.IsEmpty(); }
+  bool HasUses() const { return !uses_.empty() || !env_uses_.empty(); }
+  bool HasEnvironmentUses() const { return !env_uses_.empty(); }
+  bool HasNonEnvironmentUses() const { return !uses_.empty(); }
   bool HasOnlyOneNonEnvironmentUse() const {
-    return !HasEnvironmentUses() && GetUses().HasOnlyOneUse();
+    return !HasEnvironmentUses() && GetUses().HasExactlyOneElement();
   }
 
   // Does this instruction strictly dominate `other_instruction`?
@@ -2147,7 +2053,45 @@
   }
 
  private:
-  void RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use_node) { env_uses_.Remove(use_node); }
+  void FixUpUserRecordsAfterUseInsertion(HUseList<HInstruction*>::iterator fixup_end) {
+    auto before_use_node = uses_.before_begin();
+    for (auto use_node = uses_.begin(); use_node != fixup_end; ++use_node) {
+      HInstruction* user = use_node->GetUser();
+      size_t input_index = use_node->GetIndex();
+      user->SetRawInputRecordAt(input_index, HUserRecord<HInstruction*>(this, before_use_node));
+      before_use_node = use_node;
+    }
+  }
+
+  void FixUpUserRecordsAfterUseRemoval(HUseList<HInstruction*>::iterator before_use_node) {
+    auto next = ++HUseList<HInstruction*>::iterator(before_use_node);
+    if (next != uses_.end()) {
+      HInstruction* next_user = next->GetUser();
+      size_t next_index = next->GetIndex();
+      DCHECK(next_user->InputRecordAt(next_index).GetInstruction() == this);
+      next_user->SetRawInputRecordAt(next_index, HUserRecord<HInstruction*>(this, before_use_node));
+    }
+  }
+
+  void FixUpUserRecordsAfterEnvUseInsertion(HUseList<HEnvironment*>::iterator env_fixup_end) {
+    auto before_env_use_node = env_uses_.before_begin();
+    for (auto env_use_node = env_uses_.begin(); env_use_node != env_fixup_end; ++env_use_node) {
+      HEnvironment* user = env_use_node->GetUser();
+      size_t input_index = env_use_node->GetIndex();
+      user->vregs_[input_index] = HUserRecord<HEnvironment*>(this, before_env_use_node);
+      before_env_use_node = env_use_node;
+    }
+  }
+
+  void FixUpUserRecordsAfterEnvUseRemoval(HUseList<HEnvironment*>::iterator before_env_use_node) {
+    auto next = ++HUseList<HEnvironment*>::iterator(before_env_use_node);
+    if (next != env_uses_.end()) {
+      HEnvironment* next_user = next->GetUser();
+      size_t next_index = next->GetIndex();
+      DCHECK(next_user->vregs_[next_index].GetInstruction() == this);
+      next_user->vregs_[next_index] = HUserRecord<HEnvironment*>(this, before_env_use_node);
+    }
+  }
 
   HInstruction* previous_;
   HInstruction* next_;
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
index 764f5fe..d4e2a58 100644
--- a/compiler/optimizing/nodes_test.cc
+++ b/compiler/optimizing/nodes_test.cc
@@ -91,7 +91,7 @@
   entry->InsertInstructionBefore(to_insert, parameter2);
 
   ASSERT_TRUE(parameter1->HasUses());
-  ASSERT_TRUE(parameter1->GetUses().HasOnlyOneUse());
+  ASSERT_TRUE(parameter1->GetUses().HasExactlyOneElement());
 }
 
 /**
@@ -115,7 +115,7 @@
   entry->AddInstruction(to_add);
 
   ASSERT_TRUE(parameter->HasUses());
-  ASSERT_TRUE(parameter->GetUses().HasOnlyOneUse());
+  ASSERT_TRUE(parameter->GetUses().HasExactlyOneElement());
 }
 
 TEST(Node, ParentEnvironment) {
@@ -134,7 +134,7 @@
   entry->AddInstruction(new (&allocator) HExit());
 
   ASSERT_TRUE(parameter1->HasUses());
-  ASSERT_TRUE(parameter1->GetUses().HasOnlyOneUse());
+  ASSERT_TRUE(parameter1->GetUses().HasExactlyOneElement());
 
   HEnvironment* environment = new (&allocator) HEnvironment(
       &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, with_environment);
@@ -145,7 +145,7 @@
   with_environment->SetRawEnvironment(environment);
 
   ASSERT_TRUE(parameter1->HasEnvironmentUses());
-  ASSERT_TRUE(parameter1->GetEnvUses().HasOnlyOneUse());
+  ASSERT_TRUE(parameter1->GetEnvUses().HasExactlyOneElement());
 
   HEnvironment* parent1 = new (&allocator) HEnvironment(
       &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, nullptr);
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index fc72727..dcc89e8 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -63,8 +63,8 @@
 void PrepareForRegisterAllocation::VisitClinitCheck(HClinitCheck* check) {
   // Try to find a static invoke or a new-instance from which this check originated.
   HInstruction* implicit_clinit = nullptr;
-  for (HUseIterator<HInstruction*> it(check->GetUses()); !it.Done(); it.Advance()) {
-    HInstruction* user = it.Current()->GetUser();
+  for (const HUseListNode<HInstruction*>& use : check->GetUses()) {
+    HInstruction* user = use.GetUser();
     if ((user->IsInvokeStaticOrDirect() || user->IsNewInstance()) &&
         CanMoveClinitCheck(check, user)) {
       implicit_clinit = user;
@@ -85,11 +85,12 @@
   // If we found a static invoke or new-instance for merging, remove the check
   // from dominated static invokes.
   if (implicit_clinit != nullptr) {
-    for (HUseIterator<HInstruction*> it(check->GetUses()); !it.Done(); ) {
-      HInstruction* user = it.Current()->GetUser();
+    const HUseList<HInstruction*>& uses = check->GetUses();
+    for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
+      HInstruction* user = it->GetUser();
       // All other uses must be dominated.
       DCHECK(implicit_clinit->StrictlyDominates(user) || (implicit_clinit == user));
-      it.Advance();  // Advance before we remove the node, reference to the next node is preserved.
+      ++it;  // Advance before we remove the node, reference to the next node is preserved.
       if (user->IsInvokeStaticOrDirect()) {
         user->AsInvokeStaticOrDirect()->RemoveExplicitClinitCheck(
             HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
@@ -159,7 +160,7 @@
 
 void PrepareForRegisterAllocation::VisitCondition(HCondition* condition) {
   if (condition->HasOnlyOneNonEnvironmentUse()) {
-    HInstruction* user = condition->GetUses().GetFirst()->GetUser();
+    HInstruction* user = condition->GetUses().front().GetUser();
     if (CanEmitConditionAt(condition, user)) {
       condition->MarkEmittedAtUseSite();
     }
diff --git a/compiler/optimizing/pretty_printer.h b/compiler/optimizing/pretty_printer.h
index 429e6e3..ee32518 100644
--- a/compiler/optimizing/pretty_printer.h
+++ b/compiler/optimizing/pretty_printer.h
@@ -55,13 +55,13 @@
     if (instruction->HasUses()) {
       PrintString(" [");
       bool first = true;
-      for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
+      for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
         if (first) {
           first = false;
         } else {
           PrintString(", ");
         }
-        PrintInt(it.Current()->GetUser()->GetId());
+        PrintInt(use.GetUser()->GetId());
       }
       PrintString("]");
     }
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 95f10e0..cd4391d 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -187,8 +187,8 @@
   if (existing_bound_type->GetUpperBound().IsSupertypeOf(upper_bound)) {
     if (kIsDebugBuild) {
       // Check that the existing HBoundType dominates all the uses.
-      for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
-        HInstruction* user = it.Current()->GetUser();
+      for (const HUseListNode<HInstruction*>& use : obj->GetUses()) {
+        HInstruction* user = use.GetUser();
         if (dominator_instr != nullptr) {
           DCHECK(!dominator_instr->StrictlyDominates(user)
               || user == existing_bound_type
@@ -242,8 +242,12 @@
       ? ifInstruction->IfTrueSuccessor()
       : ifInstruction->IfFalseSuccessor();
 
-  for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
-    HInstruction* user = it.Current()->GetUser();
+  const HUseList<HInstruction*>& uses = obj->GetUses();
+  for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
+    HInstruction* user = it->GetUser();
+    size_t index = it->GetIndex();
+    // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
+    ++it;
     if (notNullBlock->Dominates(user->GetBlock())) {
       if (bound_type == nullptr) {
         ScopedObjectAccess soa(Thread::Current());
@@ -264,7 +268,7 @@
           break;
         }
       }
-      user->ReplaceInput(bound_type, it.Current()->GetIndex());
+      user->ReplaceInput(bound_type, index);
     }
   }
 }
@@ -379,8 +383,12 @@
     return;
   }
   DCHECK(!obj->IsLoadClass()) << "We should not replace HLoadClass instructions";
-  for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
-    HInstruction* user = it.Current()->GetUser();
+  const HUseList<HInstruction*>& uses = obj->GetUses();
+  for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
+    HInstruction* user = it->GetUser();
+    size_t index = it->GetIndex();
+    // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
+    ++it;
     if (instanceOfTrueBlock->Dominates(user->GetBlock())) {
       if (bound_type == nullptr) {
         ScopedObjectAccess soa(Thread::Current());
@@ -396,7 +404,7 @@
           break;
         }
       }
-      user->ReplaceInput(bound_type, it.Current()->GetIndex());
+      user->ReplaceInput(bound_type, index);
     }
   }
 }
@@ -887,8 +895,8 @@
 }
 
 void ReferenceTypePropagation::AddDependentInstructionsToWorklist(HInstruction* instruction) {
-  for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
-    HInstruction* user = it.Current()->GetUser();
+  for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
+    HInstruction* user = use.GetUser();
     if ((user->IsPhi() && user->AsPhi()->IsLive())
        || user->IsBoundType()
        || user->IsNullCheck()
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 2fe2f20..600e1d3 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -108,8 +108,8 @@
   // marked dead/conflicting too, so we add them to the worklist. Otherwise we
   // add users whose type does not match and needs to be updated.
   bool add_all_live_phis = instruction->IsPhi() && instruction->AsPhi()->IsDead();
-  for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
-    HInstruction* user = it.Current()->GetUser();
+  for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
+    HInstruction* user = use.GetUser();
     if (user->IsPhi() && user->AsPhi()->IsLive()) {
       if (add_all_live_phis || user->GetType() != instruction->GetType()) {
         worklist->push_back(user->AsPhi());
@@ -412,27 +412,24 @@
 }
 
 static bool HasAliasInEnvironments(HInstruction* instruction) {
-  for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses());
-       !use_it.Done();
-       use_it.Advance()) {
-    HEnvironment* use = use_it.Current()->GetUser();
-    HUseListNode<HEnvironment*>* next = use_it.Current()->GetNext();
-    if (next != nullptr && next->GetUser() == use) {
+  HEnvironment* last_user = nullptr;
+  for (const HUseListNode<HEnvironment*>& use : instruction->GetEnvUses()) {
+    DCHECK(use.GetUser() != nullptr);
+    // Note: The first comparison (== null) always fails.
+    if (use.GetUser() == last_user) {
       return true;
     }
+    last_user = use.GetUser();
   }
 
   if (kIsDebugBuild) {
     // Do a quadratic search to ensure same environment uses are next
     // to each other.
-    for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses());
-         !use_it.Done();
-         use_it.Advance()) {
-      HUseListNode<HEnvironment*>* current = use_it.Current();
-      HUseListNode<HEnvironment*>* next = current->GetNext();
-      while (next != nullptr) {
+    const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses();
+    for (auto current = env_uses.begin(), end = env_uses.end(); current != end; ++current) {
+      auto next = current;
+      for (++next; next != end; ++next) {
         DCHECK(next->GetUser() != current->GetUser());
-        next = next->GetNext();
       }
     }
   }
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 83e9dac..d33eca7 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -283,11 +283,9 @@
       if (current->IsEmittedAtUseSite()) {
         if (kIsDebugBuild) {
           DCHECK(!current->GetLocations()->Out().IsValid());
-          for (HUseIterator<HInstruction*> use_it(current->GetUses());
-               !use_it.Done();
-               use_it.Advance()) {
-            HInstruction* user = use_it.Current()->GetUser();
-            size_t index = use_it.Current()->GetIndex();
+          for (const HUseListNode<HInstruction*>& use : current->GetUses()) {
+            HInstruction* user = use.GetUser();
+            size_t index = use.GetIndex();
             DCHECK(!user->GetLocations()->InAt(index).IsValid());
           }
           DCHECK(!current->HasEnvironmentUses());
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index 6816b6a..aeb3109 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -43,8 +43,8 @@
 
       bool keep_alive = (graph_->IsDebuggable() && phi->HasEnvironmentUses());
       if (!keep_alive) {
-        for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) {
-          if (!use_it.Current()->GetUser()->IsPhi()) {
+        for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
+          if (!use.GetUser()->IsPhi()) {
             keep_alive = true;
             break;
           }
@@ -94,9 +94,8 @@
       if (phi->IsDead()) {
         // Make sure the phi is only used by other dead phis.
         if (kIsDebugBuild) {
-          for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done();
-               use_it.Advance()) {
-            HInstruction* user = use_it.Current()->GetUser();
+          for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
+            HInstruction* user = use.GetUser();
             DCHECK(user->IsLoopHeaderPhi());
             DCHECK(user->AsPhi()->IsDead());
           }
@@ -106,11 +105,9 @@
           phi->RemoveAsUserOfInput(i);
         }
         // Remove the phi from environments that use it.
-        for (HUseIterator<HEnvironment*> use_it(phi->GetEnvUses()); !use_it.Done();
-             use_it.Advance()) {
-          HUseListNode<HEnvironment*>* user_node = use_it.Current();
-          HEnvironment* user = user_node->GetUser();
-          user->SetRawEnvAt(user_node->GetIndex(), nullptr);
+        for (const HUseListNode<HEnvironment*>& use : phi->GetEnvUses()) {
+          HEnvironment* user = use.GetUser();
+          user->SetRawEnvAt(use.GetIndex(), nullptr);
         }
         // Delete it from the instruction list.
         block->RemovePhi(phi, /*ensure_safety=*/ false);
@@ -233,9 +230,8 @@
 
       // Because we're updating the users of this phi, we may have new candidates
       // for elimination. Add phis that use this phi to the worklist.
-      for (HUseIterator<HInstruction*> it(current->GetUses()); !it.Done(); it.Advance()) {
-        HUseListNode<HInstruction*>* use = it.Current();
-        HInstruction* user = use->GetUser();
+      for (const HUseListNode<HInstruction*>& use : current->GetUses()) {
+        HInstruction* user = use.GetUser();
         if (user->IsPhi() && !ContainsElement(visited_phis_in_cycle, user->GetId())) {
           worklist_.push_back(user->AsPhi());
         }
diff --git a/compiler/utils/intrusive_forward_list.h b/compiler/utils/intrusive_forward_list.h
new file mode 100644
index 0000000..a90b0f5
--- /dev/null
+++ b/compiler/utils/intrusive_forward_list.h
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ART_COMPILER_UTILS_INTRUSIVE_FORWARD_LIST_H_
+#define ART_COMPILER_UTILS_INTRUSIVE_FORWARD_LIST_H_
+
+#include <stdint.h>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace art {
+
+struct IntrusiveForwardListHook {
+  IntrusiveForwardListHook() : next_hook(nullptr) { }
+  explicit IntrusiveForwardListHook(const IntrusiveForwardListHook* hook) : next_hook(hook) { }
+
+  // Allow copyable values but do not copy the hook, it is not part of the value.
+  IntrusiveForwardListHook(const IntrusiveForwardListHook& other ATTRIBUTE_UNUSED)
+      : next_hook(nullptr) { }
+  IntrusiveForwardListHook& operator=(const IntrusiveForwardListHook& src ATTRIBUTE_UNUSED) {
+    return *this;
+  }
+
+  mutable const IntrusiveForwardListHook* next_hook;
+};
+
+template <typename T, IntrusiveForwardListHook T::* NextPtr = &T::hook>
+class IntrusiveForwardListMemberHook;
+
+template <typename T, typename HookTraits = IntrusiveForwardListMemberHook<T>>
+class IntrusiveForwardList;
+
+template <typename T, typename HookTraits>
+class IntrusiveForwardListIterator : public std::iterator<std::forward_iterator_tag, T> {
+ public:
+  // Construct/copy/destroy (except the private constructor used by IntrusiveForwardList<>).
+  IntrusiveForwardListIterator() : hook_(nullptr) { }
+  IntrusiveForwardListIterator(const IntrusiveForwardListIterator& src) = default;
+  IntrusiveForwardListIterator& operator=(const IntrusiveForwardListIterator& src) = default;
+
+  // Conversion from iterator to const_iterator.
+  template <typename OtherT,
+            typename = typename std::enable_if<std::is_same<T, const OtherT>::value>::type>
+  IntrusiveForwardListIterator(const IntrusiveForwardListIterator<OtherT, HookTraits>& src)
+      : hook_(src.hook_) { }
+
+  // Iteration.
+  IntrusiveForwardListIterator& operator++() {
+    DCHECK(hook_ != nullptr);
+    hook_ = hook_->next_hook;
+    return *this;
+  }
+  IntrusiveForwardListIterator operator++(int) {
+    IntrusiveForwardListIterator tmp(*this);
+    ++*this;
+    return tmp;
+  }
+
+  // Dereference
+  T& operator*() const {
+    DCHECK(hook_ != nullptr);
+    return *HookTraits::GetValue(hook_);
+  }
+  T* operator->() const {
+    return &**this;
+  }
+
+ private:
+  explicit IntrusiveForwardListIterator(const IntrusiveForwardListHook* hook) : hook_(hook) { }
+
+  const IntrusiveForwardListHook* hook_;
+
+  template <typename OtherT, typename OtherTraits>
+  friend class IntrusiveForwardListIterator;
+
+  template <typename OtherT, typename OtherTraits>
+  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
+  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==(
+    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!=(
+    const IntrusiveForwardListIterator<T, HookTraits>& lhs,
+    const IntrusiveForwardListIterator<OtherT, HookTraits>& rhs) {
+  return !(lhs == rhs);
+}
+
+// Intrusive version of std::forward_list<>. See also slist<> in Boost.Intrusive.
+//
+// This class template provides the same interface as std::forward_list<> as long
+// as the functions are meaningful for an intrusive container; this excludes emplace
+// functions and functions taking an std::initializer_list<> as the container does
+// not construct elements.
+template <typename T, typename HookTraits>
+class IntrusiveForwardList {
+ public:
+  typedef HookTraits hook_traits;
+  typedef       T  value_type;
+  typedef       T& reference;
+  typedef const T& const_reference;
+  typedef       T* pointer;
+  typedef const T* const_pointer;
+  typedef IntrusiveForwardListIterator<      T, hook_traits> iterator;
+  typedef IntrusiveForwardListIterator<const T, hook_traits> const_iterator;
+
+  // Construct/copy/destroy.
+  IntrusiveForwardList() = default;
+  template <typename InputIterator>
+  IntrusiveForwardList(InputIterator first, InputIterator last) : IntrusiveForwardList() {
+    insert_after(before_begin(), first, last);
+  }
+  IntrusiveForwardList(IntrusiveForwardList&& src) : first_(src.first_.next_hook) {
+    src.first_.next_hook = nullptr;
+  }
+  IntrusiveForwardList& operator=(const IntrusiveForwardList& src) = delete;
+  IntrusiveForwardList& operator=(IntrusiveForwardList&& src) {
+    IntrusiveForwardList tmp(std::move(src));
+    tmp.swap(*this);
+    return *this;
+  }
+  ~IntrusiveForwardList() = default;
+
+  // Iterators.
+  iterator before_begin() { return iterator(&first_); }
+  const_iterator before_begin() const { return const_iterator(&first_); }
+  iterator begin() { return iterator(first_.next_hook); }
+  const_iterator begin() const { return const_iterator(first_.next_hook); }
+  iterator end() { return iterator(nullptr); }
+  const_iterator end() const { return const_iterator(nullptr); }
+  const_iterator cbefore_begin() const { return const_iterator(&first_); }
+  const_iterator cbegin() const { return const_iterator(first_.next_hook); }
+  const_iterator cend() const { return const_iterator(nullptr); }
+
+  // Capacity.
+  bool empty() const { return begin() == end(); }
+  size_t max_size() { return static_cast<size_t>(-1); }
+
+  // Element access.
+  reference front() { return *begin(); }
+  const_reference front() const { return *begin(); }
+
+  // Modifiers.
+  template <typename InputIterator>
+  void assign(InputIterator first, InputIterator last) {
+    IntrusiveForwardList tmp(first, last);
+    tmp.swap(*this);
+  }
+  void push_front(value_type& value) {
+    insert_after(before_begin(), value);
+  }
+  void pop_front() {
+    DCHECK(!empty());
+    erase_after(before_begin());
+  }
+  iterator insert_after(const_iterator position, value_type& value) {
+    const IntrusiveForwardListHook* new_hook = hook_traits::GetHook(&value);
+    new_hook->next_hook = position.hook_->next_hook;
+    position.hook_->next_hook = new_hook;
+    return iterator(new_hook);
+  }
+  template <typename InputIterator>
+  iterator insert_after(const_iterator position, InputIterator first, InputIterator last) {
+    while (first != last) {
+      position = insert_after(position, *first++);
+    }
+    return iterator(position.hook_);
+  }
+  iterator erase_after(const_iterator position) {
+    const_iterator last = position;
+    std::advance(last, 2);
+    return erase_after(position, last);
+  }
+  iterator erase_after(const_iterator position, const_iterator last) {
+    DCHECK(position != last);
+    position.hook_->next_hook = last.hook_;
+    return iterator(last.hook_);
+  }
+  void swap(IntrusiveForwardList& other) {
+    std::swap(first_.next_hook, other.first_.next_hook);
+  }
+  void clear() {
+    first_.next_hook = nullptr;
+  }
+
+  // Operations.
+  void splice_after(const_iterator position, IntrusiveForwardList& src) {
+    DCHECK(position != end());
+    splice_after(position, src, src.before_begin(), src.end());
+  }
+  void splice_after(const_iterator position, IntrusiveForwardList&& src) {
+    splice_after(position, src);  // Use l-value overload.
+  }
+  // Splice the element after `i`.
+  void splice_after(const_iterator position, IntrusiveForwardList& src, const_iterator i) {
+    const_iterator last = i;
+    std::advance(last, 2);
+    splice_after(position, src, i, last);
+  }
+  // Splice the element after `i`.
+  void splice_after(const_iterator position, IntrusiveForwardList&& src, const_iterator i) {
+    splice_after(position, src, i);  // Use l-value overload.
+  }
+  // Splice elements between `first` and `last`, i.e. open range `(first, last)`.
+  void splice_after(const_iterator position,
+                    IntrusiveForwardList& src,
+                    const_iterator first,
+                    const_iterator last) {
+    DCHECK(position != end());
+    DCHECK(first != last);
+    if (++const_iterator(first) == last) {
+      // Nothing to do.
+      return;
+    }
+    // If position is just before end() and last is src.end(), we can finish this quickly.
+    if (++const_iterator(position) == end() && last == src.end()) {
+      position.hook_->next_hook = first.hook_->next_hook;
+      first.hook_->next_hook = nullptr;
+      return;
+    }
+    // Otherwise we need to find the position before last to fix up the hook.
+    const_iterator before_last = first;
+    while (++const_iterator(before_last) != last) {
+      ++before_last;
+    }
+    // Detach (first, last).
+    const IntrusiveForwardListHook* first_taken = first.hook_->next_hook;
+    first.hook_->next_hook = last.hook_;
+    // Attach the sequence to the new position.
+    before_last.hook_->next_hook = position.hook_->next_hook;
+    position.hook_->next_hook = first_taken;
+  }
+  // Splice elements between `first` and `last`, i.e. open range `(first, last)`.
+  void splice_after(const_iterator position,
+                    IntrusiveForwardList&& src,
+                    const_iterator first,
+                    const_iterator last) {
+    splice_after(position, src, first, last);  // Use l-value overload.
+  }
+  void remove(const value_type& value) {
+    remove_if([value](const value_type& v) { return value == v; });
+  }
+  template <typename Predicate>
+  void remove_if(Predicate pred) {
+    iterator prev = before_begin();
+    for (iterator current = begin(); current != end(); ++current) {
+      if (pred(*current)) {
+        erase_after(prev);
+        current = prev;
+      } else {
+        prev = current;
+      }
+    }
+  }
+  void unique() {
+    unique(std::equal_to<value_type>());
+  }
+  template <typename BinaryPredicate>
+  void unique(BinaryPredicate pred) {
+    if (!empty()) {
+      iterator prev = begin();
+      iterator current = prev;
+      ++current;
+      for (; current != end(); ++current) {
+        if (pred(*prev, *current)) {
+          erase_after(prev);
+          current = prev;
+        } else {
+          prev = current;
+        }
+      }
+    }
+  }
+  void merge(IntrusiveForwardList& other) {
+    merge(other, std::less<value_type>());
+  }
+  void merge(IntrusiveForwardList&& other) {
+    merge(other);  // Use l-value overload.
+  }
+  template <typename Compare>
+  void merge(IntrusiveForwardList& other, Compare cmp) {
+    iterator prev = before_begin();
+    iterator current = begin();
+    iterator other_prev = other.before_begin();
+    iterator other_current = other.begin();
+    while (current != end() && other_current != other.end()) {
+      if (cmp(*other_current, *current)) {
+        ++other_current;
+        splice_after(prev, other, other_prev);
+        ++prev;
+      } else {
+        prev = current;
+        ++current;
+      }
+      DCHECK(++const_iterator(prev) == current);
+      DCHECK(++const_iterator(other_prev) == other_current);
+    }
+    splice_after(prev, other);
+  }
+  template <typename Compare>
+  void merge(IntrusiveForwardList&& other, Compare cmp) {
+    merge(other, cmp);  // Use l-value overload.
+  }
+  void sort() {
+    sort(std::less<value_type>());
+  }
+  template <typename Compare>
+  void sort(Compare cmp) {
+    size_t n = std::distance(begin(), end());
+    if (n >= 2u) {
+      const_iterator middle = before_begin();
+      std::advance(middle, n / 2u);
+      IntrusiveForwardList second_half;
+      second_half.splice_after(second_half.before_begin(), *this, middle, end());
+      sort(cmp);
+      second_half.sort(cmp);
+      merge(second_half, cmp);
+    }
+  }
+  void reverse() {
+    IntrusiveForwardList reversed;
+    while (!empty()) {
+      value_type& value = front();
+      erase_after(before_begin());
+      reversed.insert_after(reversed.before_begin(), value);
+    }
+    reversed.swap(*this);
+  }
+
+  // Extensions.
+  bool HasExactlyOneElement() const {
+    return !empty() && ++begin() == end();
+  }
+  size_t SizeSlow() const {
+    return std::distance(begin(), end());
+  }
+  bool ContainsNode(const_reference node) const {
+    for (auto&& n : *this) {
+      if (std::addressof(n) == std::addressof(node)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+ private:
+  static IntrusiveForwardListHook* ModifiableHook(const IntrusiveForwardListHook* hook) {
+    return const_cast<IntrusiveForwardListHook*>(hook);
+  }
+
+  IntrusiveForwardListHook first_;
+};
+
+template <typename T, typename HookTraits>
+void swap(IntrusiveForwardList<T, HookTraits>& lhs, IntrusiveForwardList<T, HookTraits>& rhs) {
+  lhs.swap(rhs);
+}
+
+template <typename T, typename HookTraits>
+bool operator==(const IntrusiveForwardList<T, HookTraits>& lhs,
+                const IntrusiveForwardList<T, HookTraits>& rhs) {
+  auto lit = lhs.begin();
+  auto rit = rhs.begin();
+  for (; lit != lhs.end() && rit != rhs.end(); ++lit, ++rit) {
+    if (*lit != *rit) {
+      return false;
+    }
+  }
+  return lit == lhs.end() && rit == rhs.end();
+}
+
+template <typename T, typename HookTraits>
+bool operator!=(const IntrusiveForwardList<T, HookTraits>& lhs,
+                const IntrusiveForwardList<T, HookTraits>& rhs) {
+  return !(lhs == rhs);
+}
+
+template <typename T, typename HookTraits>
+bool operator<(const IntrusiveForwardList<T, HookTraits>& lhs,
+               const IntrusiveForwardList<T, HookTraits>& rhs) {
+  return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+}
+
+template <typename T, typename HookTraits>
+bool operator>(const IntrusiveForwardList<T, HookTraits>& lhs,
+               const IntrusiveForwardList<T, HookTraits>& rhs) {
+  return rhs < lhs;
+}
+
+template <typename T, typename HookTraits>
+bool operator<=(const IntrusiveForwardList<T, HookTraits>& lhs,
+                const IntrusiveForwardList<T, HookTraits>& rhs) {
+  return !(rhs < lhs);
+}
+
+template <typename T, typename HookTraits>
+bool operator>=(const IntrusiveForwardList<T, HookTraits>& lhs,
+                const IntrusiveForwardList<T, HookTraits>& rhs) {
+  return !(lhs < rhs);
+}
+
+template <typename T, IntrusiveForwardListHook T::* NextPtr>
+class IntrusiveForwardListMemberHook {
+ public:
+  static const IntrusiveForwardListHook* GetHook(const T* value) {
+    return &(value->*NextPtr);
+  }
+
+  static T* GetValue(const IntrusiveForwardListHook* hook) {
+    return reinterpret_cast<T*>(
+        reinterpret_cast<uintptr_t>(hook) - OFFSETOF_MEMBERPTR(T, NextPtr));
+  }
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_UTILS_INTRUSIVE_FORWARD_LIST_H_
diff --git a/compiler/utils/intrusive_forward_list_test.cc b/compiler/utils/intrusive_forward_list_test.cc
new file mode 100644
index 0000000..d40ff3a
--- /dev/null
+++ b/compiler/utils/intrusive_forward_list_test.cc
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <algorithm>
+#include <forward_list>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "intrusive_forward_list.h"
+
+namespace art {
+
+struct IFLTestValue {
+  // Deliberately not explicit.
+  IFLTestValue(int v) : hook(), value(v) { }  // NOLINT(runtime/explicit)
+
+  IntrusiveForwardListHook hook;
+  int value;
+};
+
+bool operator==(const IFLTestValue& lhs, const IFLTestValue& rhs) {
+  return lhs.value == rhs.value;
+}
+
+bool operator<(const IFLTestValue& lhs, const IFLTestValue& rhs) {
+  return lhs.value < rhs.value;
+}
+
+#define ASSERT_LISTS_EQUAL(expected, value)                                   \
+  do {                                                                        \
+    ASSERT_EQ(expected.empty(), value.empty());                               \
+    ASSERT_EQ(std::distance(expected.begin(), expected.end()),                \
+              std::distance(value.begin(), value.end()));                     \
+    ASSERT_TRUE(std::equal(expected.begin(), expected.end(), value.begin())); \
+  } while (false)
+
+TEST(IntrusiveForwardList, IteratorToConstIterator) {
+  IntrusiveForwardList<IFLTestValue> ifl;
+  IntrusiveForwardList<IFLTestValue>::iterator begin = ifl.begin();
+  IntrusiveForwardList<IFLTestValue>::const_iterator cbegin = ifl.cbegin();
+  IntrusiveForwardList<IFLTestValue>::const_iterator converted_begin = begin;
+  ASSERT_TRUE(converted_begin == cbegin);
+}
+
+TEST(IntrusiveForwardList, IteratorOperators) {
+  IntrusiveForwardList<IFLTestValue> ifl;
+  ASSERT_TRUE(ifl.begin() == ifl.cbegin());
+  ASSERT_FALSE(ifl.begin() != ifl.cbegin());
+  ASSERT_TRUE(ifl.end() == ifl.cend());
+  ASSERT_FALSE(ifl.end() != ifl.cend());
+
+  ASSERT_TRUE(ifl.begin() == ifl.end());  // Empty.
+  ASSERT_FALSE(ifl.begin() != ifl.end());  // Empty.
+
+  IFLTestValue value(1);
+  ifl.insert_after(ifl.cbefore_begin(), value);
+
+  ASSERT_FALSE(ifl.begin() == ifl.end());  // Not empty.
+  ASSERT_TRUE(ifl.begin() != ifl.end());  // Not empty.
+}
+
+TEST(IntrusiveForwardList, ConstructRange) {
+  std::forward_list<int> ref({ 1, 2, 7 });
+  std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+  IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+}
+
+TEST(IntrusiveForwardList, Assign) {
+  std::forward_list<int> ref1({ 2, 8, 5 });
+  std::vector<IFLTestValue> storage1(ref1.begin(), ref1.end());
+  IntrusiveForwardList<IFLTestValue> ifl;
+  ifl.assign(storage1.begin(), storage1.end());
+  ASSERT_LISTS_EQUAL(ref1, ifl);
+  std::forward_list<int> ref2({ 7, 1, 3 });
+  std::vector<IFLTestValue> storage2(ref2.begin(), ref2.end());
+  ifl.assign(storage2.begin(), storage2.end());
+  ASSERT_LISTS_EQUAL(ref2, ifl);
+}
+
+TEST(IntrusiveForwardList, PushPop) {
+  IFLTestValue value3(3);
+  IFLTestValue value7(7);
+  std::forward_list<int> ref;
+  IntrusiveForwardList<IFLTestValue> ifl;
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  ref.push_front(3);
+  ifl.push_front(value3);
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  ASSERT_EQ(3, ifl.front());
+  ref.push_front(7);
+  ifl.push_front(value7);
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  ASSERT_EQ(7, ifl.front());
+  ref.pop_front();
+  ifl.pop_front();
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  ASSERT_EQ(3, ifl.front());
+  ref.pop_front();
+  ifl.pop_front();
+  ASSERT_LISTS_EQUAL(ref, ifl);
+}
+
+TEST(IntrusiveForwardList, InsertAfter1) {
+  IFLTestValue value4(4);
+  IFLTestValue value8(8);
+  IFLTestValue value5(5);
+  IFLTestValue value3(3);
+  std::forward_list<int> ref;
+  IntrusiveForwardList<IFLTestValue> ifl;
+
+  auto ref_it = ref.insert_after(ref.before_begin(), 4);
+  auto ifl_it = ifl.insert_after(ifl.before_begin(), value4);
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  ASSERT_EQ(*ref_it, *ifl_it);
+  CHECK(ref_it == ref.begin());
+  ASSERT_TRUE(ifl_it == ifl.begin());
+
+  ref_it = ref.insert_after(ref.begin(), 8);
+  ifl_it = ifl.insert_after(ifl.begin(), value8);
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  ASSERT_EQ(*ref_it, *ifl_it);
+  CHECK(ref_it != ref.end());
+  ASSERT_TRUE(ifl_it != ifl.end());
+  CHECK(++ref_it == ref.end());
+  ASSERT_TRUE(++ifl_it == ifl.end());
+
+  ref_it = ref.insert_after(ref.begin(), 5);
+  ifl_it = ifl.insert_after(ifl.begin(), value5);
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  ASSERT_EQ(*ref_it, *ifl_it);
+
+  ref_it = ref.insert_after(ref_it, 3);
+  ifl_it = ifl.insert_after(ifl_it, value3);
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  ASSERT_EQ(*ref_it, *ifl_it);
+}
+
+TEST(IntrusiveForwardList, InsertAfter2) {
+  std::forward_list<int> ref;
+  IntrusiveForwardList<IFLTestValue> ifl;
+
+  auto ref_it = ref.insert_after(ref.before_begin(), { 2, 8, 5 });
+  std::vector<IFLTestValue> storage1({ { 2 }, { 8 }, { 5 } });
+  auto ifl_it = ifl.insert_after(ifl.before_begin(), storage1.begin(), storage1.end());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  ASSERT_EQ(*ref_it, *ifl_it);
+
+  std::vector<IFLTestValue> storage2({ { 7 }, { 2 } });
+  ref_it = ref.insert_after(ref.begin(), { 7, 2 });
+  ifl_it = ifl.insert_after(ifl.begin(), storage2.begin(), storage2.end());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  ASSERT_EQ(*ref_it, *ifl_it);
+
+  std::vector<IFLTestValue> storage3({ { 1 }, { 3 }, { 4 }, { 9 } });
+  ref_it = ref.begin();
+  ifl_it = ifl.begin();
+  std::advance(ref_it, std::distance(ref.begin(), ref.end()) - 1);
+  std::advance(ifl_it, std::distance(ifl.begin(), ifl.end()) - 1);
+  ref_it = ref.insert_after(ref_it, { 1, 3, 4, 9 });
+  ifl_it = ifl.insert_after(ifl_it, storage3.begin(), storage3.end());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+}
+
+TEST(IntrusiveForwardList, EraseAfter1) {
+  std::forward_list<int> ref({ 1, 2, 7, 4, 5 });
+  std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+  IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  CHECK_EQ(std::distance(ref.begin(), ref.end()), 5);
+
+  auto ref_it = ref.begin();
+  auto ifl_it = ifl.begin();
+  std::advance(ref_it, 2);
+  std::advance(ifl_it, 2);
+  ref_it = ref.erase_after(ref_it);
+  ifl_it = ifl.erase_after(ifl_it);
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  CHECK_EQ(std::distance(ref.begin(), ref.end()), 4);
+  CHECK(ref_it != ref.end());
+  ASSERT_TRUE(ifl_it != ifl.end());
+  CHECK(++ref_it == ref.end());
+  ASSERT_TRUE(++ifl_it == ifl.end());
+
+  ref_it = ref.begin();
+  ifl_it = ifl.begin();
+  std::advance(ref_it, 2);
+  std::advance(ifl_it, 2);
+  ref_it = ref.erase_after(ref_it);
+  ifl_it = ifl.erase_after(ifl_it);
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  CHECK_EQ(std::distance(ref.begin(), ref.end()), 3);
+  CHECK(ref_it == ref.end());
+  ASSERT_TRUE(ifl_it == ifl.end());
+
+  ref_it = ref.erase_after(ref.begin());
+  ifl_it = ifl.erase_after(ifl.begin());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  CHECK_EQ(std::distance(ref.begin(), ref.end()), 2);
+  CHECK(ref_it != ref.end());
+  ASSERT_TRUE(ifl_it != ifl.end());
+  CHECK(++ref_it == ref.end());
+  ASSERT_TRUE(++ifl_it == ifl.end());
+
+  ref_it = ref.erase_after(ref.before_begin());
+  ifl_it = ifl.erase_after(ifl.before_begin());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  CHECK_EQ(std::distance(ref.begin(), ref.end()), 1);
+  CHECK(ref_it == ref.begin());
+  ASSERT_TRUE(ifl_it == ifl.begin());
+
+  ref_it = ref.erase_after(ref.before_begin());
+  ifl_it = ifl.erase_after(ifl.before_begin());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  CHECK_EQ(std::distance(ref.begin(), ref.end()), 0);
+  CHECK(ref_it == ref.begin());
+  ASSERT_TRUE(ifl_it == ifl.begin());
+}
+
+TEST(IntrusiveForwardList, EraseAfter2) {
+  std::forward_list<int> ref({ 1, 2, 7, 4, 5, 3, 2, 8, 9 });
+  std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+  IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  CHECK_EQ(std::distance(ref.begin(), ref.end()), 9);
+
+  auto ref_it = ref.begin();
+  auto ifl_it = ifl.begin();
+  std::advance(ref_it, 3);
+  std::advance(ifl_it, 3);
+  ref_it = ref.erase_after(ref.begin(), ref_it);
+  ifl_it = ifl.erase_after(ifl.begin(), ifl_it);
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  ASSERT_EQ(std::distance(ref.begin(), ref_it), std::distance(ifl.begin(), ifl_it));
+  CHECK_EQ(std::distance(ref.begin(), ref.end()), 7);
+
+  ref_it = ref.erase_after(ref_it, ref.end());
+  ifl_it = ifl.erase_after(ifl_it, ifl.end());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  CHECK(ref_it == ref.end());
+  ASSERT_TRUE(ifl_it == ifl.end());
+  CHECK_EQ(std::distance(ref.begin(), ref.end()), 2);
+
+  ref_it = ref.erase_after(ref.before_begin(), ref.end());
+  ifl_it = ifl.erase_after(ifl.before_begin(), ifl.end());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  CHECK(ref_it == ref.end());
+  ASSERT_TRUE(ifl_it == ifl.end());
+  CHECK_EQ(std::distance(ref.begin(), ref.end()), 0);
+}
+
+TEST(IntrusiveForwardList, SwapClear) {
+  std::forward_list<int> ref1({ 1, 2, 7 });
+  std::vector<IFLTestValue> storage1(ref1.begin(), ref1.end());
+  IntrusiveForwardList<IFLTestValue> ifl1(storage1.begin(), storage1.end());
+  std::forward_list<int> ref2({ 3, 8, 6 });
+  std::vector<IFLTestValue> storage2(ref2.begin(), ref2.end());
+  IntrusiveForwardList<IFLTestValue> ifl2(storage2.begin(), storage2.end());
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+  ASSERT_LISTS_EQUAL(ref2, ifl2);
+  ref1.swap(ref2);
+  ifl1.swap(ifl2);
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+  ASSERT_LISTS_EQUAL(ref2, ifl2);
+  ref1.clear();
+  ifl1.clear();
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+  ASSERT_LISTS_EQUAL(ref2, ifl2);
+  swap(ref1, ref2);
+  swap(ifl1, ifl2);
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+  ASSERT_LISTS_EQUAL(ref2, ifl2);
+  ref1.clear();
+  ifl1.clear();
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+  ASSERT_LISTS_EQUAL(ref2, ifl2);
+}
+
+TEST(IntrusiveForwardList, SpliceAfter) {
+  std::forward_list<int> ref1({ 3, 1, 2, 7, 4, 5, 4, 8, 7 });
+  std::forward_list<int> ref2;
+  std::vector<IFLTestValue> storage(ref1.begin(), ref1.end());
+  IntrusiveForwardList<IFLTestValue> ifl1(storage.begin(), storage.end());
+  IntrusiveForwardList<IFLTestValue> ifl2;
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+  ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+  // Move everything to ref2/ifl2.
+  ref2.splice_after(ref2.before_begin(), ref1);
+  ifl2.splice_after(ifl2.before_begin(), ifl1);
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+  ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+  // Move first element (3) to ref1/ifl1.
+  ref1.splice_after(ref1.before_begin(), ref2, ref2.before_begin());
+  ifl1.splice_after(ifl1.before_begin(), ifl2, ifl2.before_begin());
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+  ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+  // Move second element (2) to ref1/ifl1 after the first element (3).
+  ref1.splice_after(ref1.begin(), ref2, ref2.begin());
+  ifl1.splice_after(ifl1.begin(), ifl2, ifl2.begin());
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+  ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+  // Move everything from ref2/ifl2 between the 2 elements now in ref1/ifl1.
+  ref1.splice_after(ref1.begin(), ref2);
+  ifl1.splice_after(ifl1.begin(), ifl2);
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+  ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+  std::forward_list<int> check({ 3, 1, 7, 4, 5, 4, 8, 7, 2 });
+  ASSERT_LISTS_EQUAL(check, ifl1);
+  ASSERT_TRUE(ifl2.empty());
+
+  // Empty splice_after().
+  ref2.splice_after(
+      ref2.before_begin(), ref1, ref1.before_begin(), ref1.begin());
+  ifl2.splice_after(ifl2.before_begin(), ifl1, ifl1.before_begin(), ifl1.begin());
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+  ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+  // Move { 1, 7 } to ref2/ifl2.
+  auto ref_it = ref1.begin();
+  auto ifl_it = ifl1.begin();
+  std::advance(ref_it, 3);
+  std::advance(ifl_it, 3);
+  ref2.splice_after(ref2.before_begin(), ref1, ref1.begin(), ref_it);
+  ifl2.splice_after(ifl2.before_begin(), ifl1, ifl1.begin(), ifl_it);
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+  ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+  // Move { 8, 7, 2 } to the beginning of ref1/ifl1.
+  ref_it = ref1.begin();
+  ifl_it = ifl1.begin();
+  std::advance(ref_it, 3);
+  std::advance(ifl_it, 3);
+  ref1.splice_after(ref1.before_begin(), ref1, ref_it, ref1.end());
+  ifl1.splice_after(ifl1.before_begin(), ifl1, ifl_it, ifl1.end());
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+
+  check.assign({ 8, 7, 2, 3, 4, 5, 4 });
+  ASSERT_LISTS_EQUAL(check, ifl1);
+  check.assign({ 1, 7 });
+  ASSERT_LISTS_EQUAL(check, ifl2);
+
+  // Move all but the first element to ref2/ifl2.
+  ref_it = ref2.begin();
+  ifl_it = ifl2.begin();
+  std::advance(ref_it, 1);
+  std::advance(ifl_it, 1);
+  ref2.splice_after(ref_it, ref1, ref1.begin(), ref1.end());
+  ifl2.splice_after(ifl_it, ifl1, ifl1.begin(), ifl1.end());
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+  ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+  check.assign({8});
+  ASSERT_LISTS_EQUAL(check, ifl1);
+  check.assign({ 1, 7, 7, 2, 3, 4, 5, 4 });
+  ASSERT_LISTS_EQUAL(check, ifl2);
+}
+
+TEST(IntrusiveForwardList, Remove) {
+  std::forward_list<int> ref({ 3, 1, 2, 7, 4, 5, 4, 8, 7 });
+  std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+  IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  ref.remove(1);
+  ifl.remove(1);
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  ref.remove(4);
+  ifl.remove(4);
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  auto odd = [](IFLTestValue value) { return (value.value & 1) != 0; };  // NOLINT(readability/braces)
+  ref.remove_if(odd);
+  ifl.remove_if(odd);
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  auto all = [](IFLTestValue value ATTRIBUTE_UNUSED) { return true; };  // NOLINT(readability/braces)
+  ref.remove_if(all);
+  ifl.remove_if(all);
+  ASSERT_LISTS_EQUAL(ref, ifl);
+}
+
+TEST(IntrusiveForwardList, Unique) {
+  std::forward_list<int> ref({ 3, 1, 1, 2, 3, 3, 7, 7, 4, 4, 5, 7 });
+  std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+  IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  ref.unique();
+  ifl.unique();
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  std::forward_list<int> check({ 3, 1, 2, 3, 7, 4, 5, 7 });
+  ASSERT_LISTS_EQUAL(check, ifl);
+
+  auto bin_pred = [](IFLTestValue lhs, IFLTestValue rhs) {
+    return (lhs.value & ~1) == (rhs.value & ~1);
+  };
+  ref.unique(bin_pred);
+  ifl.unique(bin_pred);
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  check.assign({ 3, 1, 2, 7, 4, 7 });
+  ASSERT_LISTS_EQUAL(check, ifl);
+}
+
+TEST(IntrusiveForwardList, Merge) {
+  std::forward_list<int> ref1({ 1, 4, 8, 8, 12 });
+  std::vector<IFLTestValue> storage1(ref1.begin(), ref1.end());
+  IntrusiveForwardList<IFLTestValue> ifl1(storage1.begin(), storage1.end());
+  std::forward_list<int> ref2({ 3, 5, 6, 7, 9 });
+  std::vector<IFLTestValue> storage2(ref2.begin(), ref2.end());
+  IntrusiveForwardList<IFLTestValue> ifl2(storage2.begin(), storage2.end());
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+  ASSERT_LISTS_EQUAL(ref2, ifl2);
+  CHECK(std::is_sorted(ref1.begin(), ref1.end()));
+  CHECK(std::is_sorted(ref2.begin(), ref2.end()));
+  ref1.merge(ref2);
+  ifl1.merge(ifl2);
+  ASSERT_LISTS_EQUAL(ref1, ifl1);
+  ASSERT_LISTS_EQUAL(ref2, ifl2);
+  CHECK(ref2.empty());
+  std::forward_list<int> check({ 1, 3, 4, 5, 6, 7, 8, 8, 9, 12 });
+  ASSERT_LISTS_EQUAL(check, ifl1);
+}
+
+TEST(IntrusiveForwardList, Sort1) {
+  std::forward_list<int> ref({ 2, 9, 8, 3, 7, 4, 1, 5, 3, 0 });
+  std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+  IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  CHECK(!std::is_sorted(ref.begin(), ref.end()));
+  ref.sort();
+  ifl.sort();
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  std::forward_list<int> check({ 0, 1, 2, 3, 3, 4, 5, 7, 8, 9 });
+  ASSERT_LISTS_EQUAL(check, ifl);
+}
+
+TEST(IntrusiveForwardList, Sort2) {
+  std::forward_list<int> ref({ 2, 9, 8, 3, 7, 4, 1, 5, 3, 0 });
+  std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+  IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  auto cmp = [](IFLTestValue lhs, IFLTestValue rhs) {
+    return (lhs.value & ~1) < (rhs.value & ~1);
+  };
+  CHECK(!std::is_sorted(ref.begin(), ref.end(), cmp));
+  ref.sort(cmp);
+  ifl.sort(cmp);
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  std::forward_list<int> check({ 1, 0, 2, 3, 3, 4, 5, 7, 9, 8 });
+  ASSERT_LISTS_EQUAL(check, ifl);
+}
+
+TEST(IntrusiveForwardList, Reverse) {
+  std::forward_list<int> ref({ 8, 3, 5, 4, 1, 3 });
+  std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+  IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  CHECK(!std::is_sorted(ref.begin(), ref.end()));
+  ref.reverse();
+  ifl.reverse();
+  ASSERT_LISTS_EQUAL(ref, ifl);
+  std::forward_list<int> check({ 3, 1, 4, 5, 3, 8 });
+  ASSERT_LISTS_EQUAL(check, ifl);
+}
+
+}  // namespace art
diff --git a/runtime/base/macros.h b/runtime/base/macros.h
index dc692d2..7a293c7 100644
--- a/runtime/base/macros.h
+++ b/runtime/base/macros.h
@@ -138,10 +138,10 @@
 #define SIZEOF_MEMBER(t, f) sizeof((reinterpret_cast<t*>(4096))->f)
 
 #define OFFSETOF_MEMBER(t, f) \
-  (reinterpret_cast<const char*>(&reinterpret_cast<t*>(16)->f) - reinterpret_cast<const char*>(16)) // NOLINT
+  (reinterpret_cast<uintptr_t>(&reinterpret_cast<t*>(16)->f) - static_cast<uintptr_t>(16u)) // NOLINT
 
-#define OFFSETOF_VOLATILE_MEMBER(t, f) \
-  (reinterpret_cast<volatile char*>(&reinterpret_cast<t*>(16)->f) - reinterpret_cast<volatile char*>(16)) // NOLINT
+#define OFFSETOF_MEMBERPTR(t, f) \
+  (reinterpret_cast<uintptr_t>(&(reinterpret_cast<t*>(16)->*f)) - static_cast<uintptr_t>(16)) // NOLINT
 
 #define PACKED(x) __attribute__ ((__aligned__(x), __packed__))
 
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index c718466..04ba8df 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -2286,16 +2286,16 @@
 // Test the offset computation of JNIEnvExt offsets. b/26071368.
 TEST_F(JniInternalTest, JNIEnvExtOffsets) {
   EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie),
-            JNIEnvExt::LocalRefCookieOffset(sizeof(void*)).Int32Value());
+            JNIEnvExt::LocalRefCookieOffset(sizeof(void*)).Uint32Value());
 
-  EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, self), JNIEnvExt::SelfOffset(sizeof(void*)).Int32Value());
+  EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, self), JNIEnvExt::SelfOffset(sizeof(void*)).Uint32Value());
 
   // segment_state_ is private in the IndirectReferenceTable. So this test isn't as good as we'd
   // hope it to be.
-  int32_t segment_state_now =
+  uint32_t segment_state_now =
       OFFSETOF_MEMBER(JNIEnvExt, locals) +
-      IndirectReferenceTable::SegmentStateOffset(sizeof(void*)).Int32Value();
-  int32_t segment_state_computed = JNIEnvExt::SegmentStateOffset(sizeof(void*)).Int32Value();
+      IndirectReferenceTable::SegmentStateOffset(sizeof(void*)).Uint32Value();
+  uint32_t segment_state_computed = JNIEnvExt::SegmentStateOffset(sizeof(void*)).Uint32Value();
   EXPECT_EQ(segment_state_now, segment_state_computed);
 }