diff options
28 files changed, 593 insertions, 152 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index e2f8d929c3..414a43c8ec 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -472,7 +472,8 @@ static void SetupIntrinsic(Thread* self, ? 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 @@ void CompilerDriver::CompileAll(jobject class_loader, // 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 5877f57b74..b617387ef8 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 36e252742c..0cf5f18c4b 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -401,7 +401,6 @@ jobject JniCompilerTest::class_loader_; 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 @@ jobject JniCompilerTest::class_loader_; 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 aa3f26809a..adfe09ba9f 100644 --- a/compiler/optimizing/dead_code_elimination.cc +++ b/compiler/optimizing/dead_code_elimination.cc @@ -343,14 +343,7 @@ void HDeadCodeElimination::RemoveDeadInstructions() { 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 63850b34b8..df31e81169 100644 --- a/compiler/optimizing/induction_var_range.h +++ b/compiler/optimizing/induction_var_range.h @@ -131,6 +131,14 @@ class InductionVarRange { */ 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 93c6c20d7c..33fa87d568 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -69,33 +69,6 @@ static bool IsEmptyBody(HBasicBlock* block, ArenaSet<HInstruction*>* iset) { 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 @@ HLoopOptimization::HLoopOptimization(HGraph* graph, 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::RemoveLoop(LoopNode* node) { 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 @@ void HLoopOptimization::SimplifyInduction(LoopNode* node) { 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 @@ void HLoopOptimization::RemoveIfEmptyLoop(LoopNode* node) { 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 b2bf1c8507..9c4b462a1f 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -46,7 +46,7 @@ class HLoopOptimization : public HOptimization { 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 @@ class HLoopOptimization : public HOptimization { 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 @@ class HLoopOptimization : public HOptimization { // 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 828c0e51c8..348f99d6df 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1931,6 +1931,19 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { 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 e1f9ba2b98..2d9149a1c5 100644 --- a/runtime/openjdkjvmti/Android.bp +++ b/runtime/openjdkjvmti/Android.bp @@ -18,6 +18,7 @@ cc_defaults { 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 6812a923b3..36be2a0127 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 0000000000..bb17cac476 --- /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 523e15f6df..45f3e4f42d 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 @@ class ObjectTagTable : public art::gc::SystemWeakHolder { 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 0ea85da5ce..5c789cdd84 100644 --- a/test/618-checker-induction/src/Main.java +++ b/test/618-checker-induction/src/Main.java @@ -134,17 +134,20 @@ public class Main { /// 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 @@ public class Main { 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 @@ public class Main { 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 @@ public class Main { } // 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 @@ public class Main { 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 @@ public class Main { 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 e0d623e6e1..5b0d219d8f 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 @@ namespace Test902HelloTransformation { 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 8ccdf49892..7d692fb5de 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 f15c1c213f..9a089bdd78 100644 --- a/test/904-object-allocation/src/Main.java +++ b/test/904-object-allocation/src/Main.java @@ -40,7 +40,7 @@ public class Main { } public static void doTest(ArrayList<Object> l) throws Exception { - setupCallback(); + setupObjectAllocCallback(); enableAllocationTracking(null, true); @@ -127,6 +127,6 @@ public class Main { } } - 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 c392cd48a7..b22fc6c8c0 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 void JNICALL ObjectAllocated(jvmtiEnv* ti_env ATTRIBUTE_UNUSED, 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 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enableAllocationTracking(JNIEnv* env 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 0000000000..898e2e54a2 --- /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 0000000000..31b73185f9 --- /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 0000000000..875a5f6ec1 --- /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 0000000000..753b742681 --- /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 0000000000..7b52e29285 --- /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 0000000000..5905d481cf --- /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 0000000000..ba4aa43ffe --- /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 b2152e875b..4457e8a655 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -247,6 +247,7 @@ art_cc_test_library { "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 @@ art_cc_test_library { "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 935d6ad440..29000f053e 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -263,7 +263,7 @@ endif # 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 @@ TEST_ART_BROKEN_TARGET_TESTS := \ 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 fde3c6bb38..2b8947ec96 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 @@ AgentLib agents[] = { { "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 0000000000..fac94b4c6e --- /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_ |