Merge "ART: Fix test dependencies and code"
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 8599471..1a2d9aa 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -355,6 +355,7 @@
"jni/jni_cfi_test.cc",
"optimizing/codegen_test.cc",
"optimizing/load_store_analysis_test.cc",
+ "optimizing/load_store_elimination_test.cc",
"optimizing/optimizing_cfi_test.cc",
"optimizing/scheduler_test.cc",
],
diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc
index 518b0ec..b409eb2 100644
--- a/compiler/dex/inline_method_analyser.cc
+++ b/compiler/dex/inline_method_analyser.cc
@@ -20,6 +20,7 @@
#include "art_method-inl.h"
#include "base/enums.h"
#include "class_linker-inl.h"
+#include "code_item_accessors-inl.h"
#include "dex_file-inl.h"
#include "dex_instruction-inl.h"
#include "dex_instruction.h"
@@ -43,7 +44,7 @@
typedef bool MatchFn(Matcher* matcher);
template <size_t size>
- static bool Match(const DexFile::CodeItem* code_item, MatchFn* const (&pattern)[size]);
+ static bool Match(const CodeItemDataAccessor* code_item, MatchFn* const (&pattern)[size]);
// Match and advance.
@@ -62,22 +63,20 @@
bool IPutOnThis();
private:
- explicit Matcher(const DexFile::CodeItem* code_item)
+ explicit Matcher(const CodeItemDataAccessor* code_item)
: code_item_(code_item),
- instruction_(code_item->Instructions().begin()),
- pos_(0u),
- mark_(0u) { }
+ instruction_(code_item->begin()) {}
- static bool DoMatch(const DexFile::CodeItem* code_item, MatchFn* const* pattern, size_t size);
+ static bool DoMatch(const CodeItemDataAccessor* code_item, MatchFn* const* pattern, size_t size);
- const DexFile::CodeItem* const code_item_;
+ const CodeItemDataAccessor* const code_item_;
DexInstructionIterator instruction_;
- size_t pos_;
- size_t mark_;
+ size_t pos_ = 0u;
+ size_t mark_ = 0u;
};
template <size_t size>
-bool Matcher::Match(const DexFile::CodeItem* code_item, MatchFn* const (&pattern)[size]) {
+bool Matcher::Match(const CodeItemDataAccessor* code_item, MatchFn* const (&pattern)[size]) {
return DoMatch(code_item, pattern, size);
}
@@ -122,12 +121,12 @@
}
bool Matcher::IPutOnThis() {
- DCHECK_NE(code_item_->ins_size_, 0u);
+ DCHECK_NE(code_item_->InsSize(), 0u);
return IsInstructionIPut(instruction_->Opcode()) &&
- instruction_->VRegB_22c() == code_item_->registers_size_ - code_item_->ins_size_;
+ instruction_->VRegB_22c() == code_item_->RegistersSize() - code_item_->InsSize();
}
-bool Matcher::DoMatch(const DexFile::CodeItem* code_item, MatchFn* const* pattern, size_t size) {
+bool Matcher::DoMatch(const CodeItemDataAccessor* code_item, MatchFn* const* pattern, size_t size) {
Matcher matcher(code_item);
while (matcher.pos_ != size) {
if (!pattern[matcher.pos_](&matcher)) {
@@ -158,7 +157,7 @@
// Return the forwarded arguments and check that all remaining arguments are zero.
// If the check fails, return static_cast<size_t>(-1).
-size_t CountForwardedConstructorArguments(const DexFile::CodeItem* code_item,
+size_t CountForwardedConstructorArguments(const CodeItemDataAccessor* code_item,
const Instruction* invoke_direct,
uint16_t zero_vreg_mask) {
DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT);
@@ -167,7 +166,7 @@
uint32_t args[Instruction::kMaxVarArgRegs];
invoke_direct->GetVarArgs(args);
uint16_t this_vreg = args[0];
- DCHECK_EQ(this_vreg, code_item->registers_size_ - code_item->ins_size_); // Checked by verifier.
+ DCHECK_EQ(this_vreg, code_item->RegistersSize() - code_item->InsSize()); // Checked by verifier.
size_t forwarded = 1u;
while (forwarded < number_of_args &&
args[forwarded] == this_vreg + forwarded &&
@@ -249,7 +248,7 @@
return true;
}
-bool DoAnalyseConstructor(const DexFile::CodeItem* code_item,
+bool DoAnalyseConstructor(const CodeItemDataAccessor* code_item,
ArtMethod* method,
/*inout*/ ConstructorIPutData (&iputs)[kMaxConstructorIPuts])
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -292,17 +291,17 @@
DCHECK(method->IsConstructor());
DCHECK(code_item != nullptr);
if (!method->GetDeclaringClass()->IsVerified() ||
- code_item->insns_size_in_code_units_ > kMaxCodeUnits ||
- code_item->registers_size_ > kMaxVRegs ||
+ code_item->InsnsSizeInCodeUnits() > kMaxCodeUnits ||
+ code_item->RegistersSize() > kMaxVRegs ||
!Matcher::Match(code_item, kConstructorPattern)) {
return false;
}
// Verify the invoke, prevent a few odd cases and collect IPUTs.
- uint16_t this_vreg = code_item->registers_size_ - code_item->ins_size_;
+ uint16_t this_vreg = code_item->RegistersSize() - code_item->InsSize();
uint16_t zero_vreg_mask = 0u;
- for (const DexInstructionPcPair& pair : code_item->Instructions()) {
+ for (const DexInstructionPcPair& pair : *code_item) {
const Instruction& instruction = pair.Inst();
if (instruction.Opcode() == Instruction::RETURN_VOID) {
break;
@@ -314,7 +313,7 @@
// We allow forwarding constructors only if they pass more arguments
// to prevent infinite recursion.
if (target_method->GetDeclaringClass() == method->GetDeclaringClass() &&
- instruction.VRegA_35c() <= code_item->ins_size_) {
+ instruction.VRegA_35c() <= code_item->InsSize()) {
return false;
}
size_t forwarded = CountForwardedConstructorArguments(code_item, &instruction, zero_vreg_mask);
@@ -322,14 +321,13 @@
return false;
}
if (target_method->GetDeclaringClass()->IsObjectClass()) {
- DCHECK_EQ(target_method->GetCodeItem()->Instructions().begin()->Opcode(),
- Instruction::RETURN_VOID);
+ DCHECK_EQ(CodeItemDataAccessor(target_method).begin()->Opcode(), Instruction::RETURN_VOID);
} else {
- const DexFile::CodeItem* target_code_item = target_method->GetCodeItem();
- if (target_code_item == nullptr) {
+ CodeItemDataAccessor target_code_item = CodeItemDataAccessor::CreateNullable(target_method);
+ if (!target_code_item.HasCodeItem()) {
return false; // Native constructor?
}
- if (!DoAnalyseConstructor(target_code_item, target_method, iputs)) {
+ if (!DoAnalyseConstructor(&target_code_item, target_method, iputs)) {
return false;
}
// Prune IPUTs with zero input.
@@ -365,7 +363,7 @@
} // anonymous namespace
-bool AnalyseConstructor(const DexFile::CodeItem* code_item,
+bool AnalyseConstructor(const CodeItemDataAccessor* code_item,
ArtMethod* method,
InlineMethod* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -429,27 +427,27 @@
InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), "iget/iput_short variant");
bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) {
- const DexFile::CodeItem* code_item = method->GetCodeItem();
- if (code_item == nullptr) {
+ CodeItemDataAccessor code_item = CodeItemDataAccessor::CreateNullable(method);
+ if (!code_item.HasCodeItem()) {
// Native or abstract.
return false;
}
- return AnalyseMethodCode(code_item,
+ return AnalyseMethodCode(&code_item,
MethodReference(method->GetDexFile(), method->GetDexMethodIndex()),
method->IsStatic(),
method,
result);
}
-bool InlineMethodAnalyser::AnalyseMethodCode(const DexFile::CodeItem* code_item,
+bool InlineMethodAnalyser::AnalyseMethodCode(const CodeItemDataAccessor* code_item,
const MethodReference& method_ref,
bool is_static,
ArtMethod* method,
InlineMethod* result) {
// We currently support only plain return or 2-instruction methods.
- DCHECK_NE(code_item->insns_size_in_code_units_, 0u);
- Instruction::Code opcode = code_item->Instructions().begin()->Opcode();
+ DCHECK_NE(code_item->InsnsSizeInCodeUnits(), 0u);
+ Instruction::Code opcode = code_item->begin()->Opcode();
switch (opcode) {
case Instruction::RETURN_VOID:
@@ -518,15 +516,15 @@
strncmp(method_name, "-", strlen("-")) == 0;
}
-bool InlineMethodAnalyser::AnalyseReturnMethod(const DexFile::CodeItem* code_item,
+bool InlineMethodAnalyser::AnalyseReturnMethod(const CodeItemDataAccessor* code_item,
InlineMethod* result) {
- DexInstructionIterator return_instruction = code_item->Instructions().begin();
+ DexInstructionIterator return_instruction = code_item->begin();
Instruction::Code return_opcode = return_instruction->Opcode();
uint32_t reg = return_instruction->VRegA_11x();
- uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
+ uint32_t arg_start = code_item->RegistersSize() - code_item->InsSize();
DCHECK_GE(reg, arg_start);
DCHECK_LT((return_opcode == Instruction::RETURN_WIDE) ? reg + 1 : reg,
- code_item->registers_size_);
+ code_item->RegistersSize());
if (result != nullptr) {
result->opcode = kInlineOpReturnArg;
@@ -540,9 +538,9 @@
return true;
}
-bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item,
+bool InlineMethodAnalyser::AnalyseConstMethod(const CodeItemDataAccessor* code_item,
InlineMethod* result) {
- DexInstructionIterator instruction = code_item->Instructions().begin();
+ DexInstructionIterator instruction = code_item->begin();
const Instruction* return_instruction = instruction->Next();
Instruction::Code return_opcode = return_instruction->Opcode();
if (return_opcode != Instruction::RETURN &&
@@ -551,13 +549,13 @@
}
int32_t return_reg = return_instruction->VRegA_11x();
- DCHECK_LT(return_reg, code_item->registers_size_);
+ DCHECK_LT(return_reg, code_item->RegistersSize());
int32_t const_value = instruction->VRegB();
if (instruction->Opcode() == Instruction::CONST_HIGH16) {
const_value <<= 16;
}
- DCHECK_LT(instruction->VRegA(), code_item->registers_size_);
+ DCHECK_LT(instruction->VRegA(), code_item->RegistersSize());
if (instruction->VRegA() != return_reg) {
return false; // Not returning the value set by const?
}
@@ -571,12 +569,12 @@
return true;
}
-bool InlineMethodAnalyser::AnalyseIGetMethod(const DexFile::CodeItem* code_item,
+bool InlineMethodAnalyser::AnalyseIGetMethod(const CodeItemDataAccessor* code_item,
const MethodReference& method_ref,
bool is_static,
ArtMethod* method,
InlineMethod* result) {
- DexInstructionIterator instruction = code_item->Instructions().begin();
+ DexInstructionIterator instruction = code_item->begin();
Instruction::Code opcode = instruction->Opcode();
DCHECK(IsInstructionIGet(opcode));
@@ -591,17 +589,17 @@
uint32_t return_reg = return_instruction->VRegA_11x();
DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1 : return_reg,
- code_item->registers_size_);
+ code_item->RegistersSize());
uint32_t dst_reg = instruction->VRegA_22c();
uint32_t object_reg = instruction->VRegB_22c();
uint32_t field_idx = instruction->VRegC_22c();
- uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
+ uint32_t arg_start = code_item->RegistersSize() - code_item->InsSize();
DCHECK_GE(object_reg, arg_start);
- DCHECK_LT(object_reg, code_item->registers_size_);
+ DCHECK_LT(object_reg, code_item->RegistersSize());
uint32_t object_arg = object_reg - arg_start;
- DCHECK_LT(opcode == Instruction::IGET_WIDE ? dst_reg + 1 : dst_reg, code_item->registers_size_);
+ DCHECK_LT(opcode == Instruction::IGET_WIDE ? dst_reg + 1 : dst_reg, code_item->RegistersSize());
if (dst_reg != return_reg) {
return false; // Not returning the value retrieved by IGET?
}
@@ -635,18 +633,18 @@
return true;
}
-bool InlineMethodAnalyser::AnalyseIPutMethod(const DexFile::CodeItem* code_item,
+bool InlineMethodAnalyser::AnalyseIPutMethod(const CodeItemDataAccessor* code_item,
const MethodReference& method_ref,
bool is_static,
ArtMethod* method,
InlineMethod* result) {
- DexInstructionIterator instruction = code_item->Instructions().begin();
+ DexInstructionIterator instruction = code_item->begin();
Instruction::Code opcode = instruction->Opcode();
DCHECK(IsInstructionIPut(opcode));
const Instruction* return_instruction = instruction->Next();
Instruction::Code return_opcode = return_instruction->Opcode();
- uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
+ uint32_t arg_start = code_item->RegistersSize() - code_item->InsSize();
uint16_t return_arg_plus1 = 0u;
if (return_opcode != Instruction::RETURN_VOID) {
if (return_opcode != Instruction::RETURN &&
@@ -658,7 +656,7 @@
uint32_t return_reg = return_instruction->VRegA_11x();
DCHECK_GE(return_reg, arg_start);
DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1u : return_reg,
- code_item->registers_size_);
+ code_item->RegistersSize());
return_arg_plus1 = return_reg - arg_start + 1u;
}
@@ -666,9 +664,9 @@
uint32_t object_reg = instruction->VRegB_22c();
uint32_t field_idx = instruction->VRegC_22c();
DCHECK_GE(object_reg, arg_start);
- DCHECK_LT(object_reg, code_item->registers_size_);
+ DCHECK_LT(object_reg, code_item->RegistersSize());
DCHECK_GE(src_reg, arg_start);
- DCHECK_LT(opcode == Instruction::IPUT_WIDE ? src_reg + 1 : src_reg, code_item->registers_size_);
+ DCHECK_LT(opcode == Instruction::IPUT_WIDE ? src_reg + 1 : src_reg, code_item->RegistersSize());
uint32_t object_arg = object_reg - arg_start;
uint32_t src_arg = src_reg - arg_start;
diff --git a/compiler/dex/inline_method_analyser.h b/compiler/dex/inline_method_analyser.h
index a35e97f..cde2147 100644
--- a/compiler/dex/inline_method_analyser.h
+++ b/compiler/dex/inline_method_analyser.h
@@ -30,6 +30,8 @@
namespace art {
+class CodeItemDataAccessor;
+
namespace verifier {
class MethodVerifier;
} // namespace verifier
@@ -121,21 +123,21 @@
static bool IsSyntheticAccessor(MethodReference ref);
private:
- static bool AnalyseMethodCode(const DexFile::CodeItem* code_item,
+ static bool AnalyseMethodCode(const CodeItemDataAccessor* code_item,
const MethodReference& method_ref,
bool is_static,
ArtMethod* method,
InlineMethod* result)
REQUIRES_SHARED(Locks::mutator_lock_);
- static bool AnalyseReturnMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
- static bool AnalyseConstMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
- static bool AnalyseIGetMethod(const DexFile::CodeItem* code_item,
+ static bool AnalyseReturnMethod(const CodeItemDataAccessor* code_item, InlineMethod* result);
+ static bool AnalyseConstMethod(const CodeItemDataAccessor* code_item, InlineMethod* result);
+ static bool AnalyseIGetMethod(const CodeItemDataAccessor* code_item,
const MethodReference& method_ref,
bool is_static,
ArtMethod* method,
InlineMethod* result)
REQUIRES_SHARED(Locks::mutator_lock_);
- static bool AnalyseIPutMethod(const DexFile::CodeItem* code_item,
+ static bool AnalyseIPutMethod(const CodeItemDataAccessor* code_item,
const MethodReference& method_ref,
bool is_static,
ArtMethod* method,
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 8678fab..66806d8 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -83,7 +83,8 @@
DCHECK(load != nullptr);
DCHECK(load->IsInstanceFieldGet() ||
load->IsStaticFieldGet() ||
- load->IsArrayGet());
+ load->IsArrayGet() ||
+ load->IsVecLoad());
HInstruction* substitute = substitute_instructions_for_loads_[i];
DCHECK(substitute != nullptr);
// Keep tracing substitute till one that's not removed.
@@ -98,7 +99,10 @@
// At this point, stores in possibly_removed_stores_ can be safely removed.
for (HInstruction* store : possibly_removed_stores_) {
- DCHECK(store->IsInstanceFieldSet() || store->IsStaticFieldSet() || store->IsArraySet());
+ DCHECK(store->IsInstanceFieldSet() ||
+ store->IsStaticFieldSet() ||
+ store->IsArraySet() ||
+ store->IsVecStore());
store->GetBlock()->RemoveInstruction(store);
}
@@ -137,7 +141,9 @@
void KeepIfIsStore(HInstruction* heap_value) {
if (heap_value == kDefaultHeapValue ||
heap_value == kUnknownHeapValue ||
- !(heap_value->IsInstanceFieldSet() || heap_value->IsArraySet())) {
+ !(heap_value->IsInstanceFieldSet() ||
+ heap_value->IsArraySet() ||
+ heap_value->IsVecStore())) {
return;
}
auto idx = std::find(possibly_removed_stores_.begin(),
@@ -320,7 +326,9 @@
return;
}
if (heap_value != kUnknownHeapValue) {
- if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) {
+ if (heap_value->IsInstanceFieldSet() ||
+ heap_value->IsArraySet() ||
+ heap_value->IsVecStore()) {
HInstruction* store = heap_value;
// This load must be from a singleton since it's from the same
// field/element that a "removed" store puts the value. That store
@@ -416,7 +424,9 @@
if (!same_value) {
if (possibly_redundant) {
- DCHECK(instruction->IsInstanceFieldSet() || instruction->IsArraySet());
+ DCHECK(instruction->IsInstanceFieldSet() ||
+ instruction->IsArraySet() ||
+ instruction->IsVecStore());
// Put the store as the heap value. If the value is loaded from heap
// by a load later, this store isn't really redundant.
heap_values[idx] = instruction;
@@ -429,8 +439,24 @@
if (i == idx) {
continue;
}
- if (heap_values[i] == value) {
- // Same value should be kept even if aliasing happens.
+ if (heap_values[i] == value && !instruction->IsVecOperation()) {
+ // For field/array, same value should be kept even if aliasing happens.
+ //
+ // For vector values , this is NOT safe. For example:
+ // packed_data = [0xA, 0xB, 0xC, 0xD]; <-- Different values in each lane.
+ // VecStore array[i ,i+1,i+2,i+3] = packed_data;
+ // VecStore array[i+1,i+2,i+3,i+4] = packed_data; <-- We are here (partial overlap).
+ // VecLoad vx = array[i,i+1,i+2,i+3]; <-- Cannot be eliminated.
+ //
+ // TODO: to allow such 'same value' optimization on vector data,
+ // LSA needs to report more fine-grain MAY alias information:
+ // (1) May alias due to two vector data partial overlap.
+ // e.g. a[i..i+3] and a[i+1,..,i+4].
+ // (2) May alias due to two vector data may complete overlap each other.
+ // e.g. a[i..i+3] and b[i..i+3].
+ // (3) May alias but the exact relationship between two locations is unknown.
+ // e.g. a[i..i+3] and b[j..j+3], where values of a,b,i,j are all unknown.
+ // This 'same value' optimization can apply only on case (2).
continue;
}
if (heap_values[i] == kUnknownHeapValue) {
@@ -520,6 +546,32 @@
value);
}
+ void VisitVecLoad(HVecLoad* instruction) OVERRIDE {
+ HInstruction* array = instruction->InputAt(0);
+ HInstruction* index = instruction->InputAt(1);
+ size_t vector_length = instruction->GetVectorLength();
+ VisitGetLocation(instruction,
+ array,
+ HeapLocation::kInvalidFieldOffset,
+ index,
+ vector_length,
+ HeapLocation::kDeclaringClassDefIndexForArrays);
+ }
+
+ void VisitVecStore(HVecStore* instruction) OVERRIDE {
+ HInstruction* array = instruction->InputAt(0);
+ HInstruction* index = instruction->InputAt(1);
+ HInstruction* value = instruction->InputAt(2);
+ size_t vector_length = instruction->GetVectorLength();
+ VisitSetLocation(instruction,
+ array,
+ HeapLocation::kInvalidFieldOffset,
+ index,
+ vector_length,
+ HeapLocation::kDeclaringClassDefIndexForArrays,
+ value);
+ }
+
void VisitDeoptimize(HDeoptimize* instruction) {
const ScopedArenaVector<HInstruction*>& heap_values =
heap_values_for_[instruction->GetBlock()->GetBlockId()];
@@ -529,7 +581,9 @@
continue;
}
// A store is kept as the heap value for possibly removed stores.
- if (heap_value->IsInstanceFieldSet() || heap_value->IsArraySet()) {
+ if (heap_value->IsInstanceFieldSet() ||
+ heap_value->IsArraySet() ||
+ heap_value->IsVecStore()) {
// Check whether the reference for a store is used by an environment local of
// HDeoptimize.
HInstruction* reference = heap_value->InputAt(0);
@@ -687,11 +741,6 @@
return;
}
- // TODO: analyze VecLoad/VecStore better.
- if (graph_->HasSIMD()) {
- return;
- }
-
LSEVisitor lse_visitor(graph_, heap_location_collector, side_effects_, stats_);
for (HBasicBlock* block : graph_->GetReversePostOrder()) {
lse_visitor.VisitBasicBlock(block);
diff --git a/compiler/optimizing/load_store_elimination_test.cc b/compiler/optimizing/load_store_elimination_test.cc
new file mode 100644
index 0000000..6f42d96
--- /dev/null
+++ b/compiler/optimizing/load_store_elimination_test.cc
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2017 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 "side_effects_analysis.h"
+#include "load_store_analysis.h"
+#include "load_store_elimination.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+class LoadStoreEliminationTest : public OptimizingUnitTest {
+ public:
+ LoadStoreEliminationTest() : pool_() {}
+
+ void PerformLSE() {
+ graph_->BuildDominatorTree();
+ SideEffectsAnalysis side_effects(graph_);
+ side_effects.Run();
+ LoadStoreAnalysis lsa(graph_);
+ lsa.Run();
+ LoadStoreElimination lse(graph_, side_effects, lsa, nullptr);
+ lse.Run();
+ }
+
+ void CreateTestControlFlowGraph() {
+ graph_ = CreateGraph();
+
+ entry_ = new (GetAllocator()) HBasicBlock(graph_);
+ pre_header_ = new (GetAllocator()) HBasicBlock(graph_);
+ loop_header_ = new (GetAllocator()) HBasicBlock(graph_);
+ loop_body_ = new (GetAllocator()) HBasicBlock(graph_);
+ exit_ = new (GetAllocator()) HBasicBlock(graph_);
+
+ graph_->AddBlock(entry_);
+ graph_->AddBlock(pre_header_);
+ graph_->AddBlock(loop_header_);
+ graph_->AddBlock(loop_body_);
+ graph_->AddBlock(exit_);
+
+ graph_->SetEntryBlock(entry_);
+
+ // This common CFG has been used by all cases in this load_store_elimination_test.
+ // entry
+ // |
+ // pre_header
+ // |
+ // loop_header <--+
+ // | |
+ // loop_body -----+
+ // |
+ // exit
+
+ entry_->AddSuccessor(pre_header_);
+ pre_header_->AddSuccessor(loop_header_);
+ loop_header_->AddSuccessor(exit_); // true successor
+ loop_header_->AddSuccessor(loop_body_); // false successor
+ loop_body_->AddSuccessor(loop_header_);
+
+ HInstruction* c0 = graph_->GetIntConstant(0);
+ HInstruction* c1 = graph_->GetIntConstant(1);
+ HInstruction* c4 = graph_->GetIntConstant(4);
+ HInstruction* c128 = graph_->GetIntConstant(128);
+
+ // entry block has following instructions:
+ // array, i, j, i+1, i+4.
+ array_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
+ dex::TypeIndex(0),
+ 0,
+ DataType::Type::kReference);
+ i_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
+ dex::TypeIndex(1),
+ 1,
+ DataType::Type::kInt32);
+ j_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
+ dex::TypeIndex(1),
+ 2,
+ DataType::Type::kInt32);
+ i_add1_ = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_, c1);
+ i_add4_ = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_, c4);
+ entry_->AddInstruction(array_);
+ entry_->AddInstruction(i_);
+ entry_->AddInstruction(j_);
+ entry_->AddInstruction(i_add1_);
+ entry_->AddInstruction(i_add4_);
+ entry_->AddInstruction(new (GetAllocator()) HGoto());
+
+ // pre_header block
+ pre_header_->AddInstruction(new (GetAllocator()) HGoto());
+
+ // loop header block has following instructions:
+ // phi = 0;
+ // if (phi >= 128);
+ phi_ = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
+ cmp_ = new (GetAllocator()) HGreaterThanOrEqual(phi_, c128);
+ if_ = new (GetAllocator()) HIf(cmp_);
+ loop_header_->AddPhi(phi_);
+ loop_header_->AddInstruction(cmp_);
+ loop_header_->AddInstruction(if_);
+ phi_->AddInput(c0);
+
+ // loop body block has following instructions:
+ // phi++;
+ HInstruction* inc_phi = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi_, c1);
+ loop_body_->AddInstruction(inc_phi);
+ loop_body_->AddInstruction(new (GetAllocator()) HGoto());
+ phi_->AddInput(inc_phi);
+
+ // exit block
+ exit_->AddInstruction(new (GetAllocator()) HExit());
+ }
+
+ // To avoid tedious HIR assembly in test functions.
+ HInstruction* AddVecLoad(HBasicBlock* block, HInstruction* array, HInstruction* index) {
+ DCHECK(block != nullptr);
+ DCHECK(array != nullptr);
+ DCHECK(index != nullptr);
+ HInstruction* vload = new (GetAllocator()) HVecLoad(
+ GetAllocator(),
+ array,
+ index,
+ DataType::Type::kInt32,
+ SideEffects::ArrayReadOfType(DataType::Type::kInt32),
+ 4,
+ /*is_string_char_at*/ false,
+ kNoDexPc);
+ block->InsertInstructionBefore(vload, block->GetLastInstruction());
+ return vload;
+ }
+
+ HInstruction* AddVecStore(HBasicBlock* block,
+ HInstruction* array,
+ HInstruction* index,
+ HVecOperation* vdata = nullptr) {
+ DCHECK(block != nullptr);
+ DCHECK(array != nullptr);
+ DCHECK(index != nullptr);
+ if (vdata == nullptr) {
+ HInstruction* c1 = graph_->GetIntConstant(1);
+ vdata = new (GetAllocator()) HVecReplicateScalar(GetAllocator(),
+ c1,
+ DataType::Type::kInt32,
+ 4,
+ kNoDexPc);
+ block->InsertInstructionBefore(vdata, block->GetLastInstruction());
+ }
+ HInstruction* vstore = new (GetAllocator()) HVecStore(
+ GetAllocator(),
+ array,
+ index,
+ vdata,
+ DataType::Type::kInt32,
+ SideEffects::ArrayWriteOfType(DataType::Type::kInt32),
+ 4,
+ kNoDexPc);
+ block->InsertInstructionBefore(vstore, block->GetLastInstruction());
+ return vstore;
+ }
+
+ HInstruction* AddArrayGet(HBasicBlock* block, HInstruction* array, HInstruction* index) {
+ DCHECK(block != nullptr);
+ DCHECK(array != nullptr);
+ DCHECK(index != nullptr);
+ HInstruction* get = new (GetAllocator()) HArrayGet(array, index, DataType::Type::kInt32, 0);
+ block->InsertInstructionBefore(get, block->GetLastInstruction());
+ return get;
+ }
+
+ HInstruction* AddArraySet(HBasicBlock* block,
+ HInstruction* array,
+ HInstruction* index,
+ HInstruction* data = nullptr) {
+ DCHECK(block != nullptr);
+ DCHECK(array != nullptr);
+ DCHECK(index != nullptr);
+ if (data == nullptr) {
+ data = graph_->GetIntConstant(1);
+ }
+ HInstruction* store = new (GetAllocator()) HArraySet(array,
+ index,
+ data,
+ DataType::Type::kInt32,
+ 0);
+ block->InsertInstructionBefore(store, block->GetLastInstruction());
+ return store;
+ }
+
+ ArenaPool pool_;
+
+ HGraph* graph_;
+ HBasicBlock* entry_;
+ HBasicBlock* pre_header_;
+ HBasicBlock* loop_header_;
+ HBasicBlock* loop_body_;
+ HBasicBlock* exit_;
+
+ HInstruction* array_;
+ HInstruction* i_;
+ HInstruction* j_;
+ HInstruction* i_add1_;
+ HInstruction* i_add4_;
+
+ HPhi* phi_;
+ HInstruction* cmp_;
+ HInstruction* if_;
+};
+
+TEST_F(LoadStoreEliminationTest, ArrayGetSetElimination) {
+ CreateTestControlFlowGraph();
+
+ HInstruction* c1 = graph_->GetIntConstant(1);
+ HInstruction* c2 = graph_->GetIntConstant(2);
+ HInstruction* c3 = graph_->GetIntConstant(3);
+
+ // array[1] = 1;
+ // x = array[1]; <--- Remove.
+ // y = array[2];
+ // array[1] = 1; <--- Remove, since it stores same value.
+ // array[i] = 3; <--- MAY alias.
+ // array[1] = 1; <--- Cannot remove, even if it stores the same value.
+ AddArraySet(entry_, array_, c1, c1);
+ HInstruction* load1 = AddArrayGet(entry_, array_, c1);
+ HInstruction* load2 = AddArrayGet(entry_, array_, c2);
+ HInstruction* store1 = AddArraySet(entry_, array_, c1, c1);
+ AddArraySet(entry_, array_, i_, c3);
+ HInstruction* store2 = AddArraySet(entry_, array_, c1, c1);
+
+ PerformLSE();
+
+ ASSERT_TRUE(IsRemoved(load1));
+ ASSERT_FALSE(IsRemoved(load2));
+ ASSERT_TRUE(IsRemoved(store1));
+ ASSERT_FALSE(IsRemoved(store2));
+}
+
+TEST_F(LoadStoreEliminationTest, SameHeapValue) {
+ CreateTestControlFlowGraph();
+
+ HInstruction* c1 = graph_->GetIntConstant(1);
+ HInstruction* c2 = graph_->GetIntConstant(2);
+
+ // Test LSE handling same value stores on array.
+ // array[1] = 1;
+ // array[2] = 1;
+ // array[1] = 1; <--- Can remove.
+ // array[1] = 2; <--- Can NOT remove.
+ AddArraySet(entry_, array_, c1, c1);
+ AddArraySet(entry_, array_, c2, c1);
+ HInstruction* store1 = AddArraySet(entry_, array_, c1, c1);
+ HInstruction* store2 = AddArraySet(entry_, array_, c1, c2);
+
+ // Test LSE handling same value stores on vector.
+ // vdata = [0x1, 0x2, 0x3, 0x4, ...]
+ // VecStore array[i...] = vdata;
+ // VecStore array[j...] = vdata; <--- MAY ALIAS.
+ // VecStore array[i...] = vdata; <--- Cannot Remove, even if it's same value.
+ AddVecStore(entry_, array_, i_);
+ AddVecStore(entry_, array_, j_);
+ HInstruction* vstore1 = AddVecStore(entry_, array_, i_);
+
+ // VecStore array[i...] = vdata;
+ // VecStore array[i+1...] = vdata; <--- MAY alias due to partial overlap.
+ // VecStore array[i...] = vdata; <--- Cannot remove, even if it's same value.
+ AddVecStore(entry_, array_, i_);
+ AddVecStore(entry_, array_, i_add1_);
+ HInstruction* vstore2 = AddVecStore(entry_, array_, i_);
+
+ PerformLSE();
+
+ ASSERT_TRUE(IsRemoved(store1));
+ ASSERT_FALSE(IsRemoved(store2));
+ ASSERT_FALSE(IsRemoved(vstore1));
+ ASSERT_FALSE(IsRemoved(vstore2));
+}
+
+TEST_F(LoadStoreEliminationTest, OverlappingLoadStore) {
+ CreateTestControlFlowGraph();
+
+ HInstruction* c1 = graph_->GetIntConstant(1);
+
+ // Test LSE handling array LSE when there is vector store in between.
+ // a[i] = 1;
+ // .. = a[i]; <-- Remove.
+ // a[i,i+1,i+2,i+3] = data; <-- PARTIAL OVERLAP !
+ // .. = a[i]; <-- Cannot remove.
+ AddArraySet(entry_, array_, i_, c1);
+ HInstruction* load1 = AddArrayGet(entry_, array_, i_);
+ AddVecStore(entry_, array_, i_);
+ HInstruction* load2 = AddArrayGet(entry_, array_, i_);
+
+ // Test LSE handling vector load/store partial overlap.
+ // a[i,i+1,i+2,i+3] = data;
+ // a[i+4,i+5,i+6,i+7] = data;
+ // .. = a[i,i+1,i+2,i+3];
+ // .. = a[i+4,i+5,i+6,i+7];
+ // a[i+1,i+2,i+3,i+4] = data; <-- PARTIAL OVERLAP !
+ // .. = a[i,i+1,i+2,i+3];
+ // .. = a[i+4,i+5,i+6,i+7];
+ AddVecStore(entry_, array_, i_);
+ AddVecStore(entry_, array_, i_add4_);
+ HInstruction* vload1 = AddVecLoad(entry_, array_, i_);
+ HInstruction* vload2 = AddVecLoad(entry_, array_, i_add4_);
+ AddVecStore(entry_, array_, i_add1_);
+ HInstruction* vload3 = AddVecLoad(entry_, array_, i_);
+ HInstruction* vload4 = AddVecLoad(entry_, array_, i_add4_);
+
+ // Test LSE handling vector LSE when there is array store in between.
+ // a[i,i+1,i+2,i+3] = data;
+ // a[i+1] = 1; <-- PARTIAL OVERLAP !
+ // .. = a[i,i+1,i+2,i+3];
+ AddVecStore(entry_, array_, i_);
+ AddArraySet(entry_, array_, i_, c1);
+ HInstruction* vload5 = AddVecLoad(entry_, array_, i_);
+
+ PerformLSE();
+
+ ASSERT_TRUE(IsRemoved(load1));
+ ASSERT_FALSE(IsRemoved(load2));
+
+ ASSERT_TRUE(IsRemoved(vload1));
+ ASSERT_TRUE(IsRemoved(vload2));
+ ASSERT_FALSE(IsRemoved(vload3));
+ ASSERT_FALSE(IsRemoved(vload4));
+
+ ASSERT_FALSE(IsRemoved(vload5));
+}
+
+// function (int[] a, int j) {
+// a[j] = 1;
+// for (int i=0; i<128; i++) {
+// /* doesn't do any write */
+// }
+// a[j] = 1;
+TEST_F(LoadStoreEliminationTest, Loop1) {
+ CreateTestControlFlowGraph();
+
+ HInstruction* c1 = graph_->GetIntConstant(1);
+
+ // a[j] = 1
+ AddArraySet(pre_header_, array_, j_, c1);
+
+ // LOOP BODY:
+ // .. = a[i,i+1,i+2,i+3];
+ AddVecLoad(loop_body_, array_, phi_);
+
+ // a[j] = 1;
+ HInstruction* array_set = AddArraySet(exit_, array_, j_, c1);
+
+ PerformLSE();
+
+ ASSERT_TRUE(IsRemoved(array_set));
+}
+
+// function (int[] a, int index) {
+// a[index] = 1;
+// int[] b = new int[128];
+// for (int i=0; i<128; i++) {
+// a[i,i+1,i+2,i+3] = vdata;
+// b[i,i+1,i+2,i+3] = a[i,i+1,i+2,i+3];
+// }
+// a[index] = 1;
+// }
+TEST_F(LoadStoreEliminationTest, Loop2) {
+ CreateTestControlFlowGraph();
+
+ HInstruction* c0 = graph_->GetIntConstant(0);
+ HInstruction* c1 = graph_->GetIntConstant(1);
+ HInstruction* c128 = graph_->GetIntConstant(128);
+
+ HInstruction* array_b = new (GetAllocator()) HNewArray(c0, c128, 0);
+ entry_->AddInstruction(array_b);
+
+ // a[index] = 1;
+ AddArraySet(pre_header_, array_, i_, c1);
+
+ // a[i,i+1,i+2,i+3] = vdata;
+ // b[i,i+1,i+2,i+3] = a[i,i+1,i+2,i+3];
+ AddVecStore(loop_body_, array_, phi_);
+ HInstruction* vload = AddVecLoad(loop_body_, array_, phi_);
+ AddVecStore(loop_body_, array_b, phi_, vload->AsVecLoad());
+
+ // a[index] = 1;
+ HInstruction* a_set = AddArraySet(exit_, array_, i_, c1);
+
+ PerformLSE();
+
+ ASSERT_TRUE(IsRemoved(vload));
+ ASSERT_FALSE(IsRemoved(a_set)); // Cannot remove due to side effect in loop.
+}
+
+} // namespace art
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index afca26d..c2556aa 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -941,7 +941,7 @@
class_it.Next()) {
if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) {
for (const DexInstructionPcPair& inst :
- class_it.GetMethodCodeItem()->Instructions()) {
+ class_it.GetMethodCodeItem()->Instructions()) {
ASSERT_FALSE(inst->IsQuickened());
}
}
diff --git a/runtime/Android.bp b/runtime/Android.bp
index e032238..69e4434 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -567,6 +567,7 @@
"class_linker_test.cc",
"class_loader_context_test.cc",
"class_table_test.cc",
+ "code_item_accessors_test.cc",
"compiler_filter_test.cc",
"dex_file_test.cc",
"dex_file_verifier_test.cc",
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index e1671c9..50913de 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -23,6 +23,7 @@
#include "base/callee_save_type.h"
#include "base/logging.h"
#include "class_linker-inl.h"
+#include "code_item_accessors-inl.h"
#include "common_throws.h"
#include "dex_file-inl.h"
#include "dex_file_annotations.h"
@@ -459,6 +460,18 @@
}
}
+inline IterationRange<DexInstructionIterator> ArtMethod::DexInstructions() {
+ CodeItemInstructionAccessor accessor(this);
+ return { accessor.begin(),
+ accessor.end() };
+}
+
+inline IterationRange<DexInstructionIterator> ArtMethod::NullableDexInstructions() {
+ CodeItemInstructionAccessor accessor(CodeItemInstructionAccessor::CreateNullable(this));
+ return { accessor.begin(),
+ accessor.end() };
+}
+
} // namespace art
#endif // ART_RUNTIME_ART_METHOD_INL_H_
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 0e98d47..c17eef1 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -22,8 +22,10 @@
#include "base/bit_utils.h"
#include "base/casts.h"
#include "base/enums.h"
+#include "base/iteration_range.h"
#include "base/logging.h"
#include "dex_file.h"
+#include "dex_instruction_iterator.h"
#include "gc_root.h"
#include "modifiers.h"
#include "obj_ptr.h"
@@ -710,6 +712,15 @@
"ptr_sized_fields_.entry_point_from_quick_compiled_code_");
}
+ // Returns the dex instructions of the code item for the art method. Must not be called on null
+ // code items.
+ ALWAYS_INLINE IterationRange<DexInstructionIterator> DexInstructions()
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Handles a null code item by returning iterators that have a null address.
+ ALWAYS_INLINE IterationRange<DexInstructionIterator> NullableDexInstructions()
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
protected:
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
// The class we are a part of.
diff --git a/runtime/base/casts.h b/runtime/base/casts.h
index 0cbabba..92c493a 100644
--- a/runtime/base/casts.h
+++ b/runtime/base/casts.h
@@ -77,6 +77,14 @@
return static_cast<To>(f);
}
+template<typename To, typename From> // use like this: down_cast<T&>(foo);
+inline To down_cast(From& f) { // so we only accept references
+ static_assert(std::is_base_of<From, typename std::remove_reference<To>::type>::value,
+ "down_cast unsafe as To is not a subtype of From");
+
+ return static_cast<To>(f);
+}
+
template <class Dest, class Source>
inline Dest bit_cast(const Source& source) {
// Compile time assertion: sizeof(Dest) == sizeof(Source)
diff --git a/runtime/cdex/compact_dex_file.h b/runtime/cdex/compact_dex_file.h
index 8ab9247..f17f8cf 100644
--- a/runtime/cdex/compact_dex_file.h
+++ b/runtime/cdex/compact_dex_file.h
@@ -24,11 +24,18 @@
// CompactDex is a currently ART internal dex file format that aims to reduce storage/RAM usage.
class CompactDexFile : public DexFile {
public:
+ static constexpr uint8_t kDexMagic[kDexMagicSize] = { 'c', 'd', 'e', 'x' };
+ static constexpr uint8_t kDexMagicVersion[] = {'0', '0', '1', '\0'};
+
class Header : public DexFile::Header {
// Same for now.
};
- static constexpr uint8_t kDexMagic[kDexMagicSize] = { 'c', 'd', 'e', 'x' };
- static constexpr uint8_t kDexMagicVersion[] = {'0', '0', '1', '\0'};
+
+ struct CodeItem : public DexFile::CodeItem {
+ private:
+ // TODO: Insert compact dex specific fields here.
+ DISALLOW_COPY_AND_ASSIGN(CodeItem);
+ };
// Write the compact dex specific magic.
static void WriteMagic(uint8_t* magic);
@@ -44,10 +51,6 @@
static bool IsVersionValid(const uint8_t* magic);
virtual bool IsVersionValid() const OVERRIDE;
- bool IsCompactDexFile() const OVERRIDE {
- return true;
- }
-
private:
// Not supported yet.
CompactDexFile(const uint8_t* base,
@@ -56,7 +59,13 @@
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
DexFileContainer* container)
- : DexFile(base, size, location, location_checksum, oat_dex_file, container) {}
+ : DexFile(base,
+ size,
+ location,
+ location_checksum,
+ oat_dex_file,
+ container,
+ /*is_compact_dex*/ true) {}
friend class DexFile;
friend class DexFileLoader;
diff --git a/runtime/code_item_accessors-inl.h b/runtime/code_item_accessors-inl.h
new file mode 100644
index 0000000..61b5175
--- /dev/null
+++ b/runtime/code_item_accessors-inl.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017 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_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_
+#define ART_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_
+
+#include "code_item_accessors.h"
+
+#include "art_method-inl.h"
+#include "cdex/compact_dex_file.h"
+#include "standard_dex_file.h"
+
+namespace art {
+
+inline void CodeItemInstructionAccessor::Init(const CompactDexFile::CodeItem& code_item) {
+ insns_size_in_code_units_ = code_item.insns_size_in_code_units_;
+ insns_ = code_item.insns_;
+}
+
+inline void CodeItemInstructionAccessor::Init(const StandardDexFile::CodeItem& code_item) {
+ insns_size_in_code_units_ = code_item.insns_size_in_code_units_;
+ insns_ = code_item.insns_;
+}
+
+inline void CodeItemInstructionAccessor::Init(const DexFile* dex_file,
+ const DexFile::CodeItem* code_item) {
+ DCHECK(dex_file != nullptr);
+ DCHECK(code_item != nullptr);
+ if (dex_file->IsCompactDexFile()) {
+ Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
+ } else {
+ DCHECK(dex_file->IsStandardDexFile());
+ Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
+ }
+}
+
+inline CodeItemInstructionAccessor::CodeItemInstructionAccessor(
+ const DexFile* dex_file,
+ const DexFile::CodeItem* code_item) {
+ Init(dex_file, code_item);
+}
+
+inline CodeItemInstructionAccessor::CodeItemInstructionAccessor(ArtMethod* method)
+ : CodeItemInstructionAccessor(method->GetDexFile(), method->GetCodeItem()) {}
+
+inline DexInstructionIterator CodeItemInstructionAccessor::begin() const {
+ return DexInstructionIterator(insns_, 0u);
+}
+
+inline DexInstructionIterator CodeItemInstructionAccessor::end() const {
+ return DexInstructionIterator(insns_, insns_size_in_code_units_);
+}
+
+inline CodeItemInstructionAccessor CodeItemInstructionAccessor::CreateNullable(
+ ArtMethod* method) {
+ DCHECK(method != nullptr);
+ CodeItemInstructionAccessor ret;
+ const DexFile::CodeItem* code_item = method->GetCodeItem();
+ if (code_item != nullptr) {
+ ret.Init(method->GetDexFile(), code_item);
+ } else {
+ DCHECK(!ret.HasCodeItem()) << "Should be null initialized";
+ }
+ return ret;
+}
+
+inline void CodeItemDataAccessor::Init(const CompactDexFile::CodeItem& code_item) {
+ CodeItemInstructionAccessor::Init(code_item);
+ registers_size_ = code_item.registers_size_;
+ ins_size_ = code_item.ins_size_;
+ outs_size_ = code_item.outs_size_;
+ tries_size_ = code_item.tries_size_;
+}
+
+inline void CodeItemDataAccessor::Init(const StandardDexFile::CodeItem& code_item) {
+ CodeItemInstructionAccessor::Init(code_item);
+ registers_size_ = code_item.registers_size_;
+ ins_size_ = code_item.ins_size_;
+ outs_size_ = code_item.outs_size_;
+ tries_size_ = code_item.tries_size_;
+}
+
+inline void CodeItemDataAccessor::Init(const DexFile* dex_file,
+ const DexFile::CodeItem* code_item) {
+ DCHECK(dex_file != nullptr);
+ DCHECK(code_item != nullptr);
+ if (dex_file->IsCompactDexFile()) {
+ CodeItemDataAccessor::Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
+ } else {
+ DCHECK(dex_file->IsStandardDexFile());
+ CodeItemDataAccessor::Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
+ }
+}
+
+inline CodeItemDataAccessor::CodeItemDataAccessor(const DexFile* dex_file,
+ const DexFile::CodeItem* code_item) {
+ Init(dex_file, code_item);
+}
+
+inline CodeItemDataAccessor::CodeItemDataAccessor(ArtMethod* method)
+ : CodeItemDataAccessor(method->GetDexFile(), method->GetCodeItem()) {}
+
+inline CodeItemDataAccessor CodeItemDataAccessor::CreateNullable(ArtMethod* method) {
+ DCHECK(method != nullptr);
+ CodeItemDataAccessor ret;
+ const DexFile::CodeItem* code_item = method->GetCodeItem();
+ if (code_item != nullptr) {
+ ret.Init(method->GetDexFile(), code_item);
+ } else {
+ DCHECK(!ret.HasCodeItem()) << "Should be null initialized";
+ }
+ return ret;
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_
diff --git a/runtime/code_item_accessors.h b/runtime/code_item_accessors.h
new file mode 100644
index 0000000..fcece3e
--- /dev/null
+++ b/runtime/code_item_accessors.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// TODO: Dex helpers have ART specific APIs, we may want to refactor these for use in dexdump.
+
+#ifndef ART_RUNTIME_CODE_ITEM_ACCESSORS_H_
+#define ART_RUNTIME_CODE_ITEM_ACCESSORS_H_
+
+#include "base/mutex.h"
+#include "cdex/compact_dex_file.h"
+#include "dex_file.h"
+#include "dex_instruction_iterator.h"
+#include "standard_dex_file.h"
+
+namespace art {
+
+class ArtMethod;
+
+// Abstracts accesses to the instruction fields of code items for CompactDexFile and
+// StandardDexFile.
+class CodeItemInstructionAccessor {
+ public:
+ ALWAYS_INLINE CodeItemInstructionAccessor(const DexFile* dex_file,
+ const DexFile::CodeItem* code_item);
+
+ ALWAYS_INLINE explicit CodeItemInstructionAccessor(ArtMethod* method);
+
+ ALWAYS_INLINE DexInstructionIterator begin() const;
+
+ ALWAYS_INLINE DexInstructionIterator end() const;
+
+ uint32_t InsnsSizeInCodeUnits() const {
+ return insns_size_in_code_units_;
+ }
+
+ const uint16_t* Insns() const {
+ return insns_;
+ }
+
+ // Return true if the accessor has a code item.
+ bool HasCodeItem() const {
+ return Insns() != nullptr;
+ }
+
+ // CreateNullable allows ArtMethods that have a null code item.
+ ALWAYS_INLINE static CodeItemInstructionAccessor CreateNullable(ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ protected:
+ CodeItemInstructionAccessor() = default;
+
+ ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item);
+ ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item);
+ ALWAYS_INLINE void Init(const DexFile* dex_file, const DexFile::CodeItem* code_item);
+
+ private:
+ // size of the insns array, in 2 byte code units. 0 if there is no code item.
+ uint32_t insns_size_in_code_units_ = 0;
+
+ // Pointer to the instructions, null if there is no code item.
+ const uint16_t* insns_ = 0;
+};
+
+// Abstracts accesses to code item fields other than debug info for CompactDexFile and
+// StandardDexFile.
+class CodeItemDataAccessor : public CodeItemInstructionAccessor {
+ public:
+ ALWAYS_INLINE CodeItemDataAccessor(const DexFile* dex_file, const DexFile::CodeItem* code_item);
+
+ ALWAYS_INLINE explicit CodeItemDataAccessor(ArtMethod* method);
+
+ uint16_t RegistersSize() const {
+ return registers_size_;
+ }
+
+ uint16_t InsSize() const {
+ return ins_size_;
+ }
+
+ uint16_t OutsSize() const {
+ return outs_size_;
+ }
+
+ uint16_t TriesSize() const {
+ return tries_size_;
+ }
+
+ // CreateNullable allows ArtMethods that have a null code item.
+ ALWAYS_INLINE static CodeItemDataAccessor CreateNullable(ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ protected:
+ CodeItemDataAccessor() = default;
+
+ ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item);
+ ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item);
+ ALWAYS_INLINE void Init(const DexFile* dex_file, const DexFile::CodeItem* code_item);
+
+ private:
+ // Fields mirrored from the dex/cdex code item.
+ uint16_t registers_size_;
+ uint16_t ins_size_;
+ uint16_t outs_size_;
+ uint16_t tries_size_;
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_CODE_ITEM_ACCESSORS_H_
diff --git a/runtime/code_item_accessors_test.cc b/runtime/code_item_accessors_test.cc
new file mode 100644
index 0000000..ef5d246
--- /dev/null
+++ b/runtime/code_item_accessors_test.cc
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 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 "code_item_accessors-inl.h"
+
+#include <memory>
+
+#include "common_runtime_test.h"
+#include "dex_file_loader.h"
+#include "mem_map.h"
+
+namespace art {
+
+class CodeItemAccessorsTest : public CommonRuntimeTest {};
+
+std::unique_ptr<const DexFile> CreateFakeDex(bool compact_dex) {
+ std::string error_msg;
+ std::unique_ptr<MemMap> map(
+ MemMap::MapAnonymous(/*name*/ "map",
+ /*addr*/ nullptr,
+ /*byte_count*/ kPageSize,
+ PROT_READ | PROT_WRITE,
+ /*low_4gb*/ false,
+ /*reuse*/ false,
+ &error_msg));
+ CHECK(map != nullptr) << error_msg;
+ if (compact_dex) {
+ CompactDexFile::WriteMagic(map->Begin());
+ CompactDexFile::WriteCurrentVersion(map->Begin());
+ } else {
+ StandardDexFile::WriteMagic(map->Begin());
+ StandardDexFile::WriteCurrentVersion(map->Begin());
+ }
+ std::unique_ptr<const DexFile> dex(
+ DexFileLoader::Open("location",
+ /*location_checksum*/ 123,
+ std::move(map),
+ /*verify*/false,
+ /*verify_checksum*/false,
+ &error_msg));
+ CHECK(dex != nullptr) << error_msg;
+ return dex;
+}
+
+TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor) {
+ MemMap::Init();
+ std::unique_ptr<const DexFile> standard_dex(CreateFakeDex(/*compact_dex*/false));
+ ASSERT_TRUE(standard_dex != nullptr);
+ std::unique_ptr<const DexFile> compact_dex(CreateFakeDex(/*compact_dex*/true));
+ ASSERT_TRUE(compact_dex != nullptr);
+ static constexpr uint16_t kRegisterSize = 1;
+ static constexpr uint16_t kInsSize = 2;
+ static constexpr uint16_t kOutsSize = 3;
+ static constexpr uint16_t kTriesSize = 4;
+ // debug_info_off_ is not accessible from the helpers yet.
+ static constexpr size_t kInsnsSizeInCodeUnits = 5;
+
+ auto verify_code_item = [&](const DexFile* dex,
+ const DexFile::CodeItem* item,
+ const uint16_t* insns) {
+ CodeItemInstructionAccessor insns_accessor(dex, item);
+ EXPECT_TRUE(insns_accessor.HasCodeItem());
+ ASSERT_EQ(insns_accessor.InsnsSizeInCodeUnits(), kInsnsSizeInCodeUnits);
+ EXPECT_EQ(insns_accessor.Insns(), insns);
+
+ CodeItemDataAccessor data_accessor(dex, item);
+ EXPECT_TRUE(data_accessor.HasCodeItem());
+ EXPECT_EQ(data_accessor.InsnsSizeInCodeUnits(), kInsnsSizeInCodeUnits);
+ EXPECT_EQ(data_accessor.Insns(), insns);
+ EXPECT_EQ(data_accessor.RegistersSize(), kRegisterSize);
+ EXPECT_EQ(data_accessor.InsSize(), kInsSize);
+ EXPECT_EQ(data_accessor.OutsSize(), kOutsSize);
+ EXPECT_EQ(data_accessor.TriesSize(), kTriesSize);
+ };
+
+ uint8_t buffer1[sizeof(CompactDexFile::CodeItem) + kInsnsSizeInCodeUnits * sizeof(uint16_t)] = {};
+ CompactDexFile::CodeItem* dex_code_item = reinterpret_cast<CompactDexFile::CodeItem*>(buffer1);
+ dex_code_item->registers_size_ = kRegisterSize;
+ dex_code_item->ins_size_ = kInsSize;
+ dex_code_item->outs_size_ = kOutsSize;
+ dex_code_item->tries_size_ = kTriesSize;
+ dex_code_item->insns_size_in_code_units_ = kInsnsSizeInCodeUnits;
+ verify_code_item(compact_dex.get(), dex_code_item, dex_code_item->insns_);
+
+ uint8_t buffer2[sizeof(CompactDexFile::CodeItem) + kInsnsSizeInCodeUnits * sizeof(uint16_t)] = {};
+ CompactDexFile::CodeItem* cdex_code_item = reinterpret_cast<CompactDexFile::CodeItem*>(buffer2);
+ cdex_code_item->registers_size_ = kRegisterSize;
+ cdex_code_item->ins_size_ = kInsSize;
+ cdex_code_item->outs_size_ = kOutsSize;
+ cdex_code_item->tries_size_ = kTriesSize;
+ cdex_code_item->insns_size_in_code_units_ = kInsnsSizeInCodeUnits;
+ verify_code_item(compact_dex.get(), cdex_code_item, cdex_code_item->insns_);
+}
+
+} // namespace art
diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h
index 5dfbd9b..58cd486 100644
--- a/runtime/dex_file-inl.h
+++ b/runtime/dex_file-inl.h
@@ -21,9 +21,11 @@
#include "base/casts.h"
#include "base/logging.h"
#include "base/stringpiece.h"
+#include "cdex/compact_dex_file.h"
#include "dex_file.h"
#include "invoke_type.h"
#include "leb128.h"
+#include "standard_dex_file.h"
namespace art {
@@ -495,6 +497,16 @@
context);
}
+inline const CompactDexFile* DexFile::AsCompactDexFile() const {
+ DCHECK(IsCompactDexFile());
+ return down_cast<const CompactDexFile*>(this);
+}
+
+inline const StandardDexFile* DexFile::AsStandardDexFile() const {
+ DCHECK(IsStandardDexFile());
+ return down_cast<const StandardDexFile*>(this);
+}
+
} // namespace art
#endif // ART_RUNTIME_DEX_FILE_INL_H_
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 974c7ac..7b0c46b 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -77,7 +77,8 @@
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
- DexFileContainer* container)
+ DexFileContainer* container,
+ bool is_compact_dex)
: begin_(base),
size_(size),
location_(location),
@@ -94,7 +95,8 @@
call_site_ids_(nullptr),
num_call_site_ids_(0),
oat_dex_file_(oat_dex_file),
- container_(container) {
+ container_(container),
+ is_compact_dex_(is_compact_dex) {
CHECK(begin_ != nullptr) << GetLocation();
CHECK_GT(size_, 0U) << GetLocation();
// Check base (=header) alignment.
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 5c9b258..5c0093f 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -32,10 +32,12 @@
namespace art {
+class CompactDexFile;
enum InvokeType : uint32_t;
class MemMap;
class OatDexFile;
class Signature;
+class StandardDexFile;
class StringPiece;
class ZipArchive;
@@ -993,13 +995,15 @@
// Returns a human-readable form of the type at an index.
std::string PrettyType(dex::TypeIndex type_idx) const;
- // Helper functions.
- virtual bool IsCompactDexFile() const {
- return false;
+ // Not virtual for performance reasons.
+ ALWAYS_INLINE bool IsCompactDexFile() const {
+ return is_compact_dex_;
}
- virtual bool IsStandardDexFile() const {
- return false;
+ ALWAYS_INLINE bool IsStandardDexFile() const {
+ return !is_compact_dex_;
}
+ ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const;
+ ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const;
protected:
DexFile(const uint8_t* base,
@@ -1007,7 +1011,8 @@
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
- DexFileContainer* container);
+ DexFileContainer* container,
+ bool is_compact_dex);
// Top-level initializer that calls other Init methods.
bool Init(std::string* error_msg);
@@ -1073,6 +1078,9 @@
// Manages the underlying memory allocation.
std::unique_ptr<DexFileContainer> container_;
+ // If the dex file is a compact dex file. If false then the dex file is a standard dex file.
+ const bool is_compact_dex_;
+
friend class DexFileLoader;
friend class DexFileVerifierTest;
friend class OatWriter;
diff --git a/runtime/gc/allocator/dlmalloc.h b/runtime/gc/allocator/dlmalloc.h
index c07da5d..29b96ee 100644
--- a/runtime/gc/allocator/dlmalloc.h
+++ b/runtime/gc/allocator/dlmalloc.h
@@ -35,13 +35,6 @@
#include "../../external/dlmalloc/malloc.h"
#pragma GCC diagnostic pop
-#ifdef ART_TARGET_ANDROID
-// Define dlmalloc routines from bionic that cannot be included directly because of redefining
-// symbols from the include above.
-extern "C" void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), void* arg);
-extern "C" int dlmalloc_trim(size_t);
-#endif
-
// Callback for dlmalloc_inspect_all or mspace_inspect_all that will madvise(2) unused
// pages back to the kernel.
extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* /*arg*/);
diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc
index 1344ca0..e54a017 100644
--- a/runtime/jit/profiling_info.cc
+++ b/runtime/jit/profiling_info.cc
@@ -44,8 +44,7 @@
DCHECK(!method->IsNative());
std::vector<uint32_t> entries;
-
- for (const DexInstructionPcPair& inst : method->GetCodeItem()->Instructions()) {
+ for (const DexInstructionPcPair& inst : method->DexInstructions()) {
switch (inst->Opcode()) {
case Instruction::INVOKE_VIRTUAL:
case Instruction::INVOKE_VIRTUAL_RANGE:
diff --git a/runtime/standard_dex_file.cc b/runtime/standard_dex_file.cc
index 36bb37a..4c1d308 100644
--- a/runtime/standard_dex_file.cc
+++ b/runtime/standard_dex_file.cc
@@ -31,6 +31,16 @@
{'0', '3', '9', '\0'},
};
+void StandardDexFile::WriteMagic(uint8_t* magic) {
+ std::copy_n(kDexMagic, kDexMagicSize, magic);
+}
+
+void StandardDexFile::WriteCurrentVersion(uint8_t* magic) {
+ std::copy_n(kDexMagicVersions[StandardDexFile::kDexVersionLen - 1],
+ kDexVersionLen,
+ magic + kDexMagicSize);
+}
+
bool StandardDexFile::IsMagicValid(const uint8_t* magic) {
return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0);
}
diff --git a/runtime/standard_dex_file.h b/runtime/standard_dex_file.h
index 784ab31..5d53597 100644
--- a/runtime/standard_dex_file.h
+++ b/runtime/standard_dex_file.h
@@ -32,6 +32,18 @@
// Same for now.
};
+ struct CodeItem : public DexFile::CodeItem {
+ private:
+ // TODO: Insert standard dex specific fields here.
+ DISALLOW_COPY_AND_ASSIGN(CodeItem);
+ };
+
+ // Write the standard dex specific magic.
+ static void WriteMagic(uint8_t* magic);
+
+ // Write the current version, note that the input is the address of the magic.
+ static void WriteCurrentVersion(uint8_t* magic);
+
static const uint8_t kDexMagic[kDexMagicSize];
static constexpr size_t kNumDexVersions = 4;
static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen];
@@ -44,10 +56,6 @@
static bool IsVersionValid(const uint8_t* magic);
virtual bool IsVersionValid() const OVERRIDE;
- bool IsStandardDexFile() const OVERRIDE {
- return true;
- }
-
private:
StandardDexFile(const uint8_t* base,
size_t size,
@@ -55,7 +63,13 @@
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
DexFileContainer* container)
- : DexFile(base, size, location, location_checksum, oat_dex_file, container) {}
+ : DexFile(base,
+ size,
+ location,
+ location_checksum,
+ oat_dex_file,
+ container,
+ /*is_compact_dex*/ false) {}
friend class DexFileLoader;
friend class DexFileVerifierTest;