diff options
| -rw-r--r-- | compiler/jni/jni_compiler_test.cc | 5 | ||||
| -rw-r--r-- | compiler/optimizing/loop_optimization.cc | 164 | ||||
| -rw-r--r-- | compiler/optimizing/loop_optimization.h | 14 | ||||
| -rw-r--r-- | dexlayout/dex_ir.cc | 117 | ||||
| -rw-r--r-- | dexlayout/dex_ir.h | 29 | ||||
| -rw-r--r-- | dexlayout/dex_visualize.cc | 19 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/OpenjdkJvmTi.cc | 4 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/object_tagging.h | 2 | ||||
| -rw-r--r-- | test/618-checker-induction/src/Main.java | 126 | ||||
| -rw-r--r-- | test/Android.run-test.mk | 6 |
10 files changed, 379 insertions, 107 deletions
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 6b56fe0b7f..36e252742c 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -523,8 +523,7 @@ bool ScopedDisableCheckNumStackReferences::sCheckNumStackReferences = true; // Check that the handle scope at the start of this block is the same as the handle scope at the end of the block. struct ScopedCheckHandleScope { - ScopedCheckHandleScope() { - handle_scope_ = Thread::Current()->GetTopHandleScope(); + ScopedCheckHandleScope() : handle_scope_(Thread::Current()->GetTopHandleScope()) { } ~ScopedCheckHandleScope() { @@ -533,7 +532,7 @@ struct ScopedCheckHandleScope { << "invocations have finished (as before they were invoked)."; } - HandleScope* handle_scope_; + HandleScope* const handle_scope_; }; static void expectNumStackReferences(size_t val1, size_t val2) { diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 4acf3ac682..93c6c20d7c 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -21,13 +21,15 @@ namespace art { // TODO: Generalize to cycles, as found by induction analysis? -static bool IsPhiAddSub(HPhi* phi, /*out*/ HInstruction** addsub_out) { +static bool IsPhiInduction(HPhi* phi, ArenaSet<HInstruction*>* iset) { + DCHECK(iset->empty()); HInputsRef inputs = phi->GetInputs(); if (inputs.size() == 2 && (inputs[1]->IsAdd() || inputs[1]->IsSub())) { HInstruction* addsub = inputs[1]; if (addsub->InputAt(0) == phi || addsub->InputAt(1) == phi) { if (addsub->GetUses().HasExactlyOneElement()) { - *addsub_out = addsub; + iset->insert(phi); + iset->insert(addsub); return true; } } @@ -35,39 +37,23 @@ static bool IsPhiAddSub(HPhi* phi, /*out*/ HInstruction** addsub_out) { return false; } -static bool IsOnlyUsedAfterLoop(const HLoopInformation& loop_info, - HPhi* phi, HInstruction* addsub) { - for (const HUseListNode<HInstruction*>& use : phi->GetUses()) { - if (use.GetUser() != addsub) { - HLoopInformation* other_loop_info = use.GetUser()->GetBlock()->GetLoopInformation(); - if (other_loop_info != nullptr && other_loop_info->IsIn(loop_info)) { - return false; - } - } - } - return true; -} - // Find: phi: Phi(init, addsub) // s: SuspendCheck // c: Condition(phi, bound) // i: If(c) // TODO: Find a less pattern matching approach? -static bool IsEmptyHeader(HBasicBlock* block, /*out*/ HInstruction** addsub) { +static bool IsEmptyHeader(HBasicBlock* block, ArenaSet<HInstruction*>* iset) { + DCHECK(iset->empty()); HInstruction* phi = block->GetFirstPhi(); - if (phi != nullptr && phi->GetNext() == nullptr && IsPhiAddSub(phi->AsPhi(), addsub)) { + if (phi != nullptr && phi->GetNext() == nullptr && IsPhiInduction(phi->AsPhi(), iset)) { HInstruction* s = block->GetFirstInstruction(); if (s != nullptr && s->IsSuspendCheck()) { HInstruction* c = s->GetNext(); if (c != nullptr && c->IsCondition() && c->GetUses().HasExactlyOneElement()) { HInstruction* i = c->GetNext(); if (i != nullptr && i->IsIf() && i->InputAt(0) == c) { - // Check that phi is only used inside loop as expected. - for (const HUseListNode<HInstruction*>& use : phi->GetUses()) { - if (use.GetUser() != *addsub && use.GetUser() != c) { - return false; - } - } + iset->insert(c); + iset->insert(s); return true; } } @@ -76,10 +62,11 @@ static bool IsEmptyHeader(HBasicBlock* block, /*out*/ HInstruction** addsub) { return false; } -static bool IsEmptyBody(HBasicBlock* block, HInstruction* addsub) { +static bool IsEmptyBody(HBasicBlock* block, ArenaSet<HInstruction*>* iset) { HInstruction* phi = block->GetFirstPhi(); HInstruction* i = block->GetFirstInstruction(); - return phi == nullptr && i == addsub && i->GetNext() != nullptr && i->GetNext()->IsGoto(); + return phi == nullptr && iset->find(i) != iset->end() && + i->GetNext() != nullptr && i->GetNext()->IsGoto(); } static HBasicBlock* TryRemovePreHeader(HBasicBlock* preheader, HBasicBlock* entry_block) { @@ -127,7 +114,8 @@ HLoopOptimization::HLoopOptimization(HGraph* graph, induction_range_(induction_analysis), loop_allocator_(nullptr), top_loop_(nullptr), - last_loop_(nullptr) { + last_loop_(nullptr), + iset_(nullptr) { } void HLoopOptimization::Run() { @@ -164,8 +152,14 @@ void HLoopOptimization::LocalRun() { } } - // Traverse the loop hierarchy inner-to-outer and optimize. - TraverseLoopsInnerToOuter(top_loop_); + // Traverse the loop hierarchy inner-to-outer and optimize. Traversal can use + // a temporary set that stores instructions using the phase-local allocator. + if (top_loop_ != nullptr) { + ArenaSet<HInstruction*> iset(loop_allocator_->Adapter(kArenaAllocLoopOptimization)); + iset_ = &iset; + TraverseLoopsInnerToOuter(top_loop_); + iset_ = nullptr; // detach + } } void HLoopOptimization::AddLoop(HLoopInformation* loop_info) { @@ -194,9 +188,25 @@ void HLoopOptimization::AddLoop(HLoopInformation* loop_info) { void HLoopOptimization::RemoveLoop(LoopNode* node) { DCHECK(node != nullptr); - // TODO: implement when needed (for current set of optimizations, we don't - // need to keep recorded loop hierarchy up to date, but as we get different - // traversal, we may want to remove the node from the hierarchy here. + DCHECK(node->inner == nullptr); + if (node->previous != nullptr) { + // Within sequence. + node->previous->next = node->next; + if (node->next != nullptr) { + node->next->previous = node->previous; + } + } else { + // First of sequence. + if (node->outer != nullptr) { + node->outer->inner = node->next; + } else { + top_loop_ = node->next; + } + if (node->next != nullptr) { + node->next->outer = node->outer; + node->next->previous = nullptr; + } + } } void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) { @@ -213,34 +223,20 @@ void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) { void HLoopOptimization::SimplifyInduction(LoopNode* node) { HBasicBlock* header = node->loop_info->GetHeader(); HBasicBlock* preheader = node->loop_info->GetPreHeader(); - // Scan the phis in the header to find opportunities to optimize induction. + // Scan the phis in the header to find opportunities to simplify an induction + // cycle that is only used outside the loop. Replace these uses, if any, with + // the last value and remove the induction cycle. + // Examples: for (int i = 0; x != null; i++) { .... no i .... } + // for (int i = 0; i < 10; i++, k++) { .... no k .... } return k; for (HInstructionIterator it(header->GetPhis()); !it.Done(); it.Advance()) { HPhi* phi = it.Current()->AsPhi(); - HInstruction* addsub = nullptr; - // Find phi-add/sub cycle. - if (IsPhiAddSub(phi, &addsub)) { - // Simple case, the induction is only used by itself. Although redundant, - // later phases do not easily detect this property. Thus, eliminate here. - // Example: for (int i = 0; x != null; i++) { .... no i .... } - if (phi->GetUses().HasExactlyOneElement()) { - // Remove the cycle, including all uses. Even environment uses can be removed, - // since these computations have no effect at all. - RemoveFromCycle(phi); // removes environment uses too - RemoveFromCycle(addsub); - continue; - } - // Closed form case. Only the last value of the induction is needed. Remove all - // overhead from the loop, and replace subsequent uses with the last value. - // Example: for (int i = 0; i < 10; i++, k++) { .... no k .... } return k; - if (IsOnlyUsedAfterLoop(*node->loop_info, phi, addsub) && - induction_range_.CanGenerateLastValue(phi)) { - HInstruction* last = induction_range_.GenerateLastValue(phi, graph_, preheader); - // Remove the cycle, replacing all uses. Even environment uses can consume the final - // value, since any first real use is outside the loop (although this may imply - // that deopting may look "ahead" a bit on the phi value). - ReplaceAllUses(phi, last, addsub); - RemoveFromCycle(phi); // removes environment uses too - RemoveFromCycle(addsub); + iset_->clear(); + int32_t use_count = 0; + if (IsPhiInduction(phi, iset_) && + IsOnlyUsedAfterLoop(*node->loop_info, phi, &use_count) && + TryReplaceWithLastValue(phi, use_count, preheader)) { + for (HInstruction* i : *iset_) { + RemoveFromCycle(i); } } } @@ -266,14 +262,18 @@ void HLoopOptimization::RemoveIfEmptyLoop(LoopNode* node) { HBasicBlock* exit = (header->GetSuccessors()[0] == body) ? header->GetSuccessors()[1] : header->GetSuccessors()[0]; - // Ensure exit can only be reached by exiting loop (this seems typically the - // case anyway, and simplifies code generation below; TODO: perhaps relax?). + // Ensure exit can only be reached by exiting loop. if (exit->GetPredecessors().size() != 1) { return; } - // Detect an empty loop: no side effects other than plain iteration. - HInstruction* addsub = nullptr; - if (IsEmptyHeader(header, &addsub) && IsEmptyBody(body, addsub)) { + // Detect an empty loop: no side effects other than plain iteration. Replace + // subsequent index uses, if any, with the last value and remove the loop. + iset_->clear(); + int32_t use_count = 0; + if (IsEmptyHeader(header, iset_) && + IsEmptyBody(body, iset_) && + 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); @@ -299,15 +299,29 @@ void HLoopOptimization::RemoveIfEmptyLoop(LoopNode* node) { } } -void HLoopOptimization::ReplaceAllUses(HInstruction* instruction, - HInstruction* replacement, - HInstruction* exclusion) { +bool HLoopOptimization::IsOnlyUsedAfterLoop(const 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)) { + return false; + } + ++*use_count; + } + } + return true; +} + +void HLoopOptimization::ReplaceAllUses(HInstruction* instruction, HInstruction* replacement) { const HUseList<HInstruction*>& uses = instruction->GetUses(); for (auto it = uses.begin(), end = uses.end(); it != end;) { HInstruction* user = it->GetUser(); size_t index = it->GetIndex(); ++it; // increment before replacing - if (user != exclusion) { + if (iset_->find(user) == iset_->end()) { // not excluded? user->ReplaceInput(replacement, index); induction_range_.Replace(user, instruction, replacement); // update induction } @@ -317,7 +331,7 @@ void HLoopOptimization::ReplaceAllUses(HInstruction* instruction, HEnvironment* user = it->GetUser(); size_t index = it->GetIndex(); ++it; // increment before replacing - if (user->GetHolder() != exclusion) { + if (iset_->find(user->GetHolder()) == iset_->end()) { // not excluded? user->RemoveAsUserOfInput(index); user->SetRawEnvAt(index, replacement); replacement->AddEnvUseAt(user, index); @@ -325,4 +339,20 @@ void HLoopOptimization::ReplaceAllUses(HInstruction* instruction, } } +bool HLoopOptimization::TryReplaceWithLastValue(HInstruction* instruction, + int32_t use_count, + HBasicBlock* block) { + // If true uses appear after the loop, replace these uses with the last value. Environment + // uses can consume this value too, since any first true use is outside the loop (although + // this may imply that de-opting may look "ahead" a bit on the phi value). If there are only + // environment uses, the value is dropped altogether, since the computations have no effect. + if (use_count > 0) { + if (!induction_range_.CanGenerateLastValue(instruction)) { + return false; + } + ReplaceAllUses(instruction, induction_range_.GenerateLastValue(instruction, graph_, block)); + } + return true; +} + } // namespace art diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index 6092955221..b2bf1c8507 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -63,9 +63,13 @@ class HLoopOptimization : public HOptimization { void SimplifyInduction(LoopNode* node); void RemoveIfEmptyLoop(LoopNode* node); - void ReplaceAllUses(HInstruction* instruction, - HInstruction* replacement, - HInstruction* exclusion); + bool IsOnlyUsedAfterLoop(const HLoopInformation& loop_info, + HInstruction* instruction, + /*out*/ int32_t* use_count); + void ReplaceAllUses(HInstruction* instruction, HInstruction* replacement); + bool TryReplaceWithLastValue(HInstruction* instruction, + int32_t use_count, + HBasicBlock* block); // Range information based on prior induction variable analysis. InductionVarRange induction_range_; @@ -79,6 +83,10 @@ class HLoopOptimization : public HOptimization { LoopNode* top_loop_; LoopNode* last_loop_; + // Temporary bookkeeping of a set of instructions. + // Contents reside in phase-local heap memory. + ArenaSet<HInstruction*>* iset_; + friend class LoopOptimizationTest; DISALLOW_COPY_AND_ASSIGN(HLoopOptimization); diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index bc909c3b56..0f07f23a88 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -21,6 +21,7 @@ */ #include "dex_ir.h" +#include "dex_instruction-inl.h" #include "dex_ir_builder.h" namespace art { @@ -103,6 +104,102 @@ static uint32_t GetDebugInfoStreamSize(const uint8_t* debug_info_stream) { } } +static bool GetIdFromInstruction(Collections& collections, + const Instruction* dec_insn, + std::vector<TypeId*>* type_ids, + std::vector<StringId*>* string_ids, + std::vector<MethodId*>* method_ids, + std::vector<FieldId*>* field_ids) { + // Determine index and width of the string. + uint32_t index = 0; + switch (Instruction::FormatOf(dec_insn->Opcode())) { + // SOME NOT SUPPORTED: + // case Instruction::k20bc: + case Instruction::k21c: + case Instruction::k35c: + // case Instruction::k35ms: + case Instruction::k3rc: + // case Instruction::k3rms: + // case Instruction::k35mi: + // case Instruction::k3rmi: + index = dec_insn->VRegB(); + break; + case Instruction::k31c: + index = dec_insn->VRegB(); + break; + case Instruction::k22c: + // case Instruction::k22cs: + index = dec_insn->VRegC(); + break; + default: + break; + } // switch + + // Determine index type, and add reference to the appropriate collection. + switch (Instruction::IndexTypeOf(dec_insn->Opcode())) { + case Instruction::kIndexTypeRef: + if (index < collections.TypeIdsSize()) { + type_ids->push_back(collections.GetTypeId(index)); + return true; + } + break; + case Instruction::kIndexStringRef: + if (index < collections.StringIdsSize()) { + string_ids->push_back(collections.GetStringId(index)); + return true; + } + break; + case Instruction::kIndexMethodRef: + if (index < collections.MethodIdsSize()) { + method_ids->push_back(collections.GetMethodId(index)); + return true; + } + break; + case Instruction::kIndexFieldRef: + if (index < collections.FieldIdsSize()) { + field_ids->push_back(collections.GetFieldId(index)); + return true; + } + break; + case Instruction::kIndexUnknown: + case Instruction::kIndexNone: + case Instruction::kIndexVtableOffset: + case Instruction::kIndexFieldOffset: + default: + break; + } // switch + return false; +} + +/* + * Get all the types, strings, methods, and fields referred to from bytecode. + */ +static bool GetIdsFromByteCode(Collections& collections, + const CodeItem* code, + std::vector<TypeId*>* type_ids, + std::vector<StringId*>* string_ids, + std::vector<MethodId*>* method_ids, + std::vector<FieldId*>* field_ids) { + bool has_id = false; + // Iterate over all instructions. + const uint16_t* insns = code->Insns(); + for (uint32_t insn_idx = 0; insn_idx < code->InsnsSize();) { + const Instruction* instruction = Instruction::At(&insns[insn_idx]); + const uint32_t insn_width = instruction->SizeInCodeUnits(); + if (insn_width == 0) { + break; + } + has_id |= GetIdFromInstruction(collections, + instruction, + type_ids, + string_ids, + method_ids, + field_ids); + insn_idx += insn_width; + } // for + return has_id; +} + EncodedValue* Collections::ReadEncodedValue(const uint8_t** data) { const uint8_t encoded_value = *(*data)++; const uint8_t type = encoded_value & 0x1f; @@ -514,6 +611,26 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, CodeItem* code_item = new CodeItem( registers_size, ins_size, outs_size, debug_info, insns_size, insns, tries, handler_list); code_items_.AddItem(code_item, offset); + // Add "fixup" references to types, strings, methods, and fields. + // This is temporary, as we will probably want more detailed parsing of the + // instructions here. + std::unique_ptr<std::vector<TypeId*>> type_ids(new std::vector<TypeId*>()); + std::unique_ptr<std::vector<StringId*>> string_ids(new std::vector<StringId*>()); + std::unique_ptr<std::vector<MethodId*>> method_ids(new std::vector<MethodId*>()); + std::unique_ptr<std::vector<FieldId*>> field_ids(new std::vector<FieldId*>()); + if (GetIdsFromByteCode(*this, + code_item, + type_ids.get(), + string_ids.get(), + method_ids.get(), + field_ids.get())) { + CodeFixups* fixups = new CodeFixups(type_ids.release(), + string_ids.release(), + method_ids.release(), + field_ids.release()); + code_item->SetCodeFixups(fixups); + } + return code_item; } diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index 5e686d3c00..38eb0b1b4b 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -802,6 +802,31 @@ class TryItem : public Item { using TryItemVector = std::vector<std::unique_ptr<const TryItem>>; +class CodeFixups { + public: + CodeFixups(std::vector<TypeId*>* type_ids, + std::vector<StringId*>* string_ids, + std::vector<MethodId*>* method_ids, + std::vector<FieldId*>* field_ids) + : type_ids_(type_ids), + string_ids_(string_ids), + method_ids_(method_ids), + field_ids_(field_ids) { } + + std::vector<TypeId*>* TypeIds() const { return type_ids_.get(); } + std::vector<StringId*>* StringIds() const { return string_ids_.get(); } + std::vector<MethodId*>* MethodIds() const { return method_ids_.get(); } + std::vector<FieldId*>* FieldIds() const { return field_ids_.get(); } + + private: + std::unique_ptr<std::vector<TypeId*>> type_ids_; + std::unique_ptr<std::vector<StringId*>> string_ids_; + std::unique_ptr<std::vector<MethodId*>> method_ids_; + std::unique_ptr<std::vector<FieldId*>> field_ids_; + + DISALLOW_COPY_AND_ASSIGN(CodeFixups); +}; + class CodeItem : public Item { public: CodeItem(uint16_t registers_size, @@ -833,6 +858,9 @@ class CodeItem : public Item { TryItemVector* Tries() const { return tries_.get(); } CatchHandlerVector* Handlers() const { return handlers_.get(); } + void SetCodeFixups(CodeFixups* fixups) { fixups_.reset(fixups); } + CodeFixups* GetCodeFixups() const { return fixups_.get(); } + void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); } private: @@ -844,6 +872,7 @@ class CodeItem : public Item { std::unique_ptr<uint16_t[]> insns_; std::unique_ptr<TryItemVector> tries_; // This can be nullptr. std::unique_ptr<CatchHandlerVector> handlers_; // This can be nullptr. + std::unique_ptr<CodeFixups> fixups_; // This can be nullptr. DISALLOW_COPY_AND_ASSIGN(CodeItem); }; diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc index 46dff5f5f2..bc9ca6de98 100644 --- a/dexlayout/dex_visualize.cc +++ b/dexlayout/dex_visualize.cc @@ -279,6 +279,25 @@ class Dumper { const dex_ir::CodeItem* code_item = method->GetCodeItem(); if (code_item != nullptr) { DumpAddressRange(code_item, class_index); + const dex_ir::CodeFixups* fixups = code_item->GetCodeFixups(); + if (fixups != nullptr) { + std::vector<dex_ir::TypeId*>* type_ids = fixups->TypeIds(); + for (dex_ir::TypeId* type_id : *type_ids) { + DumpTypeId(type_id, class_index); + } + std::vector<dex_ir::StringId*>* string_ids = fixups->StringIds(); + for (dex_ir::StringId* string_id : *string_ids) { + DumpStringId(string_id, class_index); + } + std::vector<dex_ir::MethodId*>* method_ids = fixups->MethodIds(); + for (dex_ir::MethodId* method_id : *method_ids) { + DumpMethodId(method_id, class_index); + } + std::vector<dex_ir::FieldId*>* field_ids = fixups->FieldIds(); + for (dex_ir::FieldId* field_id : *field_ids) { + DumpFieldId(field_id, class_index); + } + } } } diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index bebc2fc396..bea40c8781 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -1035,7 +1035,9 @@ static jint GetEnvHandler(art::JavaVMExt* vm, /*out*/void** env, jint version) { // The plugin initialization function. This adds the jvmti environment. extern "C" bool ArtPlugin_Initialize() { - art::Runtime::Current()->GetJavaVM()->AddEnvironmentHook(GetEnvHandler); + art::Runtime* runtime = art::Runtime::Current(); + runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler); + runtime->AddSystemWeakHolder(&gObjectTagTable); return true; } diff --git a/runtime/openjdkjvmti/object_tagging.h b/runtime/openjdkjvmti/object_tagging.h index e276c524ca..523e15f6df 100644 --- a/runtime/openjdkjvmti/object_tagging.h +++ b/runtime/openjdkjvmti/object_tagging.h @@ -102,7 +102,7 @@ class ObjectTagTable : public art::gc::SystemWeakHolder { for (auto it = tagged_objects_.begin(); it != tagged_objects_.end(); ++it) { if (!it->first.IsNull()) { - art::mirror::Object* original_obj = it->first.Read(); + 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); diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java index a68c383c0a..0ea85da5ce 100644 --- a/test/618-checker-induction/src/Main.java +++ b/test/618-checker-induction/src/Main.java @@ -155,7 +155,7 @@ public class Main { /// CHECK-DAG: Return [<<Phi1>>] loop:none // /// CHECK-START: int Main.closedFormInductionUp() loop_optimization (after) - /// CHECK-NOT: Phi loop:B\d+ outer_loop:none + /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none /// CHECK-DAG: Return loop:none static int closedFormInductionUp() { int closed = 12345; @@ -171,7 +171,7 @@ public class Main { /// CHECK-DAG: Return [<<Phi2>>] loop:none // /// CHECK-START: int Main.closedFormInductionInAndDown(int) loop_optimization (after) - /// CHECK-NOT: Phi loop:B\d+ outer_loop:none + /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none /// CHECK-DAG: Return loop:none static int closedFormInductionInAndDown(int closed) { for (int i = 0; i < 10; i++) { @@ -180,6 +180,17 @@ public class Main { return closed; // only needs last value } + // TODO: move closed form even further out? + static int closedFormNested() { + int closed = 0; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + closed++; + } + } + return closed; // only needs last-value + } + // TODO: taken test around closed form? static int closedFormInductionUpN(int n) { int closed = 12345; @@ -198,7 +209,7 @@ public class Main { } // TODO: move closed form even further out? - static int closedFormNested(int n) { + static int closedFormNestedN(int n) { int closed = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < 10; j++) { @@ -208,45 +219,78 @@ public class Main { return closed; // only needs last-value } - // TODO: handle as closed/empty eventually? - static int mainIndexReturned(int n) { + // TODO: move closed form even further out? + static int closedFormNestedNN(int n) { + int closed = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + closed++; + } + } + 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 + // + /// CHECK-START: int Main.mainIndexReturned() loop_optimization (after) + /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none + /// CHECK-DAG: Return loop:none + static int mainIndexReturned() { int i; - for (i = 0; i < n; i++); + for (i = 0; i < 10; i++); return i; } // If ever replaced by closed form, last value should be correct! - static int periodicReturned(int n) { + static int periodicReturned() { int k = 0; - for (int i = 0; i < n; i++) { + for (int i = 0; i < 9; i++) { k = 1 - k; } return k; } - // Same here. - private static int getSum(int n) { + // If ever replaced by closed form, last value should be correct! + private static int getSum21() { int k = 0; int sum = 0; - for (int i = 0; i < n; i++) { + for (int i = 0; i < 6; i++) { k++; sum += k; } return sum; } - // Same here. - private static int getSum21() { + // TODO: handle as closed/empty eventually? + static int mainIndexReturnedN(int n) { + int i; + for (i = 0; i < n; i++); + return i; + } + + // If ever replaced by closed form, last value should be correct! + static int periodicReturnedN(int n) { + int k = 0; + for (int i = 0; i < n; i++) { + k = 1 - k; + } + return k; + } + + // If ever replaced by closed form, last value should be correct! + private static int getSumN(int n) { int k = 0; int sum = 0; - for (int i = 0; i < 6; i++) { + for (int i = 0; i < n; i++) { k++; sum += k; } return sum; } - // Same here. + // If ever replaced by closed form, last value should be correct! private static int closedTwice() { int closed = 0; for (int i = 0; i < 10; i++) { @@ -269,7 +313,7 @@ public class Main { /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" // /// CHECK-START: int Main.closedFeed() loop_optimization (after) - /// CHECK-NOT: Phi loop:B\d+ outer_loop:none + /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none /// CHECK-DAG: Return loop:none private static int closedFeed() { int closed = 0; @@ -316,6 +360,27 @@ public class Main { return closed; } + /// CHECK-START: int Main.waterFall() loop_optimization (before) + /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none + /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:none + /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop3:B\d+>> outer_loop:none + /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop4:B\d+>> outer_loop:none + /// CHECK-DAG: <<Phi5:i\d+>> Phi loop:<<Loop5:B\d+>> outer_loop:none + /// CHECK-DAG: Return [<<Phi5>>] loop:none + // + /// CHECK-START: int Main.waterFall() loop_optimization (after) + /// CHECK-NOT: Phi loop:B\d+ outer_loop:none + /// CHECK-DAG: Return loop:none + private static int waterFall() { + int i = 0; + for (; i < 10; i++); + for (; i < 20; i++); + for (; i < 30; i++); + for (; i < 40; i++); + for (; i < 50; i++); + return i; // this should become just 50 + } + private static int exceptionExitBeforeAdd() { int k = 0; try { @@ -376,31 +441,32 @@ public class Main { expectEquals(4, a[i]); } - int c = closedFormInductionUp(); - expectEquals(12395, c); - c = closedFormInductionInAndDown(12345); - expectEquals(12295, c); + expectEquals(12395, closedFormInductionUp()); + expectEquals(12295, closedFormInductionInAndDown(12345)); + expectEquals(10 * 10, closedFormNested()); for (int n = -4; n < 10; n++) { int tc = (n <= 0) ? 0 : n; - c = closedFormInductionUpN(n); - expectEquals(12345 + tc * 5, c); - c = closedFormInductionInAndDownN(12345, n); - expectEquals(12345 - tc * 5, c); - c = closedFormNested(n); - expectEquals(tc * 10, c); + expectEquals(12345 + tc * 5, closedFormInductionUpN(n)); + expectEquals(12345 - tc * 5, closedFormInductionInAndDownN(12345, n)); + expectEquals(tc * 10, closedFormNestedN(n)); + expectEquals(tc * tc, closedFormNestedNN(n)); } + expectEquals(10, mainIndexReturned()); + expectEquals(1, periodicReturned()); + expectEquals(21, getSum21()); for (int n = -4; n < 4; n++) { int tc = (n <= 0) ? 0 : n; - expectEquals(tc, mainIndexReturned(n)); - expectEquals(tc & 1, periodicReturned(n)); - expectEquals((tc * (tc + 1)) / 2, getSum(n)); + expectEquals(tc, mainIndexReturnedN(n)); + expectEquals(tc & 1, periodicReturnedN(n)); + expectEquals((tc * (tc + 1)) / 2, getSumN(n)); } - expectEquals(21, getSum21()); + expectEquals(10, closedTwice()); expectEquals(20, closedFeed()); expectEquals(-10, closedLargeUp()); expectEquals(10, closedLargeDown()); + expectEquals(50, waterFall()); expectEquals(100, exceptionExitBeforeAdd()); expectEquals(100, exceptionExitAfterAdd()); diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 064fb25306..a858c75fc2 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -263,12 +263,14 @@ 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. -# 902-hello-transformation isn't supported in current form due to linker +# 902-hello-transformation and 903-hello-tagging +# isn't supported in current form due to linker # restrictions. See b/31681198 TEST_ART_BROKEN_TARGET_TESTS := \ 147-stripped-dex-fallback \ 569-checker-pattern-replacement \ - 902-hello-transformation + 902-hello-transformation \ + 903-hello-tagging ifneq (,$(filter target,$(TARGET_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ |