Merge "ART: Use cerr for host unexpected errors"
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e2f8d92..414a43c 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -472,7 +472,8 @@
       ? cls->FindDeclaredDirectMethod(method_name, signature, image_size)
       : cls->FindDeclaredVirtualMethod(method_name, signature, image_size);
   if (method == nullptr) {
-    LOG(FATAL) << "Could not find method of intrinsic " << class_name << method_name << signature;
+    LOG(FATAL) << "Could not find method of intrinsic "
+               << class_name << " " << method_name << " " << signature;
   }
   DCHECK_EQ(method->GetInvokeType(), invoke_type);
   method->SetIntrinsic(static_cast<uint32_t>(intrinsic));
@@ -497,11 +498,13 @@
     // those compilations will pick up a boot image that have the ArtMethod already
     // set with the intrinsics flag.
     ScopedObjectAccess soa(Thread::Current());
-#define OPTIMIZING_INTRINSICS(Name, InvokeType, NeedsEnvironmentOrCache, SideEffects, Exceptions, ClassName, MethodName, Signature) \
+#define SETUP_INTRINSICS(Name, InvokeType, NeedsEnvironmentOrCache, SideEffects, Exceptions, \
+                         ClassName, MethodName, Signature) \
   SetupIntrinsic(soa.Self(), Intrinsics::k##Name, InvokeType, ClassName, MethodName, Signature);
 #include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+INTRINSICS_LIST(SETUP_INTRINSICS)
 #undef INTRINSICS_LIST
+#undef SETUP_INTRINSICS
   }
   // Compile:
   // 1) Compile all classes and methods enabled for compilation. May fall back to dex-to-dex
diff --git a/compiler/intrinsics_list.h b/compiler/intrinsics_list.h
index 5877f57..b617387 100644
--- a/compiler/intrinsics_list.h
+++ b/compiler/intrinsics_list.h
@@ -1,7 +1,7 @@
 /*
- * Copyright (C, "", "", "") 2015 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License", "", "", "");
+ * 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
  *
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 36e2527..0cf5f18 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -401,7 +401,6 @@
     ScopedCheckHandleScope top_handle_scope_check;  \
     SCOPED_TRACE("Normal JNI with generic");     \
     gCurrentJni = static_cast<uint32_t>(JniKind::kNormal); \
-    TEST_DISABLED_FOR_MIPS();                    \
     SetCheckGenericJni(true);                    \
     TestName ## Impl();                          \
   }
@@ -420,7 +419,6 @@
     ScopedCheckHandleScope top_handle_scope_check;  \
     SCOPED_TRACE("@FastNative JNI with generic");  \
     gCurrentJni = static_cast<uint32_t>(JniKind::kFast); \
-    TEST_DISABLED_FOR_MIPS();                    \
     SetCheckGenericJni(true);                    \
     TestName ## Impl();                          \
   }
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index aa3f268..adfe09b 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -343,14 +343,7 @@
     for (i.Advance(); !i.Done(); i.Advance()) {
       HInstruction* inst = i.Current();
       DCHECK(!inst->IsControlFlow());
-      if (!inst->HasSideEffects()
-          && !inst->CanThrow()
-          && !inst->IsSuspendCheck()
-          && !inst->IsNativeDebugInfo()
-          // If we added an explicit barrier then we should keep it.
-          && !inst->IsMemoryBarrier()
-          && !inst->IsParameterValue()
-          && !inst->HasUses()) {
+      if (inst->IsDeadAndRemovable()) {
         block->RemoveInstruction(inst);
         MaybeRecordStat(MethodCompilationStat::kRemovedDeadInstruction);
       }
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index 63850b3..df31e81 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -131,6 +131,14 @@
    */
   void Replace(HInstruction* instruction, HInstruction* fetch, HInstruction* replacement);
 
+  /**
+   * Incrementally updates induction information for just the given loop.
+   */
+  void ReVisit(HLoopInformation* loop) {
+    induction_analysis_->induction_.erase(loop);
+    induction_analysis_->VisitLoop(loop);
+  }
+
  private:
   /*
    * Enum used in IsConstant() request.
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 93c6c20..33fa87d 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -69,33 +69,6 @@
       i->GetNext() != nullptr && i->GetNext()->IsGoto();
 }
 
-static HBasicBlock* TryRemovePreHeader(HBasicBlock* preheader, HBasicBlock* entry_block) {
-  if (preheader->GetPredecessors().size() == 1) {
-    HBasicBlock* entry = preheader->GetSinglePredecessor();
-    HInstruction* anchor = entry->GetLastInstruction();
-    // If the pre-header has a single predecessor we can remove it too if
-    // either the pre-header just contains a goto, or if the predecessor
-    // is not the entry block so we can push instructions backward
-    // (moving computation into the entry block is too dangerous!).
-    if (preheader->GetFirstInstruction() == nullptr ||
-        preheader->GetFirstInstruction()->IsGoto() ||
-        (entry != entry_block && anchor->IsGoto())) {
-      // Push non-goto statements backward to empty the pre-header.
-      for (HInstructionIterator it(preheader->GetInstructions()); !it.Done(); it.Advance()) {
-        HInstruction* instruction = it.Current();
-        if (!instruction->IsGoto()) {
-          if (!instruction->CanBeMoved()) {
-            return nullptr;  // pushing failed to move all
-          }
-          it.Current()->MoveBefore(anchor);
-        }
-      }
-      return entry;
-    }
-  }
-  return nullptr;
-}
-
 static void RemoveFromCycle(HInstruction* instruction) {
   // A bit more elaborate than the usual instruction removal,
   // since there may be a cycle in the use structure.
@@ -115,7 +88,8 @@
       loop_allocator_(nullptr),
       top_loop_(nullptr),
       last_loop_(nullptr),
-      iset_(nullptr) {
+      iset_(nullptr),
+      induction_simplication_count_(0) {
 }
 
 void HLoopOptimization::Run() {
@@ -211,11 +185,17 @@
 
 void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) {
   for ( ; node != nullptr; node = node->next) {
+    int current_induction_simplification_count = induction_simplication_count_;
     if (node->inner != nullptr) {
       TraverseLoopsInnerToOuter(node->inner);
     }
-    // Visit loop after its inner loops have been visited.
+    // Visit loop after its inner loops have been visited. If the induction of any inner
+    // loop has been simplified, recompute the induction information of this loop first.
+    if (current_induction_simplification_count != induction_simplication_count_) {
+      induction_range_.ReVisit(node->loop_info);
+    }
     SimplifyInduction(node);
+    SimplifyBlocks(node);
     RemoveIfEmptyLoop(node);
   }
 }
@@ -233,11 +213,41 @@
     iset_->clear();
     int32_t use_count = 0;
     if (IsPhiInduction(phi, iset_) &&
-        IsOnlyUsedAfterLoop(*node->loop_info, phi, &use_count) &&
+        IsOnlyUsedAfterLoop(node->loop_info, phi, &use_count) &&
         TryReplaceWithLastValue(phi, use_count, preheader)) {
       for (HInstruction* i : *iset_) {
         RemoveFromCycle(i);
       }
+      induction_simplication_count_++;
+    }
+  }
+}
+
+void HLoopOptimization::SimplifyBlocks(LoopNode* node) {
+  for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) {
+    HBasicBlock* block = it.Current();
+    // Remove instructions that are dead, usually resulting from eliminating induction cycles.
+    for (HBackwardInstructionIterator i(block->GetInstructions()); !i.Done(); i.Advance()) {
+      HInstruction* instruction = i.Current();
+      if (instruction->IsDeadAndRemovable()) {
+        block->RemoveInstruction(instruction);
+      }
+    }
+    // Remove trivial control flow blocks from the loop body, again usually resulting
+    // from eliminating induction cycles.
+    if (block->GetPredecessors().size() == 1 &&
+        block->GetSuccessors().size() == 1 &&
+        block->GetFirstInstruction()->IsGoto()) {
+      HBasicBlock* pred = block->GetSinglePredecessor();
+      HBasicBlock* succ = block->GetSingleSuccessor();
+      if (succ->GetPredecessors().size() == 1) {
+        pred->ReplaceSuccessor(block, succ);
+        block->ClearDominanceInformation();
+        block->SetDominator(pred);  // needed by next disconnect.
+        block->DisconnectAndDelete();
+        pred->AddDominatedBlock(succ);
+        succ->SetDominator(pred);
+      }
     }
   }
 }
@@ -272,41 +282,31 @@
   int32_t use_count = 0;
   if (IsEmptyHeader(header, iset_) &&
       IsEmptyBody(body, iset_) &&
-      IsOnlyUsedAfterLoop(*node->loop_info, header->GetFirstPhi(), &use_count) &&
+      IsOnlyUsedAfterLoop(node->loop_info, header->GetFirstPhi(), &use_count) &&
       TryReplaceWithLastValue(header->GetFirstPhi(), use_count, preheader)) {
-    HBasicBlock* entry = TryRemovePreHeader(preheader, graph_->GetEntryBlock());
     body->DisconnectAndDelete();
     exit->RemovePredecessor(header);
     header->RemoveSuccessor(exit);
     header->ClearDominanceInformation();
     header->SetDominator(preheader);  // needed by next disconnect.
     header->DisconnectAndDelete();
-    // If allowed, remove preheader too, which may expose next outer empty loop
-    // Otherwise, link preheader directly to exit to restore the flow graph.
-    if (entry != nullptr) {
-      entry->ReplaceSuccessor(preheader, exit);
-      entry->AddDominatedBlock(exit);
-      exit->SetDominator(entry);
-      preheader->DisconnectAndDelete();
-    } else {
-      preheader->AddSuccessor(exit);
-      preheader->AddInstruction(new (graph_->GetArena()) HGoto());  // global allocator
-      preheader->AddDominatedBlock(exit);
-      exit->SetDominator(preheader);
-    }
+    preheader->AddSuccessor(exit);
+    preheader->AddInstruction(new (graph_->GetArena()) HGoto());  // global allocator
+    preheader->AddDominatedBlock(exit);
+    exit->SetDominator(preheader);
     // Update hierarchy.
     RemoveLoop(node);
   }
 }
 
-bool HLoopOptimization::IsOnlyUsedAfterLoop(const HLoopInformation& loop_info,
+bool HLoopOptimization::IsOnlyUsedAfterLoop(HLoopInformation* loop_info,
                                             HInstruction* instruction,
                                             /*out*/ int32_t* use_count) {
   for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
     HInstruction* user = use.GetUser();
     if (iset_->find(user) == iset_->end()) {  // not excluded?
       HLoopInformation* other_loop_info = user->GetBlock()->GetLoopInformation();
-      if (other_loop_info != nullptr && other_loop_info->IsIn(loop_info)) {
+      if (other_loop_info != nullptr && other_loop_info->IsIn(*loop_info)) {
         return false;
       }
       ++*use_count;
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index b2bf1c8..9c4b462 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -46,7 +46,7 @@
           inner(nullptr),
           previous(nullptr),
           next(nullptr) {}
-    const HLoopInformation* const loop_info;
+    HLoopInformation* const loop_info;
     LoopNode* outer;
     LoopNode* inner;
     LoopNode* previous;
@@ -61,9 +61,10 @@
   void TraverseLoopsInnerToOuter(LoopNode* node);
 
   void SimplifyInduction(LoopNode* node);
+  void SimplifyBlocks(LoopNode* node);
   void RemoveIfEmptyLoop(LoopNode* node);
 
-  bool IsOnlyUsedAfterLoop(const HLoopInformation& loop_info,
+  bool IsOnlyUsedAfterLoop(HLoopInformation* loop_info,
                            HInstruction* instruction,
                            /*out*/ int32_t* use_count);
   void ReplaceAllUses(HInstruction* instruction, HInstruction* replacement);
@@ -87,6 +88,11 @@
   // Contents reside in phase-local heap memory.
   ArenaSet<HInstruction*>* iset_;
 
+  // Counter that tracks how many induction cycles have been simplified. Useful
+  // to trigger incremental updates of induction variable analysis of outer loops
+  // when the induction of inner loops has changed.
+  int32_t induction_simplication_count_;
+
   friend class LoopOptimizationTest;
 
   DISALLOW_COPY_AND_ASSIGN(HLoopOptimization);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 828c0e5..348f99d 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1931,6 +1931,19 @@
     return !HasEnvironmentUses() && GetUses().HasExactlyOneElement();
   }
 
+  bool IsDeadAndRemovable() const {
+    return
+        !HasSideEffects() &&
+        !CanThrow() &&
+        !IsSuspendCheck() &&
+        !IsControlFlow() &&
+        !IsNativeDebugInfo() &&
+        !IsParameterValue() &&
+        !HasUses() &&
+        // If we added an explicit barrier then we should keep it.
+        !IsMemoryBarrier();
+  }
+
   // Does this instruction strictly dominate `other_instruction`?
   // Returns false if this instruction and `other_instruction` are the same.
   // Aborts if this instruction and `other_instruction` are both phis.
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index e1f9ba2..2d9149a 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -18,6 +18,7 @@
     defaults: ["art_defaults"],
     host_supported: true,
     srcs: ["events.cc",
+           "object_tagging.cc",
            "OpenjdkJvmTi.cc",
            "transform.cc"],
     include_dirs: ["art/runtime"],
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 6812a92..36be2a0 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -54,8 +54,8 @@
 
 namespace openjdkjvmti {
 
-ObjectTagTable gObjectTagTable;
 EventHandler gEventHandler;
+ObjectTagTable gObjectTagTable(&gEventHandler);
 
 class JvmtiFunctions {
  private:
diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc
new file mode 100644
index 0000000..bb17cac
--- /dev/null
+++ b/runtime/openjdkjvmti/object_tagging.cc
@@ -0,0 +1,127 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "object_tagging.h"
+
+#include "art_jvmti.h"
+#include "base/logging.h"
+#include "events-inl.h"
+#include "gc/allocation_listener.h"
+#include "instrumentation.h"
+#include "jni_env_ext-inl.h"
+#include "mirror/class.h"
+#include "mirror/object.h"
+#include "runtime.h"
+#include "ScopedLocalRef.h"
+
+namespace openjdkjvmti {
+
+void ObjectTagTable::Add(art::mirror::Object* obj, jlong tag) {
+  art::Thread* self = art::Thread::Current();
+  art::MutexLock mu(self, allow_disallow_lock_);
+  Wait(self);
+
+  if (first_free_ == tagged_objects_.size()) {
+    tagged_objects_.push_back(Entry(art::GcRoot<art::mirror::Object>(obj), tag));
+    first_free_++;
+  } else {
+    DCHECK_LT(first_free_, tagged_objects_.size());
+    DCHECK(tagged_objects_[first_free_].first.IsNull());
+    tagged_objects_[first_free_] = Entry(art::GcRoot<art::mirror::Object>(obj), tag);
+    // TODO: scan for free elements.
+    first_free_ = tagged_objects_.size();
+  }
+}
+
+bool ObjectTagTable::Remove(art::mirror::Object* obj, jlong* tag) {
+  art::Thread* self = art::Thread::Current();
+  art::MutexLock mu(self, allow_disallow_lock_);
+  Wait(self);
+
+  for (auto it = tagged_objects_.begin(); it != tagged_objects_.end(); ++it) {
+    if (it->first.Read(nullptr) == obj) {
+      if (tag != nullptr) {
+        *tag = it->second;
+      }
+      it->first = art::GcRoot<art::mirror::Object>(nullptr);
+
+      size_t index = it - tagged_objects_.begin();
+      if (index < first_free_) {
+        first_free_ = index;
+      }
+
+      // TODO: compaction.
+
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void ObjectTagTable::Sweep(art::IsMarkedVisitor* visitor) {
+  if (event_handler_->IsEventEnabledAnywhere(JVMTI_EVENT_OBJECT_FREE)) {
+    SweepImpl<true>(visitor);
+  } else {
+    SweepImpl<false>(visitor);
+  }
+}
+
+template <bool kHandleNull>
+void ObjectTagTable::SweepImpl(art::IsMarkedVisitor* visitor) {
+  art::Thread* self = art::Thread::Current();
+  art::MutexLock mu(self, allow_disallow_lock_);
+
+  for (auto it = tagged_objects_.begin(); it != tagged_objects_.end(); ++it) {
+    if (!it->first.IsNull()) {
+      art::mirror::Object* original_obj = it->first.Read<art::kWithoutReadBarrier>();
+      art::mirror::Object* target_obj = visitor->IsMarked(original_obj);
+      if (original_obj != target_obj) {
+        it->first = art::GcRoot<art::mirror::Object>(target_obj);
+
+        if (kHandleNull && target_obj == nullptr) {
+          HandleNullSweep(it->second);
+        }
+      }
+    } else {
+      size_t index = it - tagged_objects_.begin();
+      if (index < first_free_) {
+        first_free_ = index;
+      }
+    }
+  }
+}
+
+void ObjectTagTable::HandleNullSweep(jlong tag) {
+  event_handler_->DispatchEvent(nullptr, JVMTI_EVENT_OBJECT_FREE, tag);
+}
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/object_tagging.h b/runtime/openjdkjvmti/object_tagging.h
index 523e15f..45f3e4f 100644
--- a/runtime/openjdkjvmti/object_tagging.h
+++ b/runtime/openjdkjvmti/object_tagging.h
@@ -25,29 +25,22 @@
 
 namespace openjdkjvmti {
 
+class EventHandler;
+
 class ObjectTagTable : public art::gc::SystemWeakHolder {
  public:
-  ObjectTagTable() : art::gc::SystemWeakHolder(art::LockLevel::kAllocTrackerLock) {
+  explicit ObjectTagTable(EventHandler* event_handler)
+      : art::gc::SystemWeakHolder(art::LockLevel::kAllocTrackerLock),
+        event_handler_(event_handler) {
   }
 
   void Add(art::mirror::Object* obj, jlong tag)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
-      REQUIRES(!allow_disallow_lock_) {
-    art::Thread* self = art::Thread::Current();
-    art::MutexLock mu(self, allow_disallow_lock_);
-    Wait(self);
+      REQUIRES(!allow_disallow_lock_);
 
-    if (first_free_ == tagged_objects_.size()) {
-      tagged_objects_.push_back(Entry(art::GcRoot<art::mirror::Object>(obj), tag));
-      first_free_++;
-    } else {
-      DCHECK_LT(first_free_, tagged_objects_.size());
-      DCHECK(tagged_objects_[first_free_].first.IsNull());
-      tagged_objects_[first_free_] = Entry(art::GcRoot<art::mirror::Object>(obj), tag);
-      // TODO: scan for free elements.
-      first_free_ = tagged_objects_.size();
-    }
-  }
+  bool Remove(art::mirror::Object* obj, jlong* tag)
+      REQUIRES_SHARED(art::Locks::mutator_lock_)
+      REQUIRES(!allow_disallow_lock_);
 
   bool GetTag(art::mirror::Object* obj, jlong* result)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
@@ -66,70 +59,23 @@
     return false;
   }
 
-  bool Remove(art::mirror::Object* obj, jlong* tag)
-      REQUIRES_SHARED(art::Locks::mutator_lock_)
-      REQUIRES(!allow_disallow_lock_) {
-    art::Thread* self = art::Thread::Current();
-    art::MutexLock mu(self, allow_disallow_lock_);
-    Wait(self);
-
-    for (auto it = tagged_objects_.begin(); it != tagged_objects_.end(); ++it) {
-      if (it->first.Read(nullptr) == obj) {
-        if (tag != nullptr) {
-          *tag = it->second;
-        }
-        it->first = art::GcRoot<art::mirror::Object>(nullptr);
-
-        size_t index = it - tagged_objects_.begin();
-        if (index < first_free_) {
-          first_free_ = index;
-        }
-
-        // TODO: compaction.
-
-        return true;
-      }
-    }
-
-    return false;
-  }
-
   void Sweep(art::IsMarkedVisitor* visitor)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
-      REQUIRES(!allow_disallow_lock_) {
-    art::Thread* self = art::Thread::Current();
-    art::MutexLock mu(self, allow_disallow_lock_);
-
-    for (auto it = tagged_objects_.begin(); it != tagged_objects_.end(); ++it) {
-      if (!it->first.IsNull()) {
-        art::mirror::Object* original_obj = it->first.Read<art::kWithoutReadBarrier>();
-        art::mirror::Object* target_obj = visitor->IsMarked(original_obj);
-        if (original_obj != target_obj) {
-          it->first = art::GcRoot<art::mirror::Object>(target_obj);
-
-          if (target_obj == nullptr) {
-            HandleNullSweep(it->second);
-          }
-        }
-      } else {
-        size_t index = it - tagged_objects_.begin();
-        if (index < first_free_) {
-          first_free_ = index;
-        }
-      }
-    }
-  }
+      REQUIRES(!allow_disallow_lock_);
 
  private:
   using Entry = std::pair<art::GcRoot<art::mirror::Object>, jlong>;
 
-  void HandleNullSweep(jlong tag ATTRIBUTE_UNUSED) {
-    // TODO: Handle deallocation reporting here. We'll have to enqueue tags temporarily, as we
-    //       probably shouldn't call the callbacks directly (to avoid any issues).
-  }
+  template <bool kHandleNull>
+  void SweepImpl(art::IsMarkedVisitor* visitor)
+        REQUIRES_SHARED(art::Locks::mutator_lock_)
+        REQUIRES(!allow_disallow_lock_);
+  void HandleNullSweep(jlong tag);
 
   std::vector<Entry> tagged_objects_ GUARDED_BY(allow_disallow_lock_);
   size_t first_free_ = 0;
+
+  EventHandler* event_handler_;
 };
 
 }  // namespace openjdkjvmti
diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java
index 0ea85da..5c789cd 100644
--- a/test/618-checker-induction/src/Main.java
+++ b/test/618-checker-induction/src/Main.java
@@ -134,17 +134,20 @@
   /// CHECK-DAG: Phi      loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-NOT: BoundsCheck
   //
   /// CHECK-START: void Main.deadCycleWithException(int) loop_optimization (after)
   /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
   /// CHECK-NOT: Phi      loop:<<Loop>>      outer_loop:none
   /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
-  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-NOT: ArrayGet loop:<<Loop>>      outer_loop:none
   static void deadCycleWithException(int k) {
     int dead = 0;
     for (int i = 0; i < a.length; i++) {
       a[i] = 4;
-      // Increment value of dead cycle may throw exception.
+      // Increment value of dead cycle may throw exception. Dynamic
+      // BCE takes care of the bounds check though, which enables
+      // removing the ArrayGet after removing the dead cycle.
       dead += a[k];
     }
   }
@@ -180,7 +183,17 @@
     return closed;  // only needs last value
   }
 
-  // TODO: move closed form even further out?
+  /// CHECK-START: int Main.closedFormNested() loop_optimization (before)
+  /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop1:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop1>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:i\d+>> Phi               loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Phi4:i\d+>> Phi               loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:               Return [<<Phi1>>] loop:none
+  //
+  /// CHECK-START: int Main.closedFormNested() loop_optimization (after)
+  /// CHECK-NOT:               Phi    loop:{{B\d+}} outer_loop:none
+  /// CHECK-NOT:               Phi    loop:{{B\d+}} outer_loop:loop{{B\d+}}
+  /// CHECK-DAG:               Return loop:none
   static int closedFormNested() {
     int closed = 0;
     for (int i = 0; i < 10; i++) {
@@ -191,6 +204,27 @@
     return closed;  // only needs last-value
   }
 
+  /// CHECK-START: int Main.closedFormNestedAlt() loop_optimization (before)
+  /// CHECK-DAG: <<Phi1:i\d+>> Phi               loop:<<Loop1:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>> Phi               loop:<<Loop1>>      outer_loop:none
+  /// CHECK-DAG: <<Phi3:i\d+>> Phi               loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+  /// CHECK-DAG: <<Phi4:i\d+>> Phi               loop:<<Loop2>>      outer_loop:<<Loop1>>
+  /// CHECK-DAG:               Return [<<Phi1>>] loop:none
+  //
+  /// CHECK-START: int Main.closedFormNestedAlt() loop_optimization (after)
+  /// CHECK-NOT:               Phi    loop:{{B\d+}} outer_loop:none
+  /// CHECK-NOT:               Phi    loop:{{B\d+}} outer_loop:loop{{B\d+}}
+  /// CHECK-DAG:               Return loop:none
+  static int closedFormNestedAlt() {
+    int closed = 12345;
+    for (int i = 0; i < 17; i++) {
+      for (int j = 0; j < 23; j++) {
+        closed += 7;
+      }
+    }
+    return closed;  // only needs last-value
+  }
+
   // TODO: taken test around closed form?
   static int closedFormInductionUpN(int n) {
     int closed = 12345;
@@ -220,9 +254,20 @@
   }
 
   // TODO: move closed form even further out?
-  static int closedFormNestedNN(int n) {
-    int closed = 0;
+  static int closedFormNestedNAlt(int n) {
+    int closed = 12345;
     for (int i = 0; i < n; i++) {
+      for (int j = 0; j < 23; j++) {
+        closed += 7;
+      }
+    }
+    return closed;  // only needs last-value
+  }
+
+  // TODO: move closed form even further out?
+  static int closedFormNestedMN(int m, int n) {
+    int closed = 0;
+    for (int i = 0; i < m; i++) {
       for (int j = 0; j < n; j++) {
         closed++;
       }
@@ -230,6 +275,17 @@
     return closed;  // only needs last-value
   }
 
+  // TODO: move closed form even further out?
+  static int closedFormNestedMNAlt(int m, int n) {
+    int closed = 12345;
+    for (int i = 0; i < m; i++) {
+      for (int j = 0; j < n; j++) {
+        closed += 7;
+      }
+    }
+    return closed;  // only needs last-value
+  }
+
   /// CHECK-START: int Main.mainIndexReturned() loop_optimization (before)
   /// CHECK-DAG: <<Phi:i\d+>> Phi              loop:{{B\d+}} outer_loop:none
   /// CHECK-DAG:              Return [<<Phi>>] loop:none
@@ -444,12 +500,15 @@
     expectEquals(12395, closedFormInductionUp());
     expectEquals(12295, closedFormInductionInAndDown(12345));
     expectEquals(10 * 10, closedFormNested());
+    expectEquals(12345 + 17 * 23 * 7, closedFormNestedAlt());
     for (int n = -4; n < 10; n++) {
       int tc = (n <= 0) ? 0 : n;
       expectEquals(12345 + tc * 5, closedFormInductionUpN(n));
       expectEquals(12345 - tc * 5, closedFormInductionInAndDownN(12345, n));
       expectEquals(tc * 10, closedFormNestedN(n));
-      expectEquals(tc * tc, closedFormNestedNN(n));
+      expectEquals(12345 + tc * 23 * 7, closedFormNestedNAlt(n));
+      expectEquals(tc * (tc + 1), closedFormNestedMN(n, n + 1));
+      expectEquals(12345 + tc * (tc + 1) * 7, closedFormNestedMNAlt(n, n + 1));
     }
 
     expectEquals(10, mainIndexReturned());
diff --git a/test/902-hello-transformation/transform.cc b/test/902-hello-transformation/transform.cc
index e0d623e..5b0d219 100644
--- a/test/902-hello-transformation/transform.cc
+++ b/test/902-hello-transformation/transform.cc
@@ -23,6 +23,7 @@
 #include "base/logging.h"
 #include "jni.h"
 #include "openjdkjvmti/jvmti.h"
+#include "ti-agent/common_load.h"
 #include "utils.h"
 
 namespace art {
@@ -30,7 +31,6 @@
 
 static bool RuntimeIsJvm = false;
 
-jvmtiEnv* jvmti_env;
 bool IsJVM() {
   return RuntimeIsJvm;
 }
diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc
index 8ccdf49..7d692fb 100644
--- a/test/903-hello-tagging/tagging.cc
+++ b/test/903-hello-tagging/tagging.cc
@@ -25,13 +25,12 @@
 #include "base/logging.h"
 #include "jni.h"
 #include "openjdkjvmti/jvmti.h"
+#include "ti-agent/common_load.h"
 #include "utils.h"
 
 namespace art {
 namespace Test903HelloTagging {
 
-static jvmtiEnv* jvmti_env;
-
 extern "C" JNIEXPORT void JNICALL Java_Main_setTag(JNIEnv* env ATTRIBUTE_UNUSED,
                                                    jclass,
                                                    jobject obj,
diff --git a/test/904-object-allocation/src/Main.java b/test/904-object-allocation/src/Main.java
index f15c1c2..9a089bd 100644
--- a/test/904-object-allocation/src/Main.java
+++ b/test/904-object-allocation/src/Main.java
@@ -40,7 +40,7 @@
   }
 
   public static void doTest(ArrayList<Object> l) throws Exception {
-    setupCallback();
+    setupObjectAllocCallback();
 
     enableAllocationTracking(null, true);
 
@@ -127,6 +127,6 @@
     }
   }
 
-  private static native void setupCallback();
+  private static native void setupObjectAllocCallback();
   private static native void enableAllocationTracking(Thread thread, boolean enable);
 }
diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc
index c392cd4..b22fc6c 100644
--- a/test/904-object-allocation/tracking.cc
+++ b/test/904-object-allocation/tracking.cc
@@ -26,13 +26,12 @@
 #include "openjdkjvmti/jvmti.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
+#include "ti-agent/common_load.h"
 #include "utils.h"
 
 namespace art {
 namespace Test904ObjectAllocation {
 
-static jvmtiEnv* jvmti_env;
-
 static std::string GetClassName(JNIEnv* jni_env, jclass cls) {
   ScopedLocalRef<jclass> class_class(jni_env, jni_env->GetObjectClass(cls));
   jmethodID mid = jni_env->GetMethodID(class_class.get(), "getName", "()Ljava/lang/String;");
@@ -58,7 +57,8 @@
          static_cast<size_t>(size));
 }
 
-extern "C" JNIEXPORT void JNICALL Java_Main_setupCallback(JNIEnv* env ATTRIBUTE_UNUSED, jclass) {
+extern "C" JNIEXPORT void JNICALL Java_Main_setupObjectAllocCallback(
+    JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) {
   jvmtiEventCallbacks callbacks;
   memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
   callbacks.VMObjectAlloc = ObjectAllocated;
@@ -82,7 +82,7 @@
   if (ret != JVMTI_ERROR_NONE) {
     char* err;
     jvmti_env->GetErrorName(ret, &err);
-    printf("Error getting tag: %s\n", err);
+    printf("Error enabling/disabling allocation tracking: %s\n", err);
   }
 }
 
diff --git a/test/905-object-free/build b/test/905-object-free/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/905-object-free/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+./default-build "$@" --experimental agents
diff --git a/test/905-object-free/expected.txt b/test/905-object-free/expected.txt
new file mode 100644
index 0000000..31b7318
--- /dev/null
+++ b/test/905-object-free/expected.txt
@@ -0,0 +1,10 @@
+ObjectFree tag=1
+---
+ObjectFree tag=10
+ObjectFree tag=100
+ObjectFree tag=1000
+---
+---
+---
+---
+---
diff --git a/test/905-object-free/info.txt b/test/905-object-free/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/905-object-free/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/905-object-free/run b/test/905-object-free/run
new file mode 100755
index 0000000..753b742
--- /dev/null
+++ b/test/905-object-free/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if  [[ "$@" == *"-O"* ]]; then
+  agent=libtiagent.so
+  plugin=libopenjdkjvmti.so
+  lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+  arg="jvm"
+else
+  arg="art"
+fi
+
+if [[ "$@" != *"--debuggable"* ]]; then
+  other_args=" -Xcompiler-option --debuggable "
+else
+  other_args=""
+fi
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --runtime-option -agentpath:${agent}=905-object-free,${arg} \
+                   --android-runtime-option -Xplugin:${plugin} \
+                   ${other_args} \
+                   --args ${lib}
diff --git a/test/905-object-free/src/Main.java b/test/905-object-free/src/Main.java
new file mode 100644
index 0000000..7b52e29
--- /dev/null
+++ b/test/905-object-free/src/Main.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+import java.util.ArrayList;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[1]);
+
+    doTest();
+  }
+
+  public static void doTest() throws Exception {
+    // Use a list to ensure objects must be allocated.
+    ArrayList<Object> l = new ArrayList<>(100);
+
+    setupObjectFreeCallback();
+
+    enableFreeTracking(true);
+    run(l);
+
+    enableFreeTracking(false);
+    run(l);
+  }
+
+  private static void run(ArrayList<Object> l) {
+    allocate(l, 1);
+    l.clear();
+
+    Runtime.getRuntime().gc();
+
+    System.out.println("---");
+
+    // Note: the reporting will not depend on the heap layout (which could be unstable). Walking
+    //       the tag table should give us a stable output order.
+    for (int i = 10; i <= 1000; i *= 10) {
+      allocate(l, i);
+    }
+    l.clear();
+
+    Runtime.getRuntime().gc();
+
+    System.out.println("---");
+
+    Runtime.getRuntime().gc();
+
+    System.out.println("---");
+  }
+
+  private static void allocate(ArrayList<Object> l, long tag) {
+    Object obj = new Object();
+    l.add(obj);
+    setTag(obj, tag);
+  }
+
+  private static native void setupObjectFreeCallback();
+  private static native void enableFreeTracking(boolean enable);
+  private static native void setTag(Object o, long tag);
+}
diff --git a/test/905-object-free/tracking_free.cc b/test/905-object-free/tracking_free.cc
new file mode 100644
index 0000000..5905d48
--- /dev/null
+++ b/test/905-object-free/tracking_free.cc
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2013 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 "tracking_free.h"
+
+#include <iostream>
+#include <pthread.h>
+#include <stdio.h>
+#include <vector>
+
+#include "base/logging.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+#include "ScopedUtfChars.h"
+#include "ti-agent/common_load.h"
+#include "utils.h"
+
+namespace art {
+namespace Test905ObjectFree {
+
+static void JNICALL ObjectFree(jvmtiEnv* ti_env ATTRIBUTE_UNUSED, jlong tag) {
+  printf("ObjectFree tag=%zu\n", static_cast<size_t>(tag));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_setupObjectFreeCallback(
+    JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) {
+  jvmtiEventCallbacks callbacks;
+  memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+  callbacks.ObjectFree = ObjectFree;
+
+  jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
+  if (ret != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(ret, &err);
+    printf("Error setting callbacks: %s\n", err);
+  }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_enableFreeTracking(JNIEnv* env ATTRIBUTE_UNUSED,
+                                                               jclass klass ATTRIBUTE_UNUSED,
+                                                               jboolean enable) {
+  jvmtiError ret = jvmti_env->SetEventNotificationMode(
+      enable ? JVMTI_ENABLE : JVMTI_DISABLE,
+      JVMTI_EVENT_OBJECT_FREE,
+      nullptr);
+  if (ret != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(ret, &err);
+    printf("Error enabling/disabling object-free callbacks: %s\n", err);
+  }
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+            char* options ATTRIBUTE_UNUSED,
+            void* reserved ATTRIBUTE_UNUSED) {
+  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+    printf("Unable to get jvmti env!\n");
+    return 1;
+  }
+  return 0;
+}
+
+}  // namespace Test905ObjectFree
+}  // namespace art
diff --git a/test/905-object-free/tracking_free.h b/test/905-object-free/tracking_free.h
new file mode 100644
index 0000000..ba4aa43
--- /dev/null
+++ b/test/905-object-free/tracking_free.h
@@ -0,0 +1,30 @@
+/*
+ * 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_TEST_905_OBJECT_FREE_TRACKING_FREE_H_
+#define ART_TEST_905_OBJECT_FREE_TRACKING_FREE_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test905ObjectFree {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+}  // namespace Test905ObjectFree
+}  // namespace art
+
+#endif  // ART_TEST_905_OBJECT_FREE_TRACKING_FREE_H_
diff --git a/test/Android.bp b/test/Android.bp
index b2152e8..4457e8a 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -247,6 +247,7 @@
         "902-hello-transformation/transform.cc",
         "903-hello-tagging/tagging.cc",
         "904-object-allocation/tracking.cc",
+        "905-object-free/tracking_free.cc",
     ],
     shared_libs: [
         "libart",
@@ -267,6 +268,7 @@
         "902-hello-transformation/transform.cc",
         "903-hello-tagging/tagging.cc",
         "904-object-allocation/tracking.cc",
+        "905-object-free/tracking_free.cc",
     ],
     shared_libs: [
         "libartd",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 935d6ad..29000f0 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -263,7 +263,7 @@
 # 147-stripped-dex-fallback isn't supported on device because --strip-dex
 # requires the zip command.
 # 569-checker-pattern-replacement tests behaviour present only on host.
-# 90{2,3,4} are not supported in current form due to linker
+# 90{2,3,4,5} are not supported in current form due to linker
 # restrictions. See b/31681198
 TEST_ART_BROKEN_TARGET_TESTS := \
   147-stripped-dex-fallback \
@@ -271,6 +271,7 @@
   902-hello-transformation \
   903-hello-tagging \
   904-object-allocation \
+  905-object-free \
 
 ifneq (,$(filter target,$(TARGET_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index fde3c6b..2b8947e 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -22,14 +22,18 @@
 #include "art_method-inl.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "common_load.h"
 
 #include "901-hello-ti-agent/basics.h"
 #include "902-hello-transformation/transform.h"
 #include "903-hello-tagging/tagging.h"
 #include "904-object-allocation/tracking.h"
+#include "905-object-free/tracking_free.h"
 
 namespace art {
 
+jvmtiEnv* jvmti_env;
+
 using OnLoad   = jint (*)(JavaVM* vm, char* options, void* reserved);
 using OnAttach = jint (*)(JavaVM* vm, char* options, void* reserved);
 
@@ -45,6 +49,7 @@
   { "902-hello-transformation", Test902HelloTransformation::OnLoad, nullptr },
   { "903-hello-tagging", Test903HelloTagging::OnLoad, nullptr },
   { "904-object-allocation", Test904ObjectAllocation::OnLoad, nullptr },
+  { "905-object-free", Test905ObjectFree::OnLoad, nullptr },
 };
 
 static AgentLib* FindAgent(char* name) {
diff --git a/test/ti-agent/common_load.h b/test/ti-agent/common_load.h
new file mode 100644
index 0000000..fac94b4
--- /dev/null
+++ b/test/ti-agent/common_load.h
@@ -0,0 +1,28 @@
+/*
+ * 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_TEST_TI_AGENT_COMMON_LOAD_H_
+#define ART_TEST_TI_AGENT_COMMON_LOAD_H_
+
+#include "openjdkjvmti/jvmti.h"
+
+namespace art {
+
+extern jvmtiEnv* jvmti_env;
+
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_COMMON_LOAD_H_