summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/jni/jni_compiler_test.cc5
-rw-r--r--compiler/optimizing/loop_optimization.cc164
-rw-r--r--compiler/optimizing/loop_optimization.h14
-rw-r--r--dexlayout/dex_ir.cc117
-rw-r--r--dexlayout/dex_ir.h29
-rw-r--r--dexlayout/dex_visualize.cc19
-rw-r--r--runtime/openjdkjvmti/OpenjdkJvmTi.cc4
-rw-r--r--runtime/openjdkjvmti/object_tagging.h2
-rw-r--r--test/618-checker-induction/src/Main.java126
-rw-r--r--test/Android.run-test.mk6
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), \