Merge "Refactor the alloc entry point generation code."
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index a443487..b507124 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -295,18 +295,22 @@
art_cflags += -DIMT_SIZE=64
endif
-ifeq ($(ART_USE_OPTIMIZING_COMPILER),true)
- art_cflags += -DART_USE_OPTIMIZING_COMPILER=1
-endif
-
ifeq ($(ART_HEAP_POISONING),true)
art_cflags += -DART_HEAP_POISONING=1
art_asflags += -DART_HEAP_POISONING=1
endif
+#
+# Used to change the read barrier type. Valid values are BAKER, BROOKS, TABLELOOKUP.
+# The default is BAKER.
+#
+ART_READ_BARRIER_TYPE ?= BAKER
+
ifeq ($(ART_USE_READ_BARRIER),true)
art_cflags += -DART_USE_READ_BARRIER=1
+ art_cflags += -DART_READ_BARRIER_TYPE_IS_$(ART_READ_BARRIER_TYPE)=1
art_asflags += -DART_USE_READ_BARRIER=1
+ art_asflags += -DART_READ_BARRIER_TYPE_IS_$(ART_READ_BARRIER_TYPE)=1
endif
ifeq ($(ART_USE_TLAB),true)
@@ -396,7 +400,6 @@
art_non_debug_cflags :=
art_host_non_debug_cflags :=
art_target_non_debug_cflags :=
-art_default_gc_type :=
art_default_gc_type_cflags :=
ART_HOST_LDLIBS :=
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 71a55bb..9775f6a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -351,6 +351,7 @@
COMPILER_GTEST_HOST_SRC_FILES_mips64 := \
$(COMPILER_GTEST_COMMON_SRC_FILES_mips64) \
+ compiler/utils/mips64/assembler_mips64_test.cc \
COMPILER_GTEST_HOST_SRC_FILES_x86 := \
$(COMPILER_GTEST_COMMON_SRC_FILES_x86) \
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 6e17ed3..592843e 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -52,12 +52,6 @@
core_pic_infix :=
core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY)
- # With the optimizing compiler, we want to rerun dex2oat whenever there is
- # a dex2oat change to catch regressions early.
- ifeq ($(ART_USE_OPTIMIZING_COMPILER), true)
- core_dex2oat_dependency := $(DEX2OAT)
- endif
-
ifeq ($(1),default)
core_compile_options += --compiler-backend=Quick
endif
@@ -178,17 +172,13 @@
core_pic_infix :=
core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY)
- # With the optimizing compiler, we want to rerun dex2oat whenever there is
- # a dex2oat change to catch regressions early.
- ifeq ($(ART_USE_OPTIMIZING_COMPILER), true)
- core_dex2oat_dependency := $(DEX2OAT)
- endif
-
ifeq ($(1),default)
core_compile_options += --compiler-backend=Quick
endif
ifeq ($(1),optimizing)
core_compile_options += --compiler-backend=Optimizing
+ # With the optimizing compiler, we want to rerun dex2oat whenever there is
+ # a dex2oat change to catch regressions early.
core_dex2oat_dependency := $(DEX2OAT)
core_infix := -optimizing
endif
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index dc2bc5c..67b4428 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -92,7 +92,7 @@
void UnreserveImageSpace();
- Compiler::Kind compiler_kind_ = kUseOptimizingCompiler ? Compiler::kOptimizing : Compiler::kQuick;
+ Compiler::Kind compiler_kind_ = Compiler::kOptimizing;
std::unique_ptr<CompilerOptions> compiler_options_;
std::unique_ptr<VerificationResults> verification_results_;
std::unique_ptr<DexFileToMethodInlinerMap> method_inliner_map_;
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 1f114cf..3c5c2fe 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -148,7 +148,7 @@
if (arg1.wide == 0) {
LoadValueDirectFixed(arg1, TargetReg(kArg1, arg1));
} else {
- RegStorage r_tmp = TargetReg(cu_->instruction_set == kMips ? kArg2 : kArg1, kWide);
+ RegStorage r_tmp = TargetReg(kArg2, kWide);
LoadValueDirectWideFixed(arg1, r_tmp);
}
LoadConstant(TargetReg(kArg0, kNotWide), arg0);
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index 939bf40..6ed666b 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -535,37 +535,76 @@
if (block->visited || block->hidden) {
return;
}
- block->visited = true;
- /* Process this block */
- DoSSAConversion(block);
+ typedef struct {
+ BasicBlock* bb;
+ int32_t* ssa_map;
+ } BasicBlockInfo;
+ BasicBlockInfo temp;
- /* Save SSA map snapshot */
ScopedArenaAllocator allocator(&cu_->arena_stack);
- uint32_t num_vregs = GetNumOfCodeAndTempVRs();
- int32_t* saved_ssa_map = allocator.AllocArray<int32_t>(num_vregs, kArenaAllocDalvikToSSAMap);
- size_t map_size = sizeof(saved_ssa_map[0]) * num_vregs;
- memcpy(saved_ssa_map, vreg_to_ssa_map_, map_size);
+ ScopedArenaVector<BasicBlockInfo> bi_stack(allocator.Adapter());
+ ScopedArenaVector<BasicBlock*> succ_stack(allocator.Adapter());
- if (block->fall_through != NullBasicBlockId) {
- DoDFSPreOrderSSARename(GetBasicBlock(block->fall_through));
- /* Restore SSA map snapshot */
- memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
- }
- if (block->taken != NullBasicBlockId) {
- DoDFSPreOrderSSARename(GetBasicBlock(block->taken));
- /* Restore SSA map snapshot */
- memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
- }
- if (block->successor_block_list_type != kNotUsed) {
- for (SuccessorBlockInfo* successor_block_info : block->successor_blocks) {
- BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
- DoDFSPreOrderSSARename(succ_bb);
- /* Restore SSA map snapshot */
- memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
+ uint32_t num_vregs = GetNumOfCodeAndTempVRs();
+ size_t map_size = sizeof(int32_t) * num_vregs;
+ temp.bb = block;
+ temp.ssa_map = vreg_to_ssa_map_;
+ bi_stack.push_back(temp);
+
+ while (!bi_stack.empty()) {
+ temp = bi_stack.back();
+ bi_stack.pop_back();
+ BasicBlock* b = temp.bb;
+
+ if (b->visited || b->hidden) {
+ continue;
+ }
+ b->visited = true;
+
+ /* Restore SSA map snapshot, except for the first block */
+ if (b != block) {
+ memcpy(vreg_to_ssa_map_, temp.ssa_map, map_size);
+ }
+
+ /* Process this block */
+ DoSSAConversion(b);
+
+ /* If there are no successor, taken, and fall through blocks, continue */
+ if (b->successor_block_list_type == kNotUsed &&
+ b->taken == NullBasicBlockId &&
+ b->fall_through == NullBasicBlockId) {
+ continue;
+ }
+
+ /* Save SSA map snapshot */
+ int32_t* saved_ssa_map =
+ allocator.AllocArray<int32_t>(num_vregs, kArenaAllocDalvikToSSAMap);
+ memcpy(saved_ssa_map, vreg_to_ssa_map_, map_size);
+
+ if (b->successor_block_list_type != kNotUsed) {
+ for (SuccessorBlockInfo* successor_block_info : b->successor_blocks) {
+ BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
+ succ_stack.push_back(succ_bb);
+ }
+ while (!succ_stack.empty()) {
+ temp.bb = succ_stack.back();
+ succ_stack.pop_back();
+ temp.ssa_map = saved_ssa_map;
+ bi_stack.push_back(temp);
+ }
+ }
+ if (b->taken != NullBasicBlockId) {
+ temp.bb = GetBasicBlock(b->taken);
+ temp.ssa_map = saved_ssa_map;
+ bi_stack.push_back(temp);
+ }
+ if (b->fall_through != NullBasicBlockId) {
+ temp.bb = GetBasicBlock(b->fall_through);
+ temp.ssa_map = saved_ssa_map;
+ bi_stack.push_back(temp);
}
}
- return;
}
} // namespace art
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 960f4d9..2c7c127 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -965,7 +965,8 @@
suspend_check->GetEnvironment(), header);
}
- HArrayLength* new_array_length = new (graph->GetArena()) HArrayLength(array);
+ HArrayLength* new_array_length
+ = new (graph->GetArena()) HArrayLength(array, array->GetDexPc());
deopt_block->InsertInstructionBefore(new_array_length, deopt_block->GetLastInstruction());
if (loop_entry_test_block_added) {
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
index 08e1e36..ce6dc75 100644
--- a/compiler/optimizing/bounds_check_elimination_test.cc
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -91,7 +91,7 @@
HBasicBlock* block2 = new (&allocator_) HBasicBlock(graph_);
graph_->AddBlock(block2);
HNullCheck* null_check = new (&allocator_) HNullCheck(parameter1, 0);
- HArrayLength* array_length = new (&allocator_) HArrayLength(null_check);
+ HArrayLength* array_length = new (&allocator_) HArrayLength(null_check, 0);
HBoundsCheck* bounds_check2 = new (&allocator_)
HBoundsCheck(parameter2, array_length, 0);
HArraySet* array_set = new (&allocator_) HArraySet(
@@ -104,7 +104,7 @@
HBasicBlock* block3 = new (&allocator_) HBasicBlock(graph_);
graph_->AddBlock(block3);
null_check = new (&allocator_) HNullCheck(parameter1, 0);
- array_length = new (&allocator_) HArrayLength(null_check);
+ array_length = new (&allocator_) HArrayLength(null_check, 0);
cmp = new (&allocator_) HLessThan(parameter2, array_length);
if_inst = new (&allocator_) HIf(cmp);
block3->AddInstruction(null_check);
@@ -115,7 +115,7 @@
HBasicBlock* block4 = new (&allocator_) HBasicBlock(graph_);
graph_->AddBlock(block4);
null_check = new (&allocator_) HNullCheck(parameter1, 0);
- array_length = new (&allocator_) HArrayLength(null_check);
+ array_length = new (&allocator_) HArrayLength(null_check, 0);
HBoundsCheck* bounds_check4 = new (&allocator_)
HBoundsCheck(parameter2, array_length, 0);
array_set = new (&allocator_) HArraySet(
@@ -128,7 +128,7 @@
HBasicBlock* block5 = new (&allocator_) HBasicBlock(graph_);
graph_->AddBlock(block5);
null_check = new (&allocator_) HNullCheck(parameter1, 0);
- array_length = new (&allocator_) HArrayLength(null_check);
+ array_length = new (&allocator_) HArrayLength(null_check, 0);
HBoundsCheck* bounds_check5 = new (&allocator_)
HBoundsCheck(parameter2, array_length, 0);
array_set = new (&allocator_) HArraySet(
@@ -190,7 +190,7 @@
graph_->AddBlock(block2);
HInstruction* add = new (&allocator_) HAdd(Primitive::kPrimInt, parameter2, constant_max_int);
HNullCheck* null_check = new (&allocator_) HNullCheck(parameter1, 0);
- HArrayLength* array_length = new (&allocator_) HArrayLength(null_check);
+ HArrayLength* array_length = new (&allocator_) HArrayLength(null_check, 0);
HInstruction* cmp2 = new (&allocator_) HGreaterThanOrEqual(add, array_length);
if_inst = new (&allocator_) HIf(cmp2);
block2->AddInstruction(add);
@@ -245,7 +245,7 @@
HBasicBlock* block1 = new (&allocator_) HBasicBlock(graph_);
graph_->AddBlock(block1);
HNullCheck* null_check = new (&allocator_) HNullCheck(parameter1, 0);
- HArrayLength* array_length = new (&allocator_) HArrayLength(null_check);
+ HArrayLength* array_length = new (&allocator_) HArrayLength(null_check, 0);
HInstruction* cmp = new (&allocator_) HGreaterThanOrEqual(parameter2, array_length);
HIf* if_inst = new (&allocator_) HIf(cmp);
block1->AddInstruction(null_check);
@@ -308,7 +308,7 @@
entry->AddSuccessor(block);
HNullCheck* null_check = new (&allocator_) HNullCheck(parameter, 0);
- HArrayLength* array_length = new (&allocator_) HArrayLength(null_check);
+ HArrayLength* array_length = new (&allocator_) HArrayLength(null_check, 0);
HBoundsCheck* bounds_check6 = new (&allocator_)
HBoundsCheck(constant_6, array_length, 0);
HInstruction* array_set = new (&allocator_) HArraySet(
@@ -319,7 +319,7 @@
block->AddInstruction(array_set);
null_check = new (&allocator_) HNullCheck(parameter, 0);
- array_length = new (&allocator_) HArrayLength(null_check);
+ array_length = new (&allocator_) HArrayLength(null_check, 0);
HBoundsCheck* bounds_check5 = new (&allocator_)
HBoundsCheck(constant_5, array_length, 0);
array_set = new (&allocator_) HArraySet(
@@ -330,7 +330,7 @@
block->AddInstruction(array_set);
null_check = new (&allocator_) HNullCheck(parameter, 0);
- array_length = new (&allocator_) HArrayLength(null_check);
+ array_length = new (&allocator_) HArrayLength(null_check, 0);
HBoundsCheck* bounds_check4 = new (&allocator_)
HBoundsCheck(constant_4, array_length, 0);
array_set = new (&allocator_) HArraySet(
@@ -389,7 +389,7 @@
HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
HInstruction* null_check = new (allocator) HNullCheck(parameter, 0);
- HInstruction* array_length = new (allocator) HArrayLength(null_check);
+ HInstruction* array_length = new (allocator) HArrayLength(null_check, 0);
HInstruction* cmp = nullptr;
if (cond == kCondGE) {
cmp = new (allocator) HGreaterThanOrEqual(phi, array_length);
@@ -406,7 +406,7 @@
phi->AddInput(constant_initial);
null_check = new (allocator) HNullCheck(parameter, 0);
- array_length = new (allocator) HArrayLength(null_check);
+ array_length = new (allocator) HArrayLength(null_check, 0);
HInstruction* bounds_check = new (allocator) HBoundsCheck(phi, array_length, 0);
HInstruction* array_set = new (allocator) HArraySet(
null_check, bounds_check, constant_10, Primitive::kPrimInt, 0);
@@ -489,7 +489,7 @@
graph->AddBlock(block);
entry->AddSuccessor(block);
HInstruction* null_check = new (allocator) HNullCheck(parameter, 0);
- HInstruction* array_length = new (allocator) HArrayLength(null_check);
+ HInstruction* array_length = new (allocator) HArrayLength(null_check, 0);
block->AddInstruction(null_check);
block->AddInstruction(array_length);
block->AddInstruction(new (allocator) HGoto());
@@ -522,7 +522,7 @@
HInstruction* add = new (allocator) HAdd(Primitive::kPrimInt, phi, constant_minus_1);
null_check = new (allocator) HNullCheck(parameter, 0);
- array_length = new (allocator) HArrayLength(null_check);
+ array_length = new (allocator) HArrayLength(null_check, 0);
HInstruction* bounds_check = new (allocator) HBoundsCheck(add, array_length, 0);
HInstruction* array_set = new (allocator) HArraySet(
null_check, bounds_check, constant_10, Primitive::kPrimInt, 0);
@@ -631,7 +631,7 @@
phi->AddInput(constant_initial);
HNullCheck* null_check = new (allocator) HNullCheck(new_array, 0);
- HArrayLength* array_length = new (allocator) HArrayLength(null_check);
+ HArrayLength* array_length = new (allocator) HArrayLength(null_check, 0);
HInstruction* bounds_check = new (allocator) HBoundsCheck(phi, array_length, 0);
HInstruction* array_set = new (allocator) HArraySet(
null_check, bounds_check, constant_10, Primitive::kPrimInt, 0);
@@ -716,7 +716,7 @@
HPhi* phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
HInstruction* null_check = new (allocator) HNullCheck(parameter, 0);
- HInstruction* array_length = new (allocator) HArrayLength(null_check);
+ HInstruction* array_length = new (allocator) HArrayLength(null_check, 0);
HInstruction* cmp = nullptr;
if (cond == kCondGE) {
cmp = new (allocator) HGreaterThanOrEqual(phi, array_length);
@@ -732,7 +732,7 @@
phi->AddInput(constant_initial);
null_check = new (allocator) HNullCheck(parameter, 0);
- array_length = new (allocator) HArrayLength(null_check);
+ array_length = new (allocator) HArrayLength(null_check, 0);
HInstruction* sub = new (allocator) HSub(Primitive::kPrimInt, array_length, phi);
HInstruction* add_minus_1 = new (allocator)
HAdd(Primitive::kPrimInt, sub, constant_minus_1);
@@ -811,7 +811,7 @@
graph_->AddBlock(outer_header);
HPhi* phi_i = new (&allocator_) HPhi(&allocator_, 0, 0, Primitive::kPrimInt);
HNullCheck* null_check = new (&allocator_) HNullCheck(parameter, 0);
- HArrayLength* array_length = new (&allocator_) HArrayLength(null_check);
+ HArrayLength* array_length = new (&allocator_) HArrayLength(null_check, 0);
HAdd* add = new (&allocator_) HAdd(Primitive::kPrimInt, array_length, constant_minus_1);
HInstruction* cmp = new (&allocator_) HGreaterThanOrEqual(phi_i, add);
HIf* if_inst = new (&allocator_) HIf(cmp);
@@ -827,7 +827,7 @@
graph_->AddBlock(inner_header);
HPhi* phi_j = new (&allocator_) HPhi(&allocator_, 0, 0, Primitive::kPrimInt);
null_check = new (&allocator_) HNullCheck(parameter, 0);
- array_length = new (&allocator_) HArrayLength(null_check);
+ array_length = new (&allocator_) HArrayLength(null_check, 0);
HSub* sub = new (&allocator_) HSub(Primitive::kPrimInt, array_length, phi_i);
add = new (&allocator_) HAdd(Primitive::kPrimInt, sub, constant_minus_1);
cmp = new (&allocator_) HGreaterThanOrEqual(phi_j, add);
@@ -844,20 +844,20 @@
HBasicBlock* inner_body_compare = new (&allocator_) HBasicBlock(graph_);
graph_->AddBlock(inner_body_compare);
null_check = new (&allocator_) HNullCheck(parameter, 0);
- array_length = new (&allocator_) HArrayLength(null_check);
+ array_length = new (&allocator_) HArrayLength(null_check, 0);
HBoundsCheck* bounds_check1 = new (&allocator_) HBoundsCheck(phi_j, array_length, 0);
HArrayGet* array_get_j = new (&allocator_)
- HArrayGet(null_check, bounds_check1, Primitive::kPrimInt);
+ HArrayGet(null_check, bounds_check1, Primitive::kPrimInt, 0);
inner_body_compare->AddInstruction(null_check);
inner_body_compare->AddInstruction(array_length);
inner_body_compare->AddInstruction(bounds_check1);
inner_body_compare->AddInstruction(array_get_j);
HInstruction* j_plus_1 = new (&allocator_) HAdd(Primitive::kPrimInt, phi_j, constant_1);
null_check = new (&allocator_) HNullCheck(parameter, 0);
- array_length = new (&allocator_) HArrayLength(null_check);
+ array_length = new (&allocator_) HArrayLength(null_check, 0);
HBoundsCheck* bounds_check2 = new (&allocator_) HBoundsCheck(j_plus_1, array_length, 0);
HArrayGet* array_get_j_plus_1 = new (&allocator_)
- HArrayGet(null_check, bounds_check2, Primitive::kPrimInt);
+ HArrayGet(null_check, bounds_check2, Primitive::kPrimInt, 0);
cmp = new (&allocator_) HGreaterThanOrEqual(array_get_j, array_get_j_plus_1);
if_inst = new (&allocator_) HIf(cmp);
inner_body_compare->AddInstruction(j_plus_1);
@@ -873,10 +873,10 @@
j_plus_1 = new (&allocator_) HAdd(Primitive::kPrimInt, phi_j, constant_1);
// temp = array[j+1]
null_check = new (&allocator_) HNullCheck(parameter, 0);
- array_length = new (&allocator_) HArrayLength(null_check);
+ array_length = new (&allocator_) HArrayLength(null_check, 0);
HInstruction* bounds_check3 = new (&allocator_) HBoundsCheck(j_plus_1, array_length, 0);
array_get_j_plus_1 = new (&allocator_)
- HArrayGet(null_check, bounds_check3, Primitive::kPrimInt);
+ HArrayGet(null_check, bounds_check3, Primitive::kPrimInt, 0);
inner_body_swap->AddInstruction(j_plus_1);
inner_body_swap->AddInstruction(null_check);
inner_body_swap->AddInstruction(array_length);
@@ -884,16 +884,16 @@
inner_body_swap->AddInstruction(array_get_j_plus_1);
// array[j+1] = array[j]
null_check = new (&allocator_) HNullCheck(parameter, 0);
- array_length = new (&allocator_) HArrayLength(null_check);
+ array_length = new (&allocator_) HArrayLength(null_check, 0);
HInstruction* bounds_check4 = new (&allocator_) HBoundsCheck(phi_j, array_length, 0);
array_get_j = new (&allocator_)
- HArrayGet(null_check, bounds_check4, Primitive::kPrimInt);
+ HArrayGet(null_check, bounds_check4, Primitive::kPrimInt, 0);
inner_body_swap->AddInstruction(null_check);
inner_body_swap->AddInstruction(array_length);
inner_body_swap->AddInstruction(bounds_check4);
inner_body_swap->AddInstruction(array_get_j);
null_check = new (&allocator_) HNullCheck(parameter, 0);
- array_length = new (&allocator_) HArrayLength(null_check);
+ array_length = new (&allocator_) HArrayLength(null_check, 0);
HInstruction* bounds_check5 = new (&allocator_) HBoundsCheck(j_plus_1, array_length, 0);
HArraySet* array_set_j_plus_1 = new (&allocator_)
HArraySet(null_check, bounds_check5, array_get_j, Primitive::kPrimInt, 0);
@@ -903,7 +903,7 @@
inner_body_swap->AddInstruction(array_set_j_plus_1);
// array[j] = temp
null_check = new (&allocator_) HNullCheck(parameter, 0);
- array_length = new (&allocator_) HArrayLength(null_check);
+ array_length = new (&allocator_) HArrayLength(null_check, 0);
HInstruction* bounds_check6 = new (&allocator_) HBoundsCheck(phi_j, array_length, 0);
HArraySet* array_set_j = new (&allocator_)
HArraySet(null_check, bounds_check6, array_get_j_plus_1, Primitive::kPrimInt, 0);
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index cb36f62..5acc5fd 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -940,7 +940,8 @@
storage_index,
*dex_compilation_unit_->GetDexFile(),
is_outer_class,
- dex_pc);
+ dex_pc,
+ /*needs_access_check*/ false);
current_block_->AddInstruction(load_class);
clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
current_block_->AddInstruction(clinit_check);
@@ -1186,6 +1187,12 @@
}
}
+static Primitive::Type GetFieldAccessType(const DexFile& dex_file, uint16_t field_index) {
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
+ const char* type = dex_file.GetFieldTypeDescriptor(field_id);
+ return Primitive::GetType(type[0]);
+}
+
bool HGraphBuilder::BuildInstanceFieldAccess(const Instruction& instruction,
uint32_t dex_pc,
bool is_put) {
@@ -1205,44 +1212,61 @@
ArtField* resolved_field =
compiler_driver_->ComputeInstanceFieldInfo(field_index, dex_compilation_unit_, is_put, soa);
- if (resolved_field == nullptr) {
- MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedField);
- return false;
- }
-
- Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
HInstruction* object = LoadLocal(obj_reg, Primitive::kPrimNot, dex_pc);
- current_block_->AddInstruction(new (arena_) HNullCheck(object, dex_pc));
+ HInstruction* null_check = new (arena_) HNullCheck(object, dex_pc);
+ current_block_->AddInstruction(null_check);
+
+ Primitive::Type field_type = (resolved_field == nullptr)
+ ? GetFieldAccessType(*dex_file_, field_index)
+ : resolved_field->GetTypeAsPrimitiveType();
if (is_put) {
Temporaries temps(graph_);
- HInstruction* null_check = current_block_->GetLastInstruction();
// We need one temporary for the null check.
temps.Add(null_check);
HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
- current_block_->AddInstruction(new (arena_) HInstanceFieldSet(
- null_check,
- value,
- field_type,
- resolved_field->GetOffset(),
- resolved_field->IsVolatile(),
- field_index,
- *dex_file_,
- dex_compilation_unit_->GetDexCache(),
- dex_pc));
+ HInstruction* field_set = nullptr;
+ if (resolved_field == nullptr) {
+ MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
+ field_set = new (arena_) HUnresolvedInstanceFieldSet(null_check,
+ value,
+ field_type,
+ field_index,
+ dex_pc);
+ } else {
+ field_set = new (arena_) HInstanceFieldSet(null_check,
+ value,
+ field_type,
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ *dex_file_,
+ dex_compilation_unit_->GetDexCache(),
+ dex_pc);
+ }
+ current_block_->AddInstruction(field_set);
} else {
- current_block_->AddInstruction(new (arena_) HInstanceFieldGet(
- current_block_->GetLastInstruction(),
- field_type,
- resolved_field->GetOffset(),
- resolved_field->IsVolatile(),
- field_index,
- *dex_file_,
- dex_compilation_unit_->GetDexCache(),
- dex_pc));
-
- UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
+ HInstruction* field_get = nullptr;
+ if (resolved_field == nullptr) {
+ MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
+ field_get = new (arena_) HUnresolvedInstanceFieldGet(null_check,
+ field_type,
+ field_index,
+ dex_pc);
+ } else {
+ field_get = new (arena_) HInstanceFieldGet(null_check,
+ field_type,
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ *dex_file_,
+ dex_compilation_unit_->GetDexCache(),
+ dex_pc);
+ }
+ current_block_->AddInstruction(field_get);
+ UpdateLocal(source_or_dest_reg, field_get, dex_pc);
}
+
return true;
}
@@ -1282,6 +1306,23 @@
return outer_class.Get() == cls.Get();
}
+void HGraphBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
+ uint32_t dex_pc,
+ bool is_put,
+ Primitive::Type field_type) {
+ uint32_t source_or_dest_reg = instruction.VRegA_21c();
+ uint16_t field_index = instruction.VRegB_21c();
+
+ if (is_put) {
+ HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
+ current_block_->AddInstruction(
+ new (arena_) HUnresolvedStaticFieldSet(value, field_type, field_index, dex_pc));
+ } else {
+ current_block_->AddInstruction(
+ new (arena_) HUnresolvedStaticFieldGet(field_type, field_index, dex_pc));
+ UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
+ }
+}
bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction,
uint32_t dex_pc,
bool is_put) {
@@ -1299,10 +1340,13 @@
soa, dex_cache, class_loader, dex_compilation_unit_, field_index, true);
if (resolved_field == nullptr) {
- MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedField);
- return false;
+ MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
+ Primitive::Type field_type = GetFieldAccessType(*dex_file_, field_index);
+ BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type);
+ return true;
}
+ Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file)));
@@ -1317,6 +1361,7 @@
// The compiler driver cannot currently understand multiple dex caches involved. Just bailout.
return false;
} else {
+ // TODO: This is rather expensive. Perf it and cache the results if needed.
std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField(
outer_dex_cache.Get(),
GetCompilingClass(),
@@ -1325,7 +1370,9 @@
&storage_index);
bool can_easily_access = is_put ? pair.second : pair.first;
if (!can_easily_access) {
- return false;
+ MaybeRecordStat(MethodCompilationStat::kUnresolvedFieldNotAFastAccess);
+ BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type);
+ return true;
}
}
@@ -1338,7 +1385,8 @@
storage_index,
*dex_compilation_unit_->GetDexFile(),
is_outer_class,
- dex_pc);
+ dex_pc,
+ /*needs_access_check*/ false);
current_block_->AddInstruction(constant);
HInstruction* cls = constant;
@@ -1346,8 +1394,6 @@
cls = new (arena_) HClinitCheck(constant, dex_pc);
current_block_->AddInstruction(cls);
}
-
- Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
if (is_put) {
// We need to keep the class alive before loading the value.
Temporaries temps(graph_);
@@ -1571,7 +1617,9 @@
static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls)
SHARED_REQUIRES(Locks::mutator_lock_) {
- if (cls->IsInterface()) {
+ if (cls.Get() == nullptr) {
+ return TypeCheckKind::kUnresolvedCheck;
+ } else if (cls->IsInterface()) {
return TypeCheckKind::kInterfaceCheck;
} else if (cls->IsArrayClass()) {
if (cls->GetComponentType()->IsObjectClass()) {
@@ -1590,11 +1638,20 @@
}
}
-bool HGraphBuilder::BuildTypeCheck(const Instruction& instruction,
+void HGraphBuilder::BuildTypeCheck(const Instruction& instruction,
uint8_t destination,
uint8_t reference,
uint16_t type_index,
uint32_t dex_pc) {
+ bool type_known_final, type_known_abstract, use_declaring_class;
+ bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
+ dex_compilation_unit_->GetDexMethodIndex(),
+ *dex_compilation_unit_->GetDexFile(),
+ type_index,
+ &type_known_final,
+ &type_known_abstract,
+ &use_declaring_class);
+
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<2> hs(soa.Self());
Handle<mirror::DexCache> dex_cache(hs.NewHandle(
@@ -1602,22 +1659,14 @@
soa.Self(), *dex_compilation_unit_->GetDexFile())));
Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
- if ((resolved_class.Get() == nullptr) ||
- // TODO: Remove this check once the compiler actually knows which
- // ArtMethod it is compiling.
- (GetCompilingClass() == nullptr) ||
- !GetCompilingClass()->CanAccess(resolved_class.Get())) {
- MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType);
- return false;
- }
-
HInstruction* object = LoadLocal(reference, Primitive::kPrimNot, dex_pc);
HLoadClass* cls = new (arena_) HLoadClass(
graph_->GetCurrentMethod(),
type_index,
*dex_compilation_unit_->GetDexFile(),
IsOutermostCompilingClass(type_index),
- dex_pc);
+ dex_pc,
+ !can_access);
current_block_->AddInstruction(cls);
// The class needs a temporary before being used by the type check.
@@ -1632,7 +1681,6 @@
DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST);
current_block_->AddInstruction(new (arena_) HCheckCast(object, cls, check_kind, dex_pc));
}
- return true;
}
bool HGraphBuilder::NeedsAccessCheck(uint32_t type_index) const {
@@ -2747,16 +2795,13 @@
bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
&type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
- if (!can_access) {
- MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType);
- return false;
- }
current_block_->AddInstruction(new (arena_) HLoadClass(
graph_->GetCurrentMethod(),
type_index,
*dex_compilation_unit_->GetDexFile(),
IsOutermostCompilingClass(type_index),
- dex_pc));
+ dex_pc,
+ !can_access));
UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction(), dex_pc);
break;
}
@@ -2783,18 +2828,14 @@
uint8_t destination = instruction.VRegA_22c();
uint8_t reference = instruction.VRegB_22c();
uint16_t type_index = instruction.VRegC_22c();
- if (!BuildTypeCheck(instruction, destination, reference, type_index, dex_pc)) {
- return false;
- }
+ BuildTypeCheck(instruction, destination, reference, type_index, dex_pc);
break;
}
case Instruction::CHECK_CAST: {
uint8_t reference = instruction.VRegA_21c();
uint16_t type_index = instruction.VRegB_21c();
- if (!BuildTypeCheck(instruction, -1, reference, type_index, dex_pc)) {
- return false;
- }
+ BuildTypeCheck(instruction, -1, reference, type_index, dex_pc);
break;
}
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 4c8e3d0..6910d51 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -187,6 +187,10 @@
// Builds an instance field access node and returns whether the instruction is supported.
bool BuildInstanceFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);
+ void BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
+ uint32_t dex_pc,
+ bool is_put,
+ Primitive::Type field_type);
// Builds a static field access node and returns whether the instruction is supported.
bool BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);
@@ -232,8 +236,7 @@
uint32_t dex_pc);
// Builds a `HInstanceOf`, or a `HCheckCast` instruction.
- // Returns whether we succeeded in building the instruction.
- bool BuildTypeCheck(const Instruction& instruction,
+ void BuildTypeCheck(const Instruction& instruction,
uint8_t destination,
uint8_t reference,
uint16_t type_index,
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index be05691..1da2a07 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -413,6 +413,151 @@
InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr);
}
+void CodeGenerator::CreateUnresolvedFieldLocationSummary(
+ HInstruction* field_access,
+ Primitive::Type field_type,
+ const FieldAccessCallingConvention& calling_convention) {
+ bool is_instance = field_access->IsUnresolvedInstanceFieldGet()
+ || field_access->IsUnresolvedInstanceFieldSet();
+ bool is_get = field_access->IsUnresolvedInstanceFieldGet()
+ || field_access->IsUnresolvedStaticFieldGet();
+
+ ArenaAllocator* allocator = field_access->GetBlock()->GetGraph()->GetArena();
+ LocationSummary* locations =
+ new (allocator) LocationSummary(field_access, LocationSummary::kCall);
+
+ locations->AddTemp(calling_convention.GetFieldIndexLocation());
+
+ if (is_instance) {
+ // Add the `this` object for instance field accesses.
+ locations->SetInAt(0, calling_convention.GetObjectLocation());
+ }
+
+ // Note that pSetXXStatic/pGetXXStatic always takes/returns an int or int64
+ // regardless of the the type. Because of that we forced to special case
+ // the access to floating point values.
+ if (is_get) {
+ if (Primitive::IsFloatingPointType(field_type)) {
+ // The return value will be stored in regular registers while register
+ // allocator expects it in a floating point register.
+ // Note We don't need to request additional temps because the return
+ // register(s) are already blocked due the call and they may overlap with
+ // the input or field index.
+ // The transfer between the two will be done at codegen level.
+ locations->SetOut(calling_convention.GetFpuLocation(field_type));
+ } else {
+ locations->SetOut(calling_convention.GetReturnLocation(field_type));
+ }
+ } else {
+ size_t set_index = is_instance ? 1 : 0;
+ if (Primitive::IsFloatingPointType(field_type)) {
+ // The set value comes from a float location while the calling convention
+ // expects it in a regular register location. Allocate a temp for it and
+ // make the transfer at codegen.
+ AddLocationAsTemp(calling_convention.GetSetValueLocation(field_type, is_instance), locations);
+ locations->SetInAt(set_index, calling_convention.GetFpuLocation(field_type));
+ } else {
+ locations->SetInAt(set_index,
+ calling_convention.GetSetValueLocation(field_type, is_instance));
+ }
+ }
+}
+
+void CodeGenerator::GenerateUnresolvedFieldAccess(
+ HInstruction* field_access,
+ Primitive::Type field_type,
+ uint32_t field_index,
+ uint32_t dex_pc,
+ const FieldAccessCallingConvention& calling_convention) {
+ LocationSummary* locations = field_access->GetLocations();
+
+ MoveConstant(locations->GetTemp(0), field_index);
+
+ bool is_instance = field_access->IsUnresolvedInstanceFieldGet()
+ || field_access->IsUnresolvedInstanceFieldSet();
+ bool is_get = field_access->IsUnresolvedInstanceFieldGet()
+ || field_access->IsUnresolvedStaticFieldGet();
+
+ if (!is_get && Primitive::IsFloatingPointType(field_type)) {
+ // Copy the float value to be set into the calling convention register.
+ // Note that using directly the temp location is problematic as we don't
+ // support temp register pairs. To avoid boilerplate conversion code, use
+ // the location from the calling convention.
+ MoveLocation(calling_convention.GetSetValueLocation(field_type, is_instance),
+ locations->InAt(is_instance ? 1 : 0),
+ (Primitive::Is64BitType(field_type) ? Primitive::kPrimLong : Primitive::kPrimInt));
+ }
+
+ QuickEntrypointEnum entrypoint = kQuickSet8Static; // Initialize to anything to avoid warnings.
+ switch (field_type) {
+ case Primitive::kPrimBoolean:
+ entrypoint = is_instance
+ ? (is_get ? kQuickGetBooleanInstance : kQuickSet8Instance)
+ : (is_get ? kQuickGetBooleanStatic : kQuickSet8Static);
+ break;
+ case Primitive::kPrimByte:
+ entrypoint = is_instance
+ ? (is_get ? kQuickGetByteInstance : kQuickSet8Instance)
+ : (is_get ? kQuickGetByteStatic : kQuickSet8Static);
+ break;
+ case Primitive::kPrimShort:
+ entrypoint = is_instance
+ ? (is_get ? kQuickGetShortInstance : kQuickSet16Instance)
+ : (is_get ? kQuickGetShortStatic : kQuickSet16Static);
+ break;
+ case Primitive::kPrimChar:
+ entrypoint = is_instance
+ ? (is_get ? kQuickGetCharInstance : kQuickSet16Instance)
+ : (is_get ? kQuickGetCharStatic : kQuickSet16Static);
+ break;
+ case Primitive::kPrimInt:
+ case Primitive::kPrimFloat:
+ entrypoint = is_instance
+ ? (is_get ? kQuickGet32Instance : kQuickSet32Instance)
+ : (is_get ? kQuickGet32Static : kQuickSet32Static);
+ break;
+ case Primitive::kPrimNot:
+ entrypoint = is_instance
+ ? (is_get ? kQuickGetObjInstance : kQuickSetObjInstance)
+ : (is_get ? kQuickGetObjStatic : kQuickSetObjStatic);
+ break;
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ entrypoint = is_instance
+ ? (is_get ? kQuickGet64Instance : kQuickSet64Instance)
+ : (is_get ? kQuickGet64Static : kQuickSet64Static);
+ break;
+ default:
+ LOG(FATAL) << "Invalid type " << field_type;
+ }
+ InvokeRuntime(entrypoint, field_access, dex_pc, nullptr);
+
+ if (is_get && Primitive::IsFloatingPointType(field_type)) {
+ MoveLocation(locations->Out(), calling_convention.GetReturnLocation(field_type), field_type);
+ }
+}
+
+void CodeGenerator::CreateLoadClassLocationSummary(HLoadClass* cls,
+ Location runtime_type_index_location,
+ Location runtime_return_location) {
+ ArenaAllocator* allocator = cls->GetBlock()->GetGraph()->GetArena();
+ LocationSummary::CallKind call_kind = cls->NeedsAccessCheck()
+ ? LocationSummary::kCall
+ : (cls->CanCallRuntime()
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall);
+ LocationSummary* locations = new (allocator) LocationSummary(cls, call_kind);
+ if (cls->NeedsAccessCheck()) {
+ locations->SetInAt(0, Location::NoLocation());
+ locations->AddTemp(runtime_type_index_location);
+ locations->SetOut(runtime_return_location);
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+ }
+}
+
+
void CodeGenerator::BlockIfInRegister(Location location, bool is_out) const {
// The DCHECKS below check that a register is not specified twice in
// the summary. The out location can overlap with an input, so we need
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 5da0e59..0a36989 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -143,6 +143,22 @@
DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitor);
};
+class FieldAccessCallingConvention {
+ public:
+ virtual Location GetObjectLocation() const = 0;
+ virtual Location GetFieldIndexLocation() const = 0;
+ virtual Location GetReturnLocation(Primitive::Type type) const = 0;
+ virtual Location GetSetValueLocation(Primitive::Type type, bool is_instance) const = 0;
+ virtual Location GetFpuLocation(Primitive::Type type) const = 0;
+ virtual ~FieldAccessCallingConvention() {}
+
+ protected:
+ FieldAccessCallingConvention() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConvention);
+};
+
class CodeGenerator {
public:
// Compiles the graph to executable instructions. Returns whether the compilation
@@ -177,6 +193,9 @@
virtual void Bind(HBasicBlock* block) = 0;
virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) = 0;
virtual void MoveConstant(Location destination, int32_t value) = 0;
+ virtual void MoveLocation(Location dst, Location src, Primitive::Type dst_type) = 0;
+ virtual void AddLocationAsTemp(Location location, LocationSummary* locations) = 0;
+
virtual Assembler* GetAssembler() = 0;
virtual const Assembler& GetAssembler() const = 0;
virtual size_t GetWordSize() const = 0;
@@ -385,6 +404,23 @@
void GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invoke);
+ void CreateUnresolvedFieldLocationSummary(
+ HInstruction* field_access,
+ Primitive::Type field_type,
+ const FieldAccessCallingConvention& calling_convention);
+
+ void GenerateUnresolvedFieldAccess(
+ HInstruction* field_access,
+ Primitive::Type field_type,
+ uint32_t field_index,
+ uint32_t dex_pc,
+ const FieldAccessCallingConvention& calling_convention);
+
+ // TODO: This overlaps a bit with MoveFromReturnRegister. Refactor for a better design.
+ static void CreateLoadClassLocationSummary(HLoadClass* cls,
+ Location runtime_type_index_location,
+ Location runtime_return_location);
+
void SetDisassemblyInformation(DisassemblyInformation* info) { disasm_info_ = info; }
DisassemblyInformation* GetDisassemblyInformation() const { return disasm_info_; }
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index a7dbb53..08d8d88 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -473,12 +473,8 @@
kNumberOfRegisterPairs,
ComputeRegisterMask(reinterpret_cast<const int*>(kCoreCalleeSaves),
arraysize(kCoreCalleeSaves)),
- graph->IsDebuggable()
- // If the graph is debuggable, we need to save the fpu registers ourselves,
- // as the stubs do not do it.
- ? 0
- : ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves),
- arraysize(kFpuCalleeSaves)),
+ ComputeRegisterMask(reinterpret_cast<const int*>(kFpuCalleeSaves),
+ arraysize(kFpuCalleeSaves)),
compiler_options,
stats),
block_labels_(nullptr),
@@ -611,7 +607,12 @@
}
blocked_core_registers_[kCoreSavedRegisterForBaseline] = false;
+ }
+ if (is_baseline || GetGraph()->IsDebuggable()) {
+ // Stubs do not save callee-save floating point registers. If the graph
+ // is debuggable, we need to deal with these registers differently. For
+ // now, just block them.
for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
blocked_fpu_registers_[kFpuCalleeSaves[i]] = true;
}
@@ -906,6 +907,10 @@
Primitive::kPrimInt);
} else if (source.IsFpuRegister()) {
UNIMPLEMENTED(FATAL);
+ } else if (source.IsFpuRegisterPair()) {
+ __ vmovrrd(destination.AsRegisterPairLow<Register>(),
+ destination.AsRegisterPairHigh<Register>(),
+ FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
} else {
DCHECK(source.IsDoubleStackSlot());
DCHECK(ExpectedPairLayout(destination));
@@ -917,6 +922,10 @@
__ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
SP,
source.GetStackIndex());
+ } else if (source.IsRegisterPair()) {
+ __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
+ source.AsRegisterPairLow<Register>(),
+ source.AsRegisterPairHigh<Register>());
} else {
UNIMPLEMENTED(FATAL);
}
@@ -1038,6 +1047,25 @@
__ LoadImmediate(location.AsRegister<Register>(), value);
}
+void CodeGeneratorARM::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
+ if (Primitive::Is64BitType(dst_type)) {
+ Move64(dst, src);
+ } else {
+ Move32(dst, src);
+ }
+}
+
+void CodeGeneratorARM::AddLocationAsTemp(Location location, LocationSummary* locations) {
+ if (location.IsRegister()) {
+ locations->AddTemp(location);
+ } else if (location.IsRegisterPair()) {
+ locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairLow<Register>()));
+ locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairHigh<Register>()));
+ } else {
+ UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
+ }
+}
+
void CodeGeneratorARM::InvokeRuntime(QuickEntrypointEnum entrypoint,
HInstruction* instruction,
uint32_t dex_pc,
@@ -3605,6 +3633,74 @@
HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
+void LocationsBuilderARM::VisitUnresolvedInstanceFieldGet(
+ HUnresolvedInstanceFieldGet* instruction) {
+ FieldAccessCallingConventionARM calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldGet(
+ HUnresolvedInstanceFieldGet* instruction) {
+ FieldAccessCallingConventionARM calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
+void LocationsBuilderARM::VisitUnresolvedInstanceFieldSet(
+ HUnresolvedInstanceFieldSet* instruction) {
+ FieldAccessCallingConventionARM calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM::VisitUnresolvedInstanceFieldSet(
+ HUnresolvedInstanceFieldSet* instruction) {
+ FieldAccessCallingConventionARM calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
+void LocationsBuilderARM::VisitUnresolvedStaticFieldGet(
+ HUnresolvedStaticFieldGet* instruction) {
+ FieldAccessCallingConventionARM calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldGet(
+ HUnresolvedStaticFieldGet* instruction) {
+ FieldAccessCallingConventionARM calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
+void LocationsBuilderARM::VisitUnresolvedStaticFieldSet(
+ HUnresolvedStaticFieldSet* instruction) {
+ FieldAccessCallingConventionARM calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM::VisitUnresolvedStaticFieldSet(
+ HUnresolvedStaticFieldSet* instruction) {
+ FieldAccessCallingConventionARM calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
? LocationSummary::kCallOnSlowPath
@@ -4372,17 +4468,24 @@
}
void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) {
- LocationSummary::CallKind call_kind = cls->CanCallRuntime()
- ? LocationSummary::kCallOnSlowPath
- : LocationSummary::kNoCall;
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister());
+ InvokeRuntimeCallingConvention calling_convention;
+ CodeGenerator::CreateLoadClassLocationSummary(
+ cls,
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ Location::RegisterLocation(R0));
}
void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) {
LocationSummary* locations = cls->GetLocations();
+ if (cls->NeedsAccessCheck()) {
+ codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess),
+ cls,
+ cls->GetDexPc(),
+ nullptr);
+ return;
+ }
+
Register out = locations->Out().AsRegister<Register>();
Register current_method = locations->InAt(0).AsRegister<Register>();
if (cls->IsReferrersClass()) {
@@ -4508,6 +4611,7 @@
case TypeCheckKind::kArrayObjectCheck:
call_kind = LocationSummary::kNoCall;
break;
+ case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCall;
break;
@@ -4548,10 +4652,11 @@
__ CompareAndBranchIfZero(obj, &zero);
}
- // In case of an interface check, we put the object class into the object register.
+ // In case of an interface/unresolved check, we put the object class into the object register.
// This is safe, as the register is caller-save, and the object must be in another
// register if it survives the runtime call.
- Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck)
+ Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) ||
+ (instruction->GetTypeCheckKind() == TypeCheckKind::kUnresolvedCheck)
? obj
: out;
__ LoadFromOffset(kLoadWord, target, obj, class_offset);
@@ -4632,7 +4737,7 @@
}
break;
}
-
+ case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
default: {
codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial),
@@ -4673,6 +4778,7 @@
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
break;
+ case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCall;
break;
@@ -4777,6 +4883,7 @@
__ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
break;
}
+ case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
default:
codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast),
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 111112e..16d1d38 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -96,6 +96,38 @@
DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitorARM);
};
+class FieldAccessCallingConventionARM : public FieldAccessCallingConvention {
+ public:
+ FieldAccessCallingConventionARM() {}
+
+ Location GetObjectLocation() const OVERRIDE {
+ return Location::RegisterLocation(R1);
+ }
+ Location GetFieldIndexLocation() const OVERRIDE {
+ return Location::RegisterLocation(R0);
+ }
+ Location GetReturnLocation(Primitive::Type type) const OVERRIDE {
+ return Primitive::Is64BitType(type)
+ ? Location::RegisterPairLocation(R0, R1)
+ : Location::RegisterLocation(R0);
+ }
+ Location GetSetValueLocation(Primitive::Type type, bool is_instance) const OVERRIDE {
+ return Primitive::Is64BitType(type)
+ ? Location::RegisterPairLocation(R2, R3)
+ : (is_instance
+ ? Location::RegisterLocation(R2)
+ : Location::RegisterLocation(R1));
+ }
+ Location GetFpuLocation(Primitive::Type type) const OVERRIDE {
+ return Primitive::Is64BitType(type)
+ ? Location::FpuRegisterPairLocation(S0, S1)
+ : Location::FpuRegisterLocation(S0);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionARM);
+};
+
class ParallelMoveResolverARM : public ParallelMoveResolverWithSwap {
public:
ParallelMoveResolverARM(ArenaAllocator* allocator, CodeGeneratorARM* codegen)
@@ -225,6 +257,9 @@
void Bind(HBasicBlock* block) OVERRIDE;
void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
void MoveConstant(Location destination, int32_t value) OVERRIDE;
+ void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
+ void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
+
size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 78ecfde..415b37d 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -19,7 +19,6 @@
#include "arch/arm64/instruction_set_features_arm64.h"
#include "art_method.h"
#include "code_generator_utils.h"
-#include "common_arm64.h"
#include "compiled_method.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
@@ -119,11 +118,8 @@
CPURegList core_list = CPURegList(CPURegister::kRegister, kXRegSize,
register_set->GetCoreRegisters() & (~callee_saved_core_registers.list()));
- CPURegList fp_list = CPURegList(
- CPURegister::kFPRegister,
- kDRegSize,
- register_set->GetFloatingPointRegisters()
- & (~(codegen->GetGraph()->IsDebuggable() ? 0 : callee_saved_fp_registers.list())));
+ CPURegList fp_list = CPURegList(CPURegister::kFPRegister, kDRegSize,
+ register_set->GetFloatingPointRegisters() & (~callee_saved_fp_registers.list()));
MacroAssembler* masm = down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler();
UseScratchRegisterScope temps(masm);
@@ -583,9 +579,7 @@
kNumberOfAllocatableFPRegisters,
kNumberOfAllocatableRegisterPairs,
callee_saved_core_registers.list(),
- // If the graph is debuggable, we need to save the fpu registers ourselves,
- // as the stubs do not do it.
- graph->IsDebuggable() ? 0 : callee_saved_fp_registers.list(),
+ callee_saved_fp_registers.list(),
compiler_options,
stats),
block_labels_(nullptr),
@@ -666,7 +660,7 @@
void ParallelMoveResolverARM64::EmitMove(size_t index) {
DCHECK_LT(index, moves_.size());
MoveOperands* move = moves_[index];
- codegen_->MoveLocation(move->GetDestination(), move->GetSource());
+ codegen_->MoveLocation(move->GetDestination(), move->GetSource(), Primitive::kPrimVoid);
}
void CodeGeneratorARM64::GenerateFrameEntry() {
@@ -750,7 +744,9 @@
}
if (instruction->IsCurrentMethod()) {
- MoveLocation(location, Location::DoubleStackSlot(kCurrentMethodStackOffset));
+ MoveLocation(location,
+ Location::DoubleStackSlot(kCurrentMethodStackOffset),
+ Primitive::kPrimVoid);
} else if (locations != nullptr && locations->Out().Equals(location)) {
return;
} else if (instruction->IsIntConstant()
@@ -793,6 +789,14 @@
__ Mov(RegisterFrom(location, Primitive::kPrimInt), value);
}
+void CodeGeneratorARM64::AddLocationAsTemp(Location location, LocationSummary* locations) {
+ if (location.IsRegister()) {
+ locations->AddTemp(location);
+ } else {
+ UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
+ }
+}
+
Location CodeGeneratorARM64::GetStackLocation(HLoadLocal* load) const {
Primitive::Type type = load->GetType();
@@ -860,7 +864,12 @@
while (!reserved_core_baseline_registers.IsEmpty()) {
blocked_core_registers_[reserved_core_baseline_registers.PopLowestIndex().code()] = true;
}
+ }
+ if (is_baseline || GetGraph()->IsDebuggable()) {
+ // Stubs do not save callee-save floating point registers. If the graph
+ // is debuggable, we need to deal with these registers differently. For
+ // now, just block them.
CPURegList reserved_fp_baseline_registers = callee_saved_fp_registers;
while (!reserved_fp_baseline_registers.IsEmpty()) {
blocked_fpu_registers_[reserved_fp_baseline_registers.PopLowestIndex().code()] = true;
@@ -943,7 +952,9 @@
(cst->IsDoubleConstant() && type == Primitive::kPrimDouble);
}
-void CodeGeneratorARM64::MoveLocation(Location destination, Location source, Primitive::Type type) {
+void CodeGeneratorARM64::MoveLocation(Location destination,
+ Location source,
+ Primitive::Type dst_type) {
if (source.Equals(destination)) {
return;
}
@@ -952,7 +963,7 @@
// locations. When moving from and to a register, the argument type can be
// used to generate 32bit instead of 64bit moves. In debug mode we also
// checks the coherency of the locations and the type.
- bool unspecified_type = (type == Primitive::kPrimVoid);
+ bool unspecified_type = (dst_type == Primitive::kPrimVoid);
if (destination.IsRegister() || destination.IsFpuRegister()) {
if (unspecified_type) {
@@ -962,30 +973,44 @@
|| src_cst->IsFloatConstant()
|| src_cst->IsNullConstant()))) {
// For stack slots and 32bit constants, a 64bit type is appropriate.
- type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat;
+ dst_type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat;
} else {
// If the source is a double stack slot or a 64bit constant, a 64bit
// type is appropriate. Else the source is a register, and since the
// type has not been specified, we chose a 64bit type to force a 64bit
// move.
- type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble;
+ dst_type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble;
}
}
- DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(type)) ||
- (destination.IsRegister() && !Primitive::IsFloatingPointType(type)));
- CPURegister dst = CPURegisterFrom(destination, type);
+ DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(dst_type)) ||
+ (destination.IsRegister() && !Primitive::IsFloatingPointType(dst_type)));
+ CPURegister dst = CPURegisterFrom(destination, dst_type);
if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot());
__ Ldr(dst, StackOperandFrom(source));
} else if (source.IsConstant()) {
- DCHECK(CoherentConstantAndType(source, type));
+ DCHECK(CoherentConstantAndType(source, dst_type));
MoveConstant(dst, source.GetConstant());
- } else {
+ } else if (source.IsRegister()) {
if (destination.IsRegister()) {
- __ Mov(Register(dst), RegisterFrom(source, type));
+ __ Mov(Register(dst), RegisterFrom(source, dst_type));
} else {
DCHECK(destination.IsFpuRegister());
- __ Fmov(FPRegister(dst), FPRegisterFrom(source, type));
+ Primitive::Type source_type = Primitive::Is64BitType(dst_type)
+ ? Primitive::kPrimLong
+ : Primitive::kPrimInt;
+ __ Fmov(FPRegisterFrom(destination, dst_type), RegisterFrom(source, source_type));
+ }
+ } else {
+ DCHECK(source.IsFpuRegister());
+ if (destination.IsRegister()) {
+ Primitive::Type source_type = Primitive::Is64BitType(dst_type)
+ ? Primitive::kPrimDouble
+ : Primitive::kPrimFloat;
+ __ Fmov(RegisterFrom(destination, dst_type), FPRegisterFrom(source, source_type));
+ } else {
+ DCHECK(destination.IsFpuRegister());
+ __ Fmov(FPRegister(dst), FPRegisterFrom(source, dst_type));
}
}
} else { // The destination is not a register. It must be a stack slot.
@@ -993,16 +1018,17 @@
if (source.IsRegister() || source.IsFpuRegister()) {
if (unspecified_type) {
if (source.IsRegister()) {
- type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong;
+ dst_type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong;
} else {
- type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble;
+ dst_type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble;
}
}
- DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(type)) &&
- (source.IsFpuRegister() == Primitive::IsFloatingPointType(type)));
- __ Str(CPURegisterFrom(source, type), StackOperandFrom(destination));
+ DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(dst_type)) &&
+ (source.IsFpuRegister() == Primitive::IsFloatingPointType(dst_type)));
+ __ Str(CPURegisterFrom(source, dst_type), StackOperandFrom(destination));
} else if (source.IsConstant()) {
- DCHECK(unspecified_type || CoherentConstantAndType(source, type)) << source << " " << type;
+ DCHECK(unspecified_type || CoherentConstantAndType(source, dst_type))
+ << source << " " << dst_type;
UseScratchRegisterScope temps(GetVIXLAssembler());
HConstant* src_cst = source.GetConstant();
CPURegister temp;
@@ -2362,6 +2388,7 @@
case TypeCheckKind::kArrayObjectCheck:
call_kind = LocationSummary::kNoCall;
break;
+ case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCall;
break;
@@ -2403,10 +2430,11 @@
__ Cbz(obj, &zero);
}
- // In case of an interface check, we put the object class into the object register.
+ // In case of an interface/unresolved check, we put the object class into the object register.
// This is safe, as the register is caller-save, and the object must be in another
// register if it survives the runtime call.
- Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck)
+ Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) ||
+ (instruction->GetTypeCheckKind() == TypeCheckKind::kUnresolvedCheck)
? obj
: out;
__ Ldr(target, HeapOperand(obj.W(), class_offset));
@@ -2487,7 +2515,7 @@
}
break;
}
-
+ case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
default: {
codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial),
@@ -2528,6 +2556,7 @@
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
break;
+ case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCall;
break;
@@ -2633,6 +2662,7 @@
__ Cbnz(temp, slow_path->GetEntryLabel());
break;
}
+ case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
default:
codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast),
@@ -2988,14 +3018,23 @@
}
void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) {
- LocationSummary::CallKind call_kind = cls->CanCallRuntime() ? LocationSummary::kCallOnSlowPath
- : LocationSummary::kNoCall;
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister());
+ InvokeRuntimeCallingConvention calling_convention;
+ CodeGenerator::CreateLoadClassLocationSummary(
+ cls,
+ LocationFrom(calling_convention.GetRegisterAt(0)),
+ LocationFrom(vixl::x0));
}
void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) {
+ if (cls->NeedsAccessCheck()) {
+ codegen_->MoveConstant(cls->GetLocations()->GetTemp(0), cls->GetTypeIndex());
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess),
+ cls,
+ cls->GetDexPc(),
+ nullptr);
+ return;
+ }
+
Register out = OutputRegister(cls);
Register current_method = InputRegisterAt(cls, 0);
if (cls->IsReferrersClass()) {
@@ -3508,6 +3547,74 @@
HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
+void LocationsBuilderARM64::VisitUnresolvedInstanceFieldGet(
+ HUnresolvedInstanceFieldGet* instruction) {
+ FieldAccessCallingConventionARM64 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM64::VisitUnresolvedInstanceFieldGet(
+ HUnresolvedInstanceFieldGet* instruction) {
+ FieldAccessCallingConventionARM64 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
+void LocationsBuilderARM64::VisitUnresolvedInstanceFieldSet(
+ HUnresolvedInstanceFieldSet* instruction) {
+ FieldAccessCallingConventionARM64 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM64::VisitUnresolvedInstanceFieldSet(
+ HUnresolvedInstanceFieldSet* instruction) {
+ FieldAccessCallingConventionARM64 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
+void LocationsBuilderARM64::VisitUnresolvedStaticFieldGet(
+ HUnresolvedStaticFieldGet* instruction) {
+ FieldAccessCallingConventionARM64 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM64::VisitUnresolvedStaticFieldGet(
+ HUnresolvedStaticFieldGet* instruction) {
+ FieldAccessCallingConventionARM64 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
+void LocationsBuilderARM64::VisitUnresolvedStaticFieldSet(
+ HUnresolvedStaticFieldSet* instruction) {
+ FieldAccessCallingConventionARM64 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorARM64::VisitUnresolvedStaticFieldSet(
+ HUnresolvedStaticFieldSet* instruction) {
+ FieldAccessCallingConventionARM64 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
void LocationsBuilderARM64::VisitSuspendCheck(HSuspendCheck* instruction) {
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
}
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 7178081..a068b48 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_
#include "code_generator.h"
+#include "common_arm64.h"
#include "dex/compiler_enums.h"
#include "driver/compiler_options.h"
#include "nodes.h"
@@ -141,6 +142,34 @@
DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitorARM64);
};
+class FieldAccessCallingConventionARM64 : public FieldAccessCallingConvention {
+ public:
+ FieldAccessCallingConventionARM64() {}
+
+ Location GetObjectLocation() const OVERRIDE {
+ return helpers::LocationFrom(vixl::x1);
+ }
+ Location GetFieldIndexLocation() const OVERRIDE {
+ return helpers::LocationFrom(vixl::x0);
+ }
+ Location GetReturnLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+ return helpers::LocationFrom(vixl::x0);
+ }
+ Location GetSetValueLocation(Primitive::Type type, bool is_instance) const OVERRIDE {
+ return Primitive::Is64BitType(type)
+ ? helpers::LocationFrom(vixl::x2)
+ : (is_instance
+ ? helpers::LocationFrom(vixl::x2)
+ : helpers::LocationFrom(vixl::x1));
+ }
+ Location GetFpuLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+ return helpers::LocationFrom(vixl::d0);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionARM64);
+};
+
class InstructionCodeGeneratorARM64 : public HGraphVisitor {
public:
InstructionCodeGeneratorARM64(HGraph* graph, CodeGeneratorARM64* codegen);
@@ -334,10 +363,9 @@
// Code generation helpers.
void MoveConstant(vixl::CPURegister destination, HConstant* constant);
void MoveConstant(Location destination, int32_t value) OVERRIDE;
- // The type is optional. When specified it must be coherent with the
- // locations, and is used for optimisation and debugging.
- void MoveLocation(Location destination, Location source,
- Primitive::Type type = Primitive::kPrimVoid);
+ void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
+ void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
+
void Load(Primitive::Type type, vixl::CPURegister dst, const vixl::MemOperand& src);
void Store(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst);
void LoadAcquire(HInstruction* instruction, vixl::CPURegister dst, const vixl::MemOperand& src);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index ad0a39c..756336d 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -617,7 +617,7 @@
void CodeGeneratorMIPS64::MoveLocation(Location destination,
Location source,
- Primitive::Type type) {
+ Primitive::Type dst_type) {
if (source.Equals(destination)) {
return;
}
@@ -625,7 +625,7 @@
// A valid move can always be inferred from the destination and source
// locations. When moving from and to a register, the argument type can be
// used to generate 32bit instead of 64bit moves.
- bool unspecified_type = (type == Primitive::kPrimVoid);
+ bool unspecified_type = (dst_type == Primitive::kPrimVoid);
DCHECK_EQ(unspecified_type, false);
if (destination.IsRegister() || destination.IsFpuRegister()) {
@@ -636,21 +636,21 @@
|| src_cst->IsFloatConstant()
|| src_cst->IsNullConstant()))) {
// For stack slots and 32bit constants, a 64bit type is appropriate.
- type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat;
+ dst_type = destination.IsRegister() ? Primitive::kPrimInt : Primitive::kPrimFloat;
} else {
// If the source is a double stack slot or a 64bit constant, a 64bit
// type is appropriate. Else the source is a register, and since the
// type has not been specified, we chose a 64bit type to force a 64bit
// move.
- type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble;
+ dst_type = destination.IsRegister() ? Primitive::kPrimLong : Primitive::kPrimDouble;
}
}
- DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(type)) ||
- (destination.IsRegister() && !Primitive::IsFloatingPointType(type)));
+ DCHECK((destination.IsFpuRegister() && Primitive::IsFloatingPointType(dst_type)) ||
+ (destination.IsRegister() && !Primitive::IsFloatingPointType(dst_type)));
if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
// Move to GPR/FPR from stack
LoadOperandType load_type = source.IsStackSlot() ? kLoadWord : kLoadDoubleword;
- if (Primitive::IsFloatingPointType(type)) {
+ if (Primitive::IsFloatingPointType(dst_type)) {
__ LoadFpuFromOffset(load_type,
destination.AsFpuRegister<FpuRegister>(),
SP,
@@ -665,31 +665,47 @@
} else if (source.IsConstant()) {
// Move to GPR/FPR from constant
GpuRegister gpr = AT;
- if (!Primitive::IsFloatingPointType(type)) {
+ if (!Primitive::IsFloatingPointType(dst_type)) {
gpr = destination.AsRegister<GpuRegister>();
}
- if (type == Primitive::kPrimInt || type == Primitive::kPrimFloat) {
+ if (dst_type == Primitive::kPrimInt || dst_type == Primitive::kPrimFloat) {
__ LoadConst32(gpr, GetInt32ValueOf(source.GetConstant()->AsConstant()));
} else {
__ LoadConst64(gpr, GetInt64ValueOf(source.GetConstant()->AsConstant()));
}
- if (type == Primitive::kPrimFloat) {
+ if (dst_type == Primitive::kPrimFloat) {
__ Mtc1(gpr, destination.AsFpuRegister<FpuRegister>());
- } else if (type == Primitive::kPrimDouble) {
+ } else if (dst_type == Primitive::kPrimDouble) {
__ Dmtc1(gpr, destination.AsFpuRegister<FpuRegister>());
}
- } else {
+ } else if (source.IsRegister()) {
if (destination.IsRegister()) {
// Move to GPR from GPR
__ Move(destination.AsRegister<GpuRegister>(), source.AsRegister<GpuRegister>());
} else {
+ DCHECK(destination.IsFpuRegister());
+ if (Primitive::Is64BitType(dst_type)) {
+ __ Dmtc1(source.AsRegister<GpuRegister>(), destination.AsFpuRegister<FpuRegister>());
+ } else {
+ __ Mtc1(source.AsRegister<GpuRegister>(), destination.AsFpuRegister<FpuRegister>());
+ }
+ }
+ } else if (source.IsFpuRegister()) {
+ if (destination.IsFpuRegister()) {
// Move to FPR from FPR
- if (type == Primitive::kPrimFloat) {
+ if (dst_type == Primitive::kPrimFloat) {
__ MovS(destination.AsFpuRegister<FpuRegister>(), source.AsFpuRegister<FpuRegister>());
} else {
- DCHECK_EQ(type, Primitive::kPrimDouble);
+ DCHECK_EQ(dst_type, Primitive::kPrimDouble);
__ MovD(destination.AsFpuRegister<FpuRegister>(), source.AsFpuRegister<FpuRegister>());
}
+ } else {
+ DCHECK(destination.IsRegister());
+ if (Primitive::Is64BitType(dst_type)) {
+ __ Dmfc1(destination.AsRegister<GpuRegister>(), source.AsFpuRegister<FpuRegister>());
+ } else {
+ __ Mfc1(destination.AsRegister<GpuRegister>(), source.AsFpuRegister<FpuRegister>());
+ }
}
}
} else { // The destination is not a register. It must be a stack slot.
@@ -697,13 +713,13 @@
if (source.IsRegister() || source.IsFpuRegister()) {
if (unspecified_type) {
if (source.IsRegister()) {
- type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong;
+ dst_type = destination.IsStackSlot() ? Primitive::kPrimInt : Primitive::kPrimLong;
} else {
- type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble;
+ dst_type = destination.IsStackSlot() ? Primitive::kPrimFloat : Primitive::kPrimDouble;
}
}
- DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(type)) &&
- (source.IsFpuRegister() == Primitive::IsFloatingPointType(type)));
+ DCHECK((destination.IsDoubleStackSlot() == Primitive::Is64BitType(dst_type)) &&
+ (source.IsFpuRegister() == Primitive::IsFloatingPointType(dst_type)));
// Move to stack from GPR/FPR
StoreOperandType store_type = destination.IsStackSlot() ? kStoreWord : kStoreDoubleword;
if (source.IsRegister()) {
@@ -861,6 +877,14 @@
__ LoadConst32(location.AsRegister<GpuRegister>(), value);
}
+void CodeGeneratorMIPS64::AddLocationAsTemp(Location location, LocationSummary* locations) {
+ if (location.IsRegister()) {
+ locations->AddTemp(location);
+ } else {
+ UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
+ }
+}
+
Location CodeGeneratorMIPS64::GetStackLocation(HLoadLocal* load) const {
Primitive::Type type = load->GetType();
@@ -2566,15 +2590,24 @@
}
void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) {
- LocationSummary::CallKind call_kind = cls->CanCallRuntime() ? LocationSummary::kCallOnSlowPath
- : LocationSummary::kNoCall;
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister());
+ InvokeRuntimeCallingConvention calling_convention;
+ CodeGenerator::CreateLoadClassLocationSummary(
+ cls,
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ Location::RegisterLocation(A0));
}
void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) {
LocationSummary* locations = cls->GetLocations();
+ if (cls->NeedsAccessCheck()) {
+ codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess),
+ cls,
+ cls->GetDexPc(),
+ nullptr);
+ return;
+ }
+
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
GpuRegister current_method = locations->InAt(0).AsRegister<GpuRegister>();
if (cls->IsReferrersClass()) {
@@ -3118,6 +3151,74 @@
HandleFieldSet(instruction, instruction->GetFieldInfo());
}
+void LocationsBuilderMIPS64::VisitUnresolvedInstanceFieldGet(
+ HUnresolvedInstanceFieldGet* instruction) {
+ FieldAccessCallingConventionMIPS64 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitUnresolvedInstanceFieldGet(
+ HUnresolvedInstanceFieldGet* instruction) {
+ FieldAccessCallingConventionMIPS64 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
+void LocationsBuilderMIPS64::VisitUnresolvedInstanceFieldSet(
+ HUnresolvedInstanceFieldSet* instruction) {
+ FieldAccessCallingConventionMIPS64 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitUnresolvedInstanceFieldSet(
+ HUnresolvedInstanceFieldSet* instruction) {
+ FieldAccessCallingConventionMIPS64 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
+void LocationsBuilderMIPS64::VisitUnresolvedStaticFieldGet(
+ HUnresolvedStaticFieldGet* instruction) {
+ FieldAccessCallingConventionMIPS64 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitUnresolvedStaticFieldGet(
+ HUnresolvedStaticFieldGet* instruction) {
+ FieldAccessCallingConventionMIPS64 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
+void LocationsBuilderMIPS64::VisitUnresolvedStaticFieldSet(
+ HUnresolvedStaticFieldSet* instruction) {
+ FieldAccessCallingConventionMIPS64 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitUnresolvedStaticFieldSet(
+ HUnresolvedStaticFieldSet* instruction) {
+ FieldAccessCallingConventionMIPS64 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
void LocationsBuilderMIPS64::VisitSuspendCheck(HSuspendCheck* instruction) {
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
}
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 16461d6..5e8f9e7 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -106,6 +106,31 @@
DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention);
};
+class FieldAccessCallingConventionMIPS64 : public FieldAccessCallingConvention {
+ public:
+ FieldAccessCallingConventionMIPS64() {}
+
+ Location GetObjectLocation() const OVERRIDE {
+ return Location::RegisterLocation(A1);
+ }
+ Location GetFieldIndexLocation() const OVERRIDE {
+ return Location::RegisterLocation(A0);
+ }
+ Location GetReturnLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+ return Location::RegisterLocation(A0);
+ }
+ Location GetSetValueLocation(
+ Primitive::Type type ATTRIBUTE_UNUSED, bool is_instance) const OVERRIDE {
+ return is_instance ? Location::RegisterLocation(A2) : Location::RegisterLocation(A1);
+ }
+ Location GetFpuLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+ return Location::FpuRegisterLocation(F0);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionMIPS64);
+};
+
class ParallelMoveResolverMIPS64 : public ParallelMoveResolverWithSwap {
public:
ParallelMoveResolverMIPS64(ArenaAllocator* allocator, CodeGeneratorMIPS64* codegen)
@@ -280,11 +305,13 @@
void Finalize(CodeAllocator* allocator) OVERRIDE;
// Code generation helpers.
-
- void MoveLocation(Location destination, Location source, Primitive::Type type);
+ void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
void MoveConstant(Location destination, int32_t value) OVERRIDE;
+ void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
+
+
void SwapLocations(Location loc1, Location loc2, Primitive::Type type);
// Generate code to invoke a runtime entry point.
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 3d97132..5ef7de0 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -827,7 +827,10 @@
Location::RegisterLocation(destination.AsRegisterPairLow<Register>()),
Primitive::kPrimInt);
} else if (source.IsFpuRegister()) {
- LOG(FATAL) << "Unimplemented";
+ XmmRegister src_reg = source.AsFpuRegister<XmmRegister>();
+ __ movd(destination.AsRegisterPairLow<Register>(), src_reg);
+ __ psrlq(src_reg, Immediate(32));
+ __ movd(destination.AsRegisterPairHigh<Register>(), src_reg);
} else {
// No conflict possible, so just do the moves.
DCHECK(source.IsDoubleStackSlot());
@@ -840,6 +843,15 @@
__ movaps(destination.AsFpuRegister<XmmRegister>(), source.AsFpuRegister<XmmRegister>());
} else if (source.IsDoubleStackSlot()) {
__ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, source.GetStackIndex()));
+ } else if (source.IsRegisterPair()) {
+ size_t elem_size = Primitive::ComponentSize(Primitive::kPrimInt);
+ // Create stack space for 2 elements.
+ __ subl(ESP, Immediate(2 * elem_size));
+ __ movl(Address(ESP, 0), source.AsRegisterPairLow<Register>());
+ __ movl(Address(ESP, elem_size), source.AsRegisterPairHigh<Register>());
+ __ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, 0));
+ // And remove the temporary stack space we allocated.
+ __ addl(ESP, Immediate(2 * elem_size));
} else {
LOG(FATAL) << "Unimplemented";
}
@@ -966,6 +978,25 @@
__ movl(location.AsRegister<Register>(), Immediate(value));
}
+void CodeGeneratorX86::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
+ if (Primitive::Is64BitType(dst_type)) {
+ Move64(dst, src);
+ } else {
+ Move32(dst, src);
+ }
+}
+
+void CodeGeneratorX86::AddLocationAsTemp(Location location, LocationSummary* locations) {
+ if (location.IsRegister()) {
+ locations->AddTemp(location);
+ } else if (location.IsRegisterPair()) {
+ locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairLow<Register>()));
+ locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairHigh<Register>()));
+ } else {
+ UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
+ }
+}
+
void InstructionCodeGeneratorX86::HandleGoto(HInstruction* got, HBasicBlock* successor) {
DCHECK(!successor->IsExitBlock());
@@ -4085,6 +4116,74 @@
HandleFieldGet(instruction, instruction->GetFieldInfo());
}
+void LocationsBuilderX86::VisitUnresolvedInstanceFieldGet(
+ HUnresolvedInstanceFieldGet* instruction) {
+ FieldAccessCallingConventionX86 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86::VisitUnresolvedInstanceFieldGet(
+ HUnresolvedInstanceFieldGet* instruction) {
+ FieldAccessCallingConventionX86 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
+void LocationsBuilderX86::VisitUnresolvedInstanceFieldSet(
+ HUnresolvedInstanceFieldSet* instruction) {
+ FieldAccessCallingConventionX86 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86::VisitUnresolvedInstanceFieldSet(
+ HUnresolvedInstanceFieldSet* instruction) {
+ FieldAccessCallingConventionX86 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
+void LocationsBuilderX86::VisitUnresolvedStaticFieldGet(
+ HUnresolvedStaticFieldGet* instruction) {
+ FieldAccessCallingConventionX86 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86::VisitUnresolvedStaticFieldGet(
+ HUnresolvedStaticFieldGet* instruction) {
+ FieldAccessCallingConventionX86 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
+void LocationsBuilderX86::VisitUnresolvedStaticFieldSet(
+ HUnresolvedStaticFieldSet* instruction) {
+ FieldAccessCallingConventionX86 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86::VisitUnresolvedStaticFieldSet(
+ HUnresolvedStaticFieldSet* instruction) {
+ FieldAccessCallingConventionX86 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) {
LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
? LocationSummary::kCallOnSlowPath
@@ -4890,17 +4989,24 @@
}
void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) {
- LocationSummary::CallKind call_kind = cls->CanCallRuntime()
- ? LocationSummary::kCallOnSlowPath
- : LocationSummary::kNoCall;
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister());
+ InvokeRuntimeCallingConvention calling_convention;
+ CodeGenerator::CreateLoadClassLocationSummary(
+ cls,
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ Location::RegisterLocation(EAX));
}
void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) {
LocationSummary* locations = cls->GetLocations();
+ if (cls->NeedsAccessCheck()) {
+ codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess),
+ cls,
+ cls->GetDexPc(),
+ nullptr);
+ return;
+ }
+
Register out = locations->Out().AsRegister<Register>();
Register current_method = locations->InAt(0).AsRegister<Register>();
if (cls->IsReferrersClass()) {
@@ -5022,6 +5128,7 @@
case TypeCheckKind::kArrayObjectCheck:
call_kind = LocationSummary::kNoCall;
break;
+ case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCall;
break;
@@ -5062,10 +5169,11 @@
__ j(kEqual, &zero);
}
- // In case of an interface check, we put the object class into the object register.
+ // In case of an interface/unresolved check, we put the object class into the object register.
// This is safe, as the register is caller-save, and the object must be in another
// register if it survives the runtime call.
- Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck)
+ Register target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) ||
+ (instruction->GetTypeCheckKind() == TypeCheckKind::kUnresolvedCheck)
? obj
: out;
__ movl(target, Address(obj, class_offset));
@@ -5174,7 +5282,7 @@
}
break;
}
-
+ case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
default: {
codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial),
@@ -5216,6 +5324,7 @@
: LocationSummary::kNoCall;
break;
case TypeCheckKind::kInterfaceCheck:
+ case TypeCheckKind::kUnresolvedCheck:
call_kind = LocationSummary::kCall;
break;
case TypeCheckKind::kArrayCheck:
@@ -5342,6 +5451,7 @@
__ j(kNotEqual, slow_path->GetEntryLabel());
break;
}
+ case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
default:
codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast),
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 2c2fc65..ae2d84f 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -91,6 +91,36 @@
DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitorX86);
};
+class FieldAccessCallingConventionX86 : public FieldAccessCallingConvention {
+ public:
+ FieldAccessCallingConventionX86() {}
+
+ Location GetObjectLocation() const OVERRIDE {
+ return Location::RegisterLocation(ECX);
+ }
+ Location GetFieldIndexLocation() const OVERRIDE {
+ return Location::RegisterLocation(EAX);
+ }
+ Location GetReturnLocation(Primitive::Type type) const OVERRIDE {
+ return Primitive::Is64BitType(type)
+ ? Location::RegisterPairLocation(EAX, EDX)
+ : Location::RegisterLocation(EAX);
+ }
+ Location GetSetValueLocation(Primitive::Type type, bool is_instance) const OVERRIDE {
+ return Primitive::Is64BitType(type)
+ ? Location::RegisterPairLocation(EDX, EBX)
+ : (is_instance
+ ? Location::RegisterLocation(EDX)
+ : Location::RegisterLocation(ECX));
+ }
+ Location GetFpuLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+ return Location::FpuRegisterLocation(XMM0);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionX86);
+};
+
class ParallelMoveResolverX86 : public ParallelMoveResolverWithSwap {
public:
ParallelMoveResolverX86(ArenaAllocator* allocator, CodeGeneratorX86* codegen)
@@ -228,6 +258,9 @@
void Bind(HBasicBlock* block) OVERRIDE;
void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
void MoveConstant(Location destination, int32_t value) OVERRIDE;
+ void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
+ void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
+
size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 6ea6138..272d86f 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -990,6 +990,19 @@
Load64BitValue(location.AsRegister<CpuRegister>(), static_cast<int64_t>(value));
}
+void CodeGeneratorX86_64::MoveLocation(
+ Location dst, Location src, Primitive::Type dst_type ATTRIBUTE_UNUSED) {
+ Move(dst, src);
+}
+
+void CodeGeneratorX86_64::AddLocationAsTemp(Location location, LocationSummary* locations) {
+ if (location.IsRegister()) {
+ locations->AddTemp(location);
+ } else {
+ UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
+ }
+}
+
void InstructionCodeGeneratorX86_64::HandleGoto(HInstruction* got, HBasicBlock* successor) {
DCHECK(!successor->IsExitBlock());
@@ -3849,6 +3862,74 @@
HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
+void LocationsBuilderX86_64::VisitUnresolvedInstanceFieldGet(
+ HUnresolvedInstanceFieldGet* instruction) {
+ FieldAccessCallingConventionX86_64 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86_64::VisitUnresolvedInstanceFieldGet(
+ HUnresolvedInstanceFieldGet* instruction) {
+ FieldAccessCallingConventionX86_64 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
+void LocationsBuilderX86_64::VisitUnresolvedInstanceFieldSet(
+ HUnresolvedInstanceFieldSet* instruction) {
+ FieldAccessCallingConventionX86_64 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86_64::VisitUnresolvedInstanceFieldSet(
+ HUnresolvedInstanceFieldSet* instruction) {
+ FieldAccessCallingConventionX86_64 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
+void LocationsBuilderX86_64::VisitUnresolvedStaticFieldGet(
+ HUnresolvedStaticFieldGet* instruction) {
+ FieldAccessCallingConventionX86_64 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86_64::VisitUnresolvedStaticFieldGet(
+ HUnresolvedStaticFieldGet* instruction) {
+ FieldAccessCallingConventionX86_64 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
+void LocationsBuilderX86_64::VisitUnresolvedStaticFieldSet(
+ HUnresolvedStaticFieldSet* instruction) {
+ FieldAccessCallingConventionX86_64 calling_convention;
+ codegen_->CreateUnresolvedFieldLocationSummary(
+ instruction, instruction->GetFieldType(), calling_convention);
+}
+
+void InstructionCodeGeneratorX86_64::VisitUnresolvedStaticFieldSet(
+ HUnresolvedStaticFieldSet* instruction) {
+ FieldAccessCallingConventionX86_64 calling_convention;
+ codegen_->GenerateUnresolvedFieldAccess(instruction,
+ instruction->GetFieldType(),
+ instruction->GetFieldIndex(),
+ instruction->GetDexPc(),
+ calling_convention);
+}
+
void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) {
LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock()
? LocationSummary::kCallOnSlowPath
@@ -4613,17 +4694,24 @@
}
void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) {
- LocationSummary::CallKind call_kind = cls->CanCallRuntime()
- ? LocationSummary::kCallOnSlowPath
- : LocationSummary::kNoCall;
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister());
+ InvokeRuntimeCallingConvention calling_convention;
+ CodeGenerator::CreateLoadClassLocationSummary(
+ cls,
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ Location::RegisterLocation(RAX));
}
void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) {
LocationSummary* locations = cls->GetLocations();
+ if (cls->NeedsAccessCheck()) {
+ codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess),
+ cls,
+ cls->GetDexPc(),
+ nullptr);
+ return;
+ }
+
CpuRegister out = locations->Out().AsRegister<CpuRegister>();
CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
if (cls->IsReferrersClass()) {
@@ -4736,6 +4824,7 @@
case TypeCheckKind::kArrayObjectCheck:
call_kind = LocationSummary::kNoCall;
break;
+ case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCall;
break;
@@ -4776,10 +4865,11 @@
__ j(kEqual, &zero);
}
- // In case of an interface check, we put the object class into the object register.
+ // In case of an interface/unresolved check, we put the object class into the object register.
// This is safe, as the register is caller-save, and the object must be in another
// register if it survives the runtime call.
- CpuRegister target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck)
+ CpuRegister target = (instruction->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) ||
+ (instruction->GetTypeCheckKind() == TypeCheckKind::kUnresolvedCheck)
? obj
: out;
__ movl(target, Address(obj, class_offset));
@@ -4893,7 +4983,7 @@
}
break;
}
-
+ case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
default: {
codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInstanceofNonTrivial),
@@ -4934,6 +5024,7 @@
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
break;
+ case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
call_kind = LocationSummary::kCall;
break;
@@ -5061,6 +5152,7 @@
__ j(kNotEqual, slow_path->GetEntryLabel());
break;
}
+ case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
default:
codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pCheckCast),
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 197ce63..ecc8630 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -70,6 +70,35 @@
DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention);
};
+class FieldAccessCallingConventionX86_64 : public FieldAccessCallingConvention {
+ public:
+ FieldAccessCallingConventionX86_64() {}
+
+ Location GetObjectLocation() const OVERRIDE {
+ return Location::RegisterLocation(RSI);
+ }
+ Location GetFieldIndexLocation() const OVERRIDE {
+ return Location::RegisterLocation(RDI);
+ }
+ Location GetReturnLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+ return Location::RegisterLocation(RAX);
+ }
+ Location GetSetValueLocation(Primitive::Type type, bool is_instance) const OVERRIDE {
+ return Primitive::Is64BitType(type)
+ ? Location::RegisterLocation(RDX)
+ : (is_instance
+ ? Location::RegisterLocation(RDX)
+ : Location::RegisterLocation(RSI));
+ }
+ Location GetFpuLocation(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE {
+ return Location::FpuRegisterLocation(XMM0);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FieldAccessCallingConventionX86_64);
+};
+
+
class InvokeDexCallingConventionVisitorX86_64 : public InvokeDexCallingConventionVisitor {
public:
InvokeDexCallingConventionVisitorX86_64() {}
@@ -215,6 +244,9 @@
void Bind(HBasicBlock* block) OVERRIDE;
void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
void MoveConstant(Location destination, int32_t value) OVERRIDE;
+ void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
+ void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
+
size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 2c6c3b7..d38f4c8 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -398,6 +398,22 @@
StartAttributeStream("intrinsic") << invoke->GetIntrinsic();
}
+ void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* field_access) OVERRIDE {
+ StartAttributeStream("field_type") << field_access->GetFieldType();
+ }
+
+ void VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet* field_access) OVERRIDE {
+ StartAttributeStream("field_type") << field_access->GetFieldType();
+ }
+
+ void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* field_access) OVERRIDE {
+ StartAttributeStream("field_type") << field_access->GetFieldType();
+ }
+
+ void VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet* field_access) OVERRIDE {
+ StartAttributeStream("field_type") << field_access->GetFieldType();
+ }
+
void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE {
StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit");
}
@@ -485,8 +501,11 @@
StartAttributeStream("can_be_null")
<< std::boolalpha << instruction->CanBeNull() << std::noboolalpha;
StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha;
+ } else if (instruction->IsLoadClass()) {
+ StartAttributeStream("klass") << "unresolved";
} else {
- DCHECK(!is_after_pass_) << "Type info should be valid after reference type propagation";
+ DCHECK(!is_after_pass_)
+ << "Expected a valid rti after reference type propagation";
}
}
if (disasm_info_ != nullptr) {
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index 32f45b5..56f2718 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -47,14 +47,16 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache));
+ dex_cache,
+ 0));
block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
Primitive::kPrimNot,
MemberOffset(42),
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache));
+ dex_cache,
+ 0));
HInstruction* to_remove = block->GetLastInstruction();
block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
Primitive::kPrimNot,
@@ -62,7 +64,8 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache));
+ dex_cache,
+ 0));
HInstruction* different_offset = block->GetLastInstruction();
// Kill the value.
block->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
@@ -72,14 +75,16 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache));
+ dex_cache,
+ 0));
block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
Primitive::kPrimNot,
MemberOffset(42),
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache));
+ dex_cache,
+ 0));
HInstruction* use_after_kill = block->GetLastInstruction();
block->AddInstruction(new (&allocator) HExit());
@@ -118,7 +123,8 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache));
+ dex_cache,
+ 0));
block->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
HBasicBlock* then = new (&allocator) HBasicBlock(graph);
@@ -139,7 +145,8 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache));
+ dex_cache,
+ 0));
then->AddInstruction(new (&allocator) HGoto());
else_->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
Primitive::kPrimBoolean,
@@ -147,7 +154,8 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache));
+ dex_cache,
+ 0));
else_->AddInstruction(new (&allocator) HGoto());
join->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
Primitive::kPrimBoolean,
@@ -155,7 +163,8 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache));
+ dex_cache,
+ 0));
join->AddInstruction(new (&allocator) HExit());
graph->TryBuildingSsa();
@@ -191,7 +200,8 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache));
+ dex_cache,
+ 0));
block->AddInstruction(new (&allocator) HGoto());
HBasicBlock* loop_header = new (&allocator) HBasicBlock(graph);
@@ -212,7 +222,8 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache));
+ dex_cache,
+ 0));
HInstruction* field_get_in_loop_header = loop_header->GetLastInstruction();
loop_header->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
@@ -225,7 +236,8 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache));
+ dex_cache,
+ 0));
HInstruction* field_set = loop_body->GetLastInstruction();
loop_body->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
Primitive::kPrimBoolean,
@@ -233,7 +245,8 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache));
+ dex_cache,
+ 0));
HInstruction* field_get_in_loop_body = loop_body->GetLastInstruction();
loop_body->AddInstruction(new (&allocator) HGoto());
@@ -243,7 +256,8 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache));
+ dex_cache,
+ 0));
HInstruction* field_get_in_exit = exit->GetLastInstruction();
exit->AddInstruction(new (&allocator) HExit());
@@ -339,7 +353,8 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache));
+ dex_cache,
+ 0));
SideEffectsAnalysis side_effects(graph);
side_effects.Run();
@@ -362,7 +377,8 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache),
+ dex_cache,
+ 0),
outer_loop_body->GetLastInstruction());
SideEffectsAnalysis side_effects(graph);
@@ -386,7 +402,8 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache),
+ dex_cache,
+ 0),
inner_loop_body->GetLastInstruction());
SideEffectsAnalysis side_effects(graph);
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 039029a..f3b5f08 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -43,6 +43,11 @@
static constexpr size_t kMaximumNumberOfHInstructions = 12;
void HInliner::Run() {
+ const CompilerOptions& compiler_options = compiler_driver_->GetCompilerOptions();
+ if ((compiler_options.GetInlineDepthLimit() == 0)
+ || (compiler_options.GetInlineMaxCodeUnits() == 0)) {
+ return;
+ }
if (graph_->IsDebuggable()) {
// For simplicity, we currently never inline when the graph is debuggable. This avoids
// doing some logic in the runtime to discover if a method could have been inlined.
@@ -247,12 +252,14 @@
return false;
}
- uint16_t class_def_idx = resolved_method->GetDeclaringClass()->GetDexClassDefIndex();
- if (!compiler_driver_->IsMethodVerifiedWithoutFailures(
- resolved_method->GetDexMethodIndex(), class_def_idx, *resolved_method->GetDexFile())) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
- << " couldn't be verified, so it cannot be inlined";
- return false;
+ if (!resolved_method->GetDeclaringClass()->IsVerified()) {
+ uint16_t class_def_idx = resolved_method->GetDeclaringClass()->GetDexClassDefIndex();
+ if (!compiler_driver_->IsMethodVerifiedWithoutFailures(
+ resolved_method->GetDexMethodIndex(), class_def_idx, *resolved_method->GetDexFile())) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
+ << " couldn't be verified, so it cannot be inlined";
+ return false;
+ }
}
if (invoke_instruction->IsInvokeStaticOrDirect() &&
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 3287a0a..86a3ad9 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -216,7 +216,11 @@
}
ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI();
- DCHECK(class_rti.IsValid() && class_rti.IsExact());
+ if (!class_rti.IsValid()) {
+ // Happens when the loaded class is unresolved.
+ return false;
+ }
+ DCHECK(class_rti.IsExact());
if (class_rti.IsSupertypeOf(obj_rti)) {
*outcome = true;
return true;
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 1b4d161..b60905d 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -404,6 +404,29 @@
GpuRegister rhs = locations->InAt(1).AsRegister<GpuRegister>();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
+ // Some architectures, such as ARM and MIPS (prior to r6), have a
+ // conditional move instruction which only changes the target
+ // (output) register if the condition is true (MIPS prior to r6 had
+ // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always
+ // change the target (output) register. If the condition is true the
+ // output register gets the contents of the "rs" register; otherwise,
+ // the output register is set to zero. One consequence of this is
+ // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6
+ // needs to use a pair of SELEQZ/SELNEZ instructions. After
+ // executing this pair of instructions one of the output registers
+ // from the pair will necessarily contain zero. Then the code ORs the
+ // output registers from the SELEQZ/SELNEZ instructions to get the
+ // final result.
+ //
+ // The initial test to see if the output register is same as the
+ // first input register is needed to make sure that value in the
+ // first input register isn't clobbered before we've finished
+ // computing the output value. The logic in the corresponding else
+ // clause performs the same task but makes sure the second input
+ // register isn't clobbered in the event that it's the same register
+ // as the output register; the else clause also handles the case
+ // where the output register is distinct from both the first, and the
+ // second input registers.
if (out == lhs) {
__ Slt(AT, rhs, lhs);
if (is_min) {
@@ -512,13 +535,12 @@
CreateFPToFP(arena_, invoke);
}
-// 0x200 - +zero
-// 0x040 - +infinity
-// 0x020 - -zero
-// 0x004 - -infinity
-// 0x002 - quiet NaN
-// 0x001 - signaling NaN
-const constexpr uint16_t CLASS_MASK = 0x267;
+const constexpr uint16_t kFPLeaveUnchanged = kPositiveZero |
+ kPositiveInfinity |
+ kNegativeZero |
+ kNegativeInfinity |
+ kQuietNaN |
+ kSignalingNaN;
void IntrinsicCodeGeneratorMIPS64::VisitMathFloor(HInvoke* invoke) {
LocationSummary* locations = invoke->GetLocations();
@@ -534,7 +556,7 @@
// }
__ ClassD(out, in);
__ Dmfc1(AT, out);
- __ Andi(AT, AT, CLASS_MASK); // +0.0 | +Inf | -0.0 | -Inf | qNaN | sNaN
+ __ Andi(AT, AT, kFPLeaveUnchanged); // +0.0 | +Inf | -0.0 | -Inf | qNaN | sNaN
__ MovD(out, in);
__ Bnezc(AT, &done);
@@ -583,7 +605,7 @@
// }
__ ClassD(out, in);
__ Dmfc1(AT, out);
- __ Andi(AT, AT, CLASS_MASK); // +0.0 | +Inf | -0.0 | -Inf | qNaN | sNaN
+ __ Andi(AT, AT, kFPLeaveUnchanged); // +0.0 | +Inf | -0.0 | -Inf | qNaN | sNaN
__ MovD(out, in);
__ Bnezc(AT, &done);
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index ec4a9ec..558892d 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -106,11 +106,11 @@
NullHandle<mirror::DexCache> dex_cache;
HInstruction* get_field = new (&allocator_) HInstanceFieldGet(
parameter_, Primitive::kPrimLong, MemberOffset(10),
- false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache);
+ false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache, 0);
loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
HInstruction* set_field = new (&allocator_) HInstanceFieldSet(
parameter_, constant_, Primitive::kPrimInt, MemberOffset(20),
- false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache);
+ false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache, 0);
loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
EXPECT_EQ(get_field->GetBlock(), loop_body_);
@@ -127,11 +127,11 @@
NullHandle<mirror::DexCache> dex_cache;
HInstruction* get_field = new (&allocator_) HInstanceFieldGet(
parameter_, Primitive::kPrimLong, MemberOffset(10),
- false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache);
+ false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache, 0);
loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
HInstruction* set_field = new (&allocator_) HInstanceFieldSet(
parameter_, get_field, Primitive::kPrimLong, MemberOffset(10),
- false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache);
+ false, kUnknownFieldIndex, graph_->GetDexFile(), dex_cache, 0);
loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
EXPECT_EQ(get_field->GetBlock(), loop_body_);
@@ -146,7 +146,7 @@
// Populate the loop with instructions: set/get array with different types.
HInstruction* get_array = new (&allocator_) HArrayGet(
- parameter_, constant_, Primitive::kPrimLong);
+ parameter_, constant_, Primitive::kPrimLong, 0);
loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
HInstruction* set_array = new (&allocator_) HArraySet(
parameter_, constant_, constant_, Primitive::kPrimInt, 0);
@@ -164,7 +164,7 @@
// Populate the loop with instructions: set/get array with same types.
HInstruction* get_array = new (&allocator_) HArrayGet(
- parameter_, constant_, Primitive::kPrimLong);
+ parameter_, constant_, Primitive::kPrimLong, 0);
loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
HInstruction* set_array = new (&allocator_) HArraySet(
parameter_, get_array, constant_, Primitive::kPrimLong, 0);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index d52f592..dbf46ce 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1067,6 +1067,10 @@
M(Shr, BinaryOperation) \
M(StaticFieldGet, Instruction) \
M(StaticFieldSet, Instruction) \
+ M(UnresolvedInstanceFieldGet, Instruction) \
+ M(UnresolvedInstanceFieldSet, Instruction) \
+ M(UnresolvedStaticFieldGet, Instruction) \
+ M(UnresolvedStaticFieldSet, Instruction) \
M(StoreLocal, Instruction) \
M(Sub, BinaryOperation) \
M(SuspendCheck, Instruction) \
@@ -1711,7 +1715,7 @@
class HInstruction : public ArenaObject<kArenaAllocInstruction> {
public:
- HInstruction(SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
+ HInstruction(SideEffects side_effects, uint32_t dex_pc)
: previous_(nullptr),
next_(nullptr),
block_(nullptr),
@@ -2068,7 +2072,7 @@
template<size_t N>
class HTemplateInstruction: public HInstruction {
public:
- HTemplateInstruction<N>(SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
+ HTemplateInstruction<N>(SideEffects side_effects, uint32_t dex_pc)
: HInstruction(side_effects, dex_pc), inputs_() {}
virtual ~HTemplateInstruction() {}
@@ -2095,7 +2099,7 @@
template<>
class HTemplateInstruction<0>: public HInstruction {
public:
- explicit HTemplateInstruction<0>(SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
+ explicit HTemplateInstruction<0>(SideEffects side_effects, uint32_t dex_pc)
: HInstruction(side_effects, dex_pc) {}
virtual ~HTemplateInstruction() {}
@@ -2121,7 +2125,7 @@
template<intptr_t N>
class HExpression : public HTemplateInstruction<N> {
public:
- HExpression<N>(Primitive::Type type, SideEffects side_effects, uint32_t dex_pc = kNoDexPc)
+ HExpression<N>(Primitive::Type type, SideEffects side_effects, uint32_t dex_pc)
: HTemplateInstruction<N>(side_effects, dex_pc), type_(type) {}
virtual ~HExpression() {}
@@ -4206,7 +4210,7 @@
uint32_t field_idx,
const DexFile& dex_file,
Handle<mirror::DexCache> dex_cache,
- uint32_t dex_pc = kNoDexPc)
+ uint32_t dex_pc)
: HExpression(
field_type,
SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
@@ -4252,7 +4256,7 @@
uint32_t field_idx,
const DexFile& dex_file,
Handle<mirror::DexCache> dex_cache,
- uint32_t dex_pc = kNoDexPc)
+ uint32_t dex_pc)
: HTemplateInstruction(
SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
field_info_(field_offset, field_type, is_volatile, field_idx, dex_file, dex_cache),
@@ -4287,7 +4291,7 @@
HArrayGet(HInstruction* array,
HInstruction* index,
Primitive::Type type,
- uint32_t dex_pc = kNoDexPc)
+ uint32_t dex_pc)
: HExpression(type, SideEffects::ArrayReadOfType(type), dex_pc) {
SetRawInputAt(0, array);
SetRawInputAt(1, index);
@@ -4403,7 +4407,7 @@
class HArrayLength : public HExpression<1> {
public:
- explicit HArrayLength(HInstruction* array, uint32_t dex_pc = kNoDexPc)
+ explicit HArrayLength(HInstruction* array, uint32_t dex_pc)
: HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) {
// Note that arrays do not change length, so the instruction does not
// depend on any write.
@@ -4509,12 +4513,14 @@
uint16_t type_index,
const DexFile& dex_file,
bool is_referrers_class,
- uint32_t dex_pc)
+ uint32_t dex_pc,
+ bool needs_access_check)
: HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
type_index_(type_index),
dex_file_(dex_file),
is_referrers_class_(is_referrers_class),
generate_clinit_check_(false),
+ needs_access_check_(needs_access_check),
loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) {
SetRawInputAt(0, current_method);
}
@@ -4534,19 +4540,22 @@
bool NeedsEnvironment() const OVERRIDE {
// Will call runtime and load the class if the class is not loaded yet.
// TODO: finer grain decision.
- return !is_referrers_class_;
+ return !is_referrers_class_ || needs_access_check_;
}
bool MustGenerateClinitCheck() const {
return generate_clinit_check_;
}
-
void SetMustGenerateClinitCheck(bool generate_clinit_check) {
generate_clinit_check_ = generate_clinit_check;
}
bool CanCallRuntime() const {
- return MustGenerateClinitCheck() || !is_referrers_class_;
+ return MustGenerateClinitCheck() || !is_referrers_class_ || needs_access_check_;
+ }
+
+ bool NeedsAccessCheck() const {
+ return needs_access_check_;
}
bool CanThrow() const OVERRIDE {
@@ -4582,6 +4591,7 @@
// Whether this instruction must generate the initialization check.
// Used for code generation.
bool generate_clinit_check_;
+ bool needs_access_check_;
ReferenceTypeInfo loaded_class_rti_;
@@ -4665,7 +4675,7 @@
uint32_t field_idx,
const DexFile& dex_file,
Handle<mirror::DexCache> dex_cache,
- uint32_t dex_pc = kNoDexPc)
+ uint32_t dex_pc)
: HExpression(
field_type,
SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
@@ -4708,7 +4718,7 @@
uint32_t field_idx,
const DexFile& dex_file,
Handle<mirror::DexCache> dex_cache,
- uint32_t dex_pc = kNoDexPc)
+ uint32_t dex_pc)
: HTemplateInstruction(
SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
field_info_(field_offset, field_type, is_volatile, field_idx, dex_file, dex_cache),
@@ -4735,6 +4745,112 @@
DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet);
};
+class HUnresolvedInstanceFieldGet : public HExpression<1> {
+ public:
+ HUnresolvedInstanceFieldGet(HInstruction* obj,
+ Primitive::Type field_type,
+ uint32_t field_index,
+ uint32_t dex_pc)
+ : HExpression(field_type, SideEffects::AllExceptGCDependency(), dex_pc),
+ field_index_(field_index) {
+ SetRawInputAt(0, obj);
+ }
+
+ bool NeedsEnvironment() const OVERRIDE { return true; }
+ bool CanThrow() const OVERRIDE { return true; }
+
+ Primitive::Type GetFieldType() const { return GetType(); }
+ uint32_t GetFieldIndex() const { return field_index_; }
+
+ DECLARE_INSTRUCTION(UnresolvedInstanceFieldGet);
+
+ private:
+ const uint32_t field_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldGet);
+};
+
+class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> {
+ public:
+ HUnresolvedInstanceFieldSet(HInstruction* obj,
+ HInstruction* value,
+ Primitive::Type field_type,
+ uint32_t field_index,
+ uint32_t dex_pc)
+ : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
+ field_type_(field_type),
+ field_index_(field_index) {
+ DCHECK_EQ(field_type, value->GetType());
+ SetRawInputAt(0, obj);
+ SetRawInputAt(1, value);
+ }
+
+ bool NeedsEnvironment() const OVERRIDE { return true; }
+ bool CanThrow() const OVERRIDE { return true; }
+
+ Primitive::Type GetFieldType() const { return field_type_; }
+ uint32_t GetFieldIndex() const { return field_index_; }
+
+ DECLARE_INSTRUCTION(UnresolvedInstanceFieldSet);
+
+ private:
+ const Primitive::Type field_type_;
+ const uint32_t field_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldSet);
+};
+
+class HUnresolvedStaticFieldGet : public HExpression<0> {
+ public:
+ HUnresolvedStaticFieldGet(Primitive::Type field_type,
+ uint32_t field_index,
+ uint32_t dex_pc)
+ : HExpression(field_type, SideEffects::AllExceptGCDependency(), dex_pc),
+ field_index_(field_index) {
+ }
+
+ bool NeedsEnvironment() const OVERRIDE { return true; }
+ bool CanThrow() const OVERRIDE { return true; }
+
+ Primitive::Type GetFieldType() const { return GetType(); }
+ uint32_t GetFieldIndex() const { return field_index_; }
+
+ DECLARE_INSTRUCTION(UnresolvedStaticFieldGet);
+
+ private:
+ const uint32_t field_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldGet);
+};
+
+class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> {
+ public:
+ HUnresolvedStaticFieldSet(HInstruction* value,
+ Primitive::Type field_type,
+ uint32_t field_index,
+ uint32_t dex_pc)
+ : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
+ field_type_(field_type),
+ field_index_(field_index) {
+ DCHECK_EQ(field_type, value->GetType());
+ SetRawInputAt(0, value);
+ }
+
+ bool NeedsEnvironment() const OVERRIDE { return true; }
+ bool CanThrow() const OVERRIDE { return true; }
+
+ Primitive::Type GetFieldType() const { return field_type_; }
+ uint32_t GetFieldIndex() const { return field_index_; }
+
+ DECLARE_INSTRUCTION(UnresolvedStaticFieldSet);
+
+ private:
+ const Primitive::Type field_type_;
+ const uint32_t field_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldSet);
+};
+
// Implement the move-exception DEX instruction.
class HLoadException : public HExpression<0> {
public:
@@ -4787,6 +4903,7 @@
* or `HCheckCast`.
*/
enum class TypeCheckKind {
+ kUnresolvedCheck, // Check against an unresolved type.
kExactCheck, // Can do a single class compare.
kClassHierarchyCheck, // Can just walk the super class chain.
kAbstractClassCheck, // Can just walk the super class chain, starting one up.
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index ddc5730..f7cc872 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -23,7 +23,8 @@
class HX86ComputeBaseMethodAddress : public HExpression<0> {
public:
// Treat the value as an int32_t, but it is really a 32 bit native pointer.
- HX86ComputeBaseMethodAddress() : HExpression(Primitive::kPrimInt, SideEffects::None()) {}
+ HX86ComputeBaseMethodAddress()
+ : HExpression(Primitive::kPrimInt, SideEffects::None(), kNoDexPc) {}
DECLARE_INSTRUCTION(X86ComputeBaseMethodAddress);
@@ -37,7 +38,7 @@
HX86LoadFromConstantTable(HX86ComputeBaseMethodAddress* method_base,
HConstant* constant,
bool needs_materialization = true)
- : HExpression(constant->GetType(), SideEffects::None()),
+ : HExpression(constant->GetType(), SideEffects::None(), kNoDexPc),
needs_materialization_(needs_materialization) {
SetRawInputAt(0, method_base);
SetRawInputAt(1, constant);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index dbfbd96..12d6b03 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -310,9 +310,6 @@
std::unique_ptr<std::ostream> visualizer_output_;
- // Delegate to Quick in case the optimizing compiler cannot compile a method.
- std::unique_ptr<Compiler> delegate_;
-
DISALLOW_COPY_AND_ASSIGN(OptimizingCompiler);
};
@@ -321,11 +318,9 @@
OptimizingCompiler::OptimizingCompiler(CompilerDriver* driver)
: Compiler(driver, kMaximumCompilationTimeBeforeWarning),
run_optimizations_(
- driver->GetCompilerOptions().GetCompilerFilter() != CompilerOptions::kTime),
- delegate_(Create(driver, Compiler::Kind::kQuick)) {}
+ driver->GetCompilerOptions().GetCompilerFilter() != CompilerOptions::kTime) {}
void OptimizingCompiler::Init() {
- delegate_->Init();
// Enable C1visualizer output. Must be done in Init() because the compiler
// driver is not fully initialized when passed to the compiler's constructor.
CompilerDriver* driver = GetCompilerDriver();
@@ -344,7 +339,6 @@
}
void OptimizingCompiler::UnInit() const {
- delegate_->UnInit();
}
OptimizingCompiler::~OptimizingCompiler() {
@@ -353,8 +347,7 @@
}
}
-void OptimizingCompiler::InitCompilationUnit(CompilationUnit& cu) const {
- delegate_->InitCompilationUnit(cu);
+void OptimizingCompiler::InitCompilationUnit(CompilationUnit& cu ATTRIBUTE_UNUSED) const {
}
bool OptimizingCompiler::CanCompileMethod(uint32_t method_idx ATTRIBUTE_UNUSED,
@@ -569,6 +562,9 @@
return linker_patches;
}
+// TODO: The function below uses too much stack space. Bug: 24698147
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph,
CodeGenerator* codegen,
CompilerDriver* compiler_driver,
@@ -618,6 +614,7 @@
soa.Self()->TransitionFromSuspendedToRunnable();
return compiled_method;
}
+#pragma GCC diagnostic pop
CompiledMethod* OptimizingCompiler::CompileBaseline(
CodeGenerator* codegen,
@@ -829,8 +826,12 @@
return compiled_method;
}
-static bool HasOnlyUnresolvedFailures(const VerifiedMethod* verified_method) {
- uint32_t unresolved_mask = verifier::VerifyError::VERIFY_ERROR_NO_CLASS;
+static bool CanHandleVerificationFailure(const VerifiedMethod* verified_method) {
+ // For access errors the compiler will use the unresolved helpers (e.g. HInvokeUnresolved).
+ uint32_t unresolved_mask = verifier::VerifyError::VERIFY_ERROR_NO_CLASS
+ | verifier::VerifyError::VERIFY_ERROR_ACCESS_CLASS
+ | verifier::VerifyError::VERIFY_ERROR_ACCESS_FIELD
+ | verifier::VerifyError::VERIFY_ERROR_ACCESS_METHOD;
return (verified_method->GetEncounteredVerificationFailures() & (~unresolved_mask)) == 0;
}
@@ -847,7 +848,7 @@
const VerifiedMethod* verified_method = compiler_driver->GetVerifiedMethod(&dex_file, method_idx);
DCHECK(!verified_method->HasRuntimeThrow());
if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file)
- || HasOnlyUnresolvedFailures(verified_method)) {
+ || CanHandleVerificationFailure(verified_method)) {
method = TryCompile(code_item, access_flags, invoke_type, class_def_idx,
method_idx, jclass_loader, dex_file, dex_cache);
} else {
@@ -858,15 +859,6 @@
}
}
- if (method != nullptr) {
- return method;
- }
- method = delegate_->Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx,
- jclass_loader, dex_file, dex_cache);
-
- if (method != nullptr) {
- MaybeRecordStat(MethodCompilationStat::kCompiledQuick);
- }
return method;
}
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index c7701b7..6375cf1 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -29,11 +29,12 @@
kAttemptCompilation = 0,
kCompiledBaseline,
kCompiledOptimized,
- kCompiledQuick,
kInlinedInvoke,
kInstructionSimplifications,
kInstructionSimplificationsArch,
kUnresolvedMethod,
+ kUnresolvedField,
+ kUnresolvedFieldNotAFastAccess,
kNotCompiledBranchOutsideMethodCode,
kNotCompiledCannotBuildSSA,
kNotCompiledCantAccesType,
@@ -45,7 +46,6 @@
kNotCompiledPathological,
kNotCompiledSpaceFilter,
kNotCompiledUnhandledInstruction,
- kNotCompiledUnresolvedField,
kNotCompiledUnsupportedIsa,
kNotCompiledVerifyAtRuntime,
kNotOptimizedDisabled,
@@ -73,14 +73,11 @@
compile_stats_[kCompiledBaseline] * 100 / compile_stats_[kAttemptCompilation];
size_t optimized_percent =
compile_stats_[kCompiledOptimized] * 100 / compile_stats_[kAttemptCompilation];
- size_t quick_percent =
- compile_stats_[kCompiledQuick] * 100 / compile_stats_[kAttemptCompilation];
std::ostringstream oss;
oss << "Attempted compilation of " << compile_stats_[kAttemptCompilation] << " methods: ";
oss << unoptimized_percent << "% (" << compile_stats_[kCompiledBaseline] << ") unoptimized, ";
oss << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized, ";
- oss << quick_percent << "% (" << compile_stats_[kCompiledQuick] << ") quick.";
LOG(INFO) << oss.str();
@@ -99,11 +96,12 @@
case kAttemptCompilation : return "kAttemptCompilation";
case kCompiledBaseline : return "kCompiledBaseline";
case kCompiledOptimized : return "kCompiledOptimized";
- case kCompiledQuick : return "kCompiledQuick";
case kInlinedInvoke : return "kInlinedInvoke";
case kInstructionSimplifications: return "kInstructionSimplifications";
case kInstructionSimplificationsArch: return "kInstructionSimplificationsArch";
case kUnresolvedMethod : return "kUnresolvedMethod";
+ case kUnresolvedField : return "kUnresolvedField";
+ case kUnresolvedFieldNotAFastAccess : return "kUnresolvedFieldNotAFastAccess";
case kNotCompiledBranchOutsideMethodCode: return "kNotCompiledBranchOutsideMethodCode";
case kNotCompiledCannotBuildSSA : return "kNotCompiledCannotBuildSSA";
case kNotCompiledCantAccesType : return "kNotCompiledCantAccesType";
@@ -115,7 +113,6 @@
case kNotCompiledPathological : return "kNotCompiledPathological";
case kNotCompiledSpaceFilter : return "kNotCompiledSpaceFilter";
case kNotCompiledUnhandledInstruction : return "kNotCompiledUnhandledInstruction";
- case kNotCompiledUnresolvedField : return "kNotCompiledUnresolvedField";
case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa";
case kNotCompiledVerifyAtRuntime : return "kNotCompiledVerifyAtRuntime";
case kNotOptimizedDisabled : return "kNotOptimizedDisabled";
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index fe837e4..f7a7e42 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -52,6 +52,8 @@
void SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass, bool is_exact);
void VisitInstanceFieldGet(HInstanceFieldGet* instr) OVERRIDE;
void VisitStaticFieldGet(HStaticFieldGet* instr) OVERRIDE;
+ void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instr) OVERRIDE;
+ void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* instr) OVERRIDE;
void VisitInvoke(HInvoke* instr) OVERRIDE;
void VisitArrayGet(HArrayGet* instr) OVERRIDE;
void VisitCheckCast(HCheckCast* instr) OVERRIDE;
@@ -119,8 +121,9 @@
if (instr->IsBoundType()) {
DCHECK(instr->AsBoundType()->GetUpperBound().IsValid());
} else if (instr->IsLoadClass()) {
- DCHECK(instr->AsLoadClass()->GetReferenceTypeInfo().IsExact());
- DCHECK(instr->AsLoadClass()->GetLoadedClassRTI().IsValid());
+ HLoadClass* cls = instr->AsLoadClass();
+ DCHECK(cls->GetReferenceTypeInfo().IsExact());
+ DCHECK(!cls->GetLoadedClassRTI().IsValid() || cls->GetLoadedClassRTI().IsExact());
} else if (instr->IsNullCheck()) {
DCHECK(instr->GetReferenceTypeInfo().IsEqual(instr->InputAt(0)->GetReferenceTypeInfo()))
<< "NullCheck " << instr->GetReferenceTypeInfo()
@@ -166,6 +169,7 @@
SHARED_REQUIRES(Locks::mutator_lock_) {
ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo();
ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+ DCHECK(class_rti.IsValid());
HBoundType* bound_type = new (arena) HBoundType(obj, class_rti, upper_can_be_null);
// Narrow the type as much as possible.
if (class_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes()) {
@@ -314,6 +318,15 @@
return;
}
+ HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass();
+ ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ if (!class_rti.IsValid()) {
+ // He have loaded an unresolved class. Don't bother bounding the type.
+ return;
+ }
+ }
// We only need to bound the type if we have uses in the relevant block.
// So start with null and create the HBoundType lazily, only if it's needed.
HBoundType* bound_type = nullptr;
@@ -334,8 +347,6 @@
if (instanceOfTrueBlock->Dominates(user->GetBlock())) {
if (bound_type == nullptr) {
ScopedObjectAccess soa(Thread::Current());
- HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass();
- ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
HInstruction* insert_point = instanceOfTrueBlock->GetFirstInstruction();
if (ShouldCreateBoundType(insert_point, obj, class_rti, nullptr, instanceOfTrueBlock)) {
bound_type = CreateBoundType(
@@ -450,6 +461,22 @@
UpdateFieldAccessTypeInfo(instr, instr->GetFieldInfo());
}
+void RTPVisitor::VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instr) {
+ // TODO: Use descriptor to get the actual type.
+ if (instr->GetFieldType() == Primitive::kPrimNot) {
+ instr->SetReferenceTypeInfo(
+ ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false));
+ }
+}
+
+void RTPVisitor::VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* instr) {
+ // TODO: Use descriptor to get the actual type.
+ if (instr->GetFieldType() == Primitive::kPrimNot) {
+ instr->SetReferenceTypeInfo(
+ ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false));
+ }
+}
+
void RTPVisitor::VisitLoadClass(HLoadClass* instr) {
ScopedObjectAccess soa(Thread::Current());
mirror::DexCache* dex_cache =
@@ -457,10 +484,10 @@
// Get type from dex cache assuming it was populated by the verifier.
mirror::Class* resolved_class = dex_cache->GetResolvedType(instr->GetTypeIndex());
// TODO: investigating why we are still getting unresolved classes: b/22821472.
- ReferenceTypeInfo::TypeHandle handle = (resolved_class != nullptr)
- ? handles_->NewHandle(resolved_class)
- : object_class_handle_;
- instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(handle, /* is_exact */ true));
+ if (resolved_class != nullptr) {
+ instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(
+ handles_->NewHandle(resolved_class), /* is_exact */ true));
+ }
instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(class_class_handle_, /* is_exact */ true));
}
@@ -499,6 +526,15 @@
}
void RTPVisitor::VisitCheckCast(HCheckCast* check_cast) {
+ HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
+ ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ if (!class_rti.IsValid()) {
+ // He have loaded an unresolved class. Don't bother bounding the type.
+ return;
+ }
+ }
HInstruction* obj = check_cast->InputAt(0);
HBoundType* bound_type = nullptr;
for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
@@ -506,8 +542,6 @@
if (check_cast->StrictlyDominates(user)) {
if (bound_type == nullptr) {
ScopedObjectAccess soa(Thread::Current());
- HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
- ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
if (ShouldCreateBoundType(check_cast->GetNext(), obj, class_rti, check_cast, nullptr)) {
bound_type = CreateBoundType(
GetGraph()->GetArena(),
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 2bb5a8b..21b36cb 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -488,7 +488,8 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache);
+ dex_cache,
+ 0);
block->AddInstruction(test);
block->AddInstruction(new (allocator) HIf(test));
HBasicBlock* then = new (allocator) HBasicBlock(graph);
@@ -513,14 +514,16 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache);
+ dex_cache,
+ 0);
*input2 = new (allocator) HInstanceFieldGet(parameter,
Primitive::kPrimInt,
MemberOffset(42),
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache);
+ dex_cache,
+ 0);
then->AddInstruction(*input1);
else_->AddInstruction(*input2);
join->AddInstruction(new (allocator) HExit());
@@ -634,7 +637,8 @@
false,
kUnknownFieldIndex,
graph->GetDexFile(),
- dex_cache);
+ dex_cache,
+ 0);
block->AddInstruction(*field);
*ret = new (allocator) HReturn(*field);
block->AddInstruction(*ret);
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index f27cecc..a095809 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -24,6 +24,7 @@
uint32_t num_dex_registers,
uint8_t inlining_depth) {
DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry";
+ DCHECK_NE(dex_pc, static_cast<uint32_t>(-1)) << "invalid dex_pc";
current_entry_.dex_pc = dex_pc;
current_entry_.native_pc_offset = native_pc_offset;
current_entry_.register_mask = register_mask;
diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc
index 2a0912e..4380596 100644
--- a/compiler/utils/arm/assembler_arm32_test.cc
+++ b/compiler/utils/arm/assembler_arm32_test.cc
@@ -287,7 +287,7 @@
case 1:
return Base::REG2_TOKEN;
case 2:
- return REG3_TOKEN;
+ return Base::REG3_TOKEN;
case 3:
return REG4_TOKEN;
default:
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index 017402d..bd994f4 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -92,6 +92,17 @@
fmt);
}
+ std::string RepeatRRR(void (Ass::*f)(Reg, Reg, Reg), std::string fmt) {
+ return RepeatTemplatedRegisters<Reg, Reg, Reg>(f,
+ GetRegisters(),
+ GetRegisters(),
+ GetRegisters(),
+ &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+ &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+ &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+ fmt);
+ }
+
std::string Repeatrb(void (Ass::*f)(Reg, Reg), std::string fmt) {
return RepeatTemplatedRegisters<Reg, Reg>(f,
GetRegisters(),
@@ -118,6 +129,66 @@
return RepeatRegisterImm<RegisterView::kUseSecondaryName>(f, imm_bytes, fmt);
}
+ template <typename Reg1Type, typename Reg2Type, typename ImmType,
+ RegisterView Reg1View, RegisterView Reg2View>
+ std::string RepeatRegRegImmBits(void (Ass::*f)(Reg1Type, Reg2Type, ImmType),
+ int imm_bits,
+ std::string fmt) {
+ const std::vector<Reg1Type*> reg1_registers = GetRegisters();
+ const std::vector<Reg2Type*> reg2_registers = GetRegisters();
+ std::string str;
+ std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), imm_bits > 0);
+
+ for (auto reg1 : reg1_registers) {
+ for (auto reg2 : reg2_registers) {
+ for (int64_t imm : imms) {
+ ImmType new_imm = CreateImmediate(imm);
+ (assembler_.get()->*f)(*reg1, *reg2, new_imm);
+ std::string base = fmt;
+
+ std::string reg1_string = GetRegName<Reg1View>(*reg1);
+ size_t reg1_index;
+ while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
+ base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
+ }
+
+ std::string reg2_string = GetRegName<Reg2View>(*reg2);
+ size_t reg2_index;
+ while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
+ base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
+ }
+
+ size_t imm_index = base.find(IMM_TOKEN);
+ if (imm_index != std::string::npos) {
+ std::ostringstream sreg;
+ sreg << imm;
+ std::string imm_string = sreg.str();
+ base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
+ }
+
+ if (str.size() > 0) {
+ str += "\n";
+ }
+ str += base;
+ }
+ }
+ }
+ // Add a newline at the end.
+ str += "\n";
+ return str;
+ }
+
+ template <typename Reg1Type, typename Reg2Type, typename ImmType>
+ std::string RepeatRRIb(void (Ass::*f)(Reg1Type, Reg2Type, ImmType),
+ int imm_bits,
+ std::string fmt) {
+ return RepeatRegRegImmBits<Reg1Type,
+ Reg2Type,
+ ImmType,
+ RegisterView::kUsePrimaryName,
+ RegisterView::kUsePrimaryName>(f, imm_bits, fmt);
+ }
+
std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), std::string fmt) {
return RepeatTemplatedRegisters<FPReg, FPReg>(f,
GetFPRegisters(),
@@ -127,14 +198,27 @@
fmt);
}
- std::string RepeatFFI(void (Ass::*f)(FPReg, FPReg, const Imm&), size_t imm_bytes, std::string fmt) {
+ std::string RepeatFFF(void (Ass::*f)(FPReg, FPReg, FPReg), std::string fmt) {
+ return RepeatTemplatedRegisters<FPReg, FPReg, FPReg>(f,
+ GetFPRegisters(),
+ GetFPRegisters(),
+ GetFPRegisters(),
+ &AssemblerTest::GetFPRegName,
+ &AssemblerTest::GetFPRegName,
+ &AssemblerTest::GetFPRegName,
+ fmt);
+ }
+
+ std::string RepeatFFI(void (Ass::*f)(FPReg, FPReg, const Imm&),
+ size_t imm_bytes,
+ std::string fmt) {
return RepeatTemplatedRegistersImm<FPReg, FPReg>(f,
- GetFPRegisters(),
- GetFPRegisters(),
- &AssemblerTest::GetFPRegName,
- &AssemblerTest::GetFPRegName,
- imm_bytes,
- fmt);
+ GetFPRegisters(),
+ GetFPRegisters(),
+ &AssemblerTest::GetFPRegName,
+ &AssemblerTest::GetFPRegName,
+ imm_bytes,
+ fmt);
}
std::string RepeatFR(void (Ass::*f)(FPReg, Reg), std::string fmt) {
@@ -339,6 +423,63 @@
return res;
}
+ const int kMaxBitsExhaustiveTest = 8;
+
+ // Create a couple of immediate values up to the number of bits given.
+ virtual std::vector<int64_t> CreateImmediateValuesBits(const int imm_bits, bool as_uint = false) {
+ CHECK_GT(imm_bits, 0);
+ CHECK_LE(imm_bits, 64);
+ std::vector<int64_t> res;
+
+ if (imm_bits <= kMaxBitsExhaustiveTest) {
+ if (as_uint) {
+ for (uint64_t i = MinInt<uint64_t>(imm_bits); i <= MaxInt<uint64_t>(imm_bits); i++) {
+ res.push_back(static_cast<int64_t>(i));
+ }
+ } else {
+ for (int64_t i = MinInt<int64_t>(imm_bits); i <= MaxInt<int64_t>(imm_bits); i++) {
+ res.push_back(i);
+ }
+ }
+ } else {
+ if (as_uint) {
+ for (uint64_t i = MinInt<uint64_t>(kMaxBitsExhaustiveTest);
+ i <= MaxInt<uint64_t>(kMaxBitsExhaustiveTest);
+ i++) {
+ res.push_back(static_cast<int64_t>(i));
+ }
+ for (int i = 0; i <= imm_bits; i++) {
+ uint64_t j = (MaxInt<uint64_t>(kMaxBitsExhaustiveTest) + 1) +
+ ((MaxInt<uint64_t>(imm_bits) -
+ (MaxInt<uint64_t>(kMaxBitsExhaustiveTest) + 1))
+ * i / imm_bits);
+ res.push_back(static_cast<int64_t>(j));
+ }
+ } else {
+ for (int i = 0; i <= imm_bits; i++) {
+ int64_t j = MinInt<int64_t>(imm_bits) +
+ ((((MinInt<int64_t>(kMaxBitsExhaustiveTest) - 1) -
+ MinInt<int64_t>(imm_bits))
+ * i) / imm_bits);
+ res.push_back(static_cast<int64_t>(j));
+ }
+ for (int64_t i = MinInt<int64_t>(kMaxBitsExhaustiveTest);
+ i <= MaxInt<int64_t>(kMaxBitsExhaustiveTest);
+ i++) {
+ res.push_back(static_cast<int64_t>(i));
+ }
+ for (int i = 0; i <= imm_bits; i++) {
+ int64_t j = (MaxInt<int64_t>(kMaxBitsExhaustiveTest) + 1) +
+ ((MaxInt<int64_t>(imm_bits) - (MaxInt<int64_t>(kMaxBitsExhaustiveTest) + 1))
+ * i / imm_bits);
+ res.push_back(static_cast<int64_t>(j));
+ }
+ }
+ }
+
+ return res;
+ }
+
// Create an immediate from the specific value.
virtual Imm CreateImmediate(int64_t imm_value) = 0;
@@ -406,6 +547,52 @@
return str;
}
+ template <typename Reg1, typename Reg2, typename Reg3>
+ std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2, Reg3),
+ const std::vector<Reg1*> reg1_registers,
+ const std::vector<Reg2*> reg2_registers,
+ const std::vector<Reg3*> reg3_registers,
+ std::string (AssemblerTest::*GetName1)(const Reg1&),
+ std::string (AssemblerTest::*GetName2)(const Reg2&),
+ std::string (AssemblerTest::*GetName3)(const Reg3&),
+ std::string fmt) {
+ std::string str;
+ for (auto reg1 : reg1_registers) {
+ for (auto reg2 : reg2_registers) {
+ for (auto reg3 : reg3_registers) {
+ (assembler_.get()->*f)(*reg1, *reg2, *reg3);
+ std::string base = fmt;
+
+ std::string reg1_string = (this->*GetName1)(*reg1);
+ size_t reg1_index;
+ while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
+ base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
+ }
+
+ std::string reg2_string = (this->*GetName2)(*reg2);
+ size_t reg2_index;
+ while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
+ base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
+ }
+
+ std::string reg3_string = (this->*GetName3)(*reg3);
+ size_t reg3_index;
+ while ((reg3_index = base.find(REG3_TOKEN)) != std::string::npos) {
+ base.replace(reg3_index, ConstexprStrLen(REG3_TOKEN), reg3_string);
+ }
+
+ if (str.size() > 0) {
+ str += "\n";
+ }
+ str += base;
+ }
+ }
+ }
+ // Add a newline at the end.
+ str += "\n";
+ return str;
+ }
+
template <typename Reg1, typename Reg2>
std::string RepeatTemplatedRegistersImm(void (Ass::*f)(Reg1, Reg2, const Imm&),
const std::vector<Reg1*> reg1_registers,
@@ -500,6 +687,7 @@
static constexpr const char* REG_TOKEN = "{reg}";
static constexpr const char* REG1_TOKEN = "{reg1}";
static constexpr const char* REG2_TOKEN = "{reg2}";
+ static constexpr const char* REG3_TOKEN = "{reg3}";
static constexpr const char* IMM_TOKEN = "{imm}";
private:
diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h
index c8b3fe5..43c9d94 100644
--- a/compiler/utils/assembler_test_base.h
+++ b/compiler/utils/assembler_test_base.h
@@ -67,12 +67,20 @@
// This is intended to be run as a test.
bool CheckTools() {
- if (!FileExists(FindTool(assembler_cmd_name_))) {
+ std::string asm_tool = FindTool(assembler_cmd_name_);
+ if (!FileExists(asm_tool)) {
+ LOG(ERROR) << "Could not find assembler from " << assembler_cmd_name_;
+ LOG(ERROR) << "FindTool returned " << asm_tool;
+ FindToolDump(assembler_cmd_name_);
return false;
}
LOG(INFO) << "Chosen assembler command: " << GetAssemblerCommand();
- if (!FileExists(FindTool(objdump_cmd_name_))) {
+ std::string objdump_tool = FindTool(objdump_cmd_name_);
+ if (!FileExists(objdump_tool)) {
+ LOG(ERROR) << "Could not find objdump from " << objdump_cmd_name_;
+ LOG(ERROR) << "FindTool returned " << objdump_tool;
+ FindToolDump(objdump_cmd_name_);
return false;
}
LOG(INFO) << "Chosen objdump command: " << GetObjdumpCommand();
@@ -80,7 +88,11 @@
// Disassembly is optional.
std::string disassembler = GetDisassembleCommand();
if (disassembler.length() != 0) {
- if (!FileExists(FindTool(disassembler_cmd_name_))) {
+ std::string disassembler_tool = FindTool(disassembler_cmd_name_);
+ if (!FileExists(disassembler_tool)) {
+ LOG(ERROR) << "Could not find disassembler from " << disassembler_cmd_name_;
+ LOG(ERROR) << "FindTool returned " << disassembler_tool;
+ FindToolDump(disassembler_cmd_name_);
return false;
}
LOG(INFO) << "Chosen disassemble command: " << GetDisassembleCommand();
@@ -493,7 +505,7 @@
std::string error_msg;
if (!Exec(args, &error_msg)) {
EXPECT_TRUE(false) << error_msg;
- return "";
+ UNREACHABLE();
}
std::ifstream in(tmp_file.c_str());
@@ -508,6 +520,39 @@
return line;
}
+ // For debug purposes.
+ void FindToolDump(std::string tool_name) {
+ // Find the current tool. Wild-card pattern is "arch-string*tool-name".
+ std::string gcc_path = GetRootPath() + GetGCCRootPath();
+ std::vector<std::string> args;
+ args.push_back("find");
+ args.push_back(gcc_path);
+ args.push_back("-name");
+ args.push_back(architecture_string_ + "*" + tool_name);
+ args.push_back("|");
+ args.push_back("sort");
+ std::string tmp_file = GetTmpnam();
+ args.push_back(">");
+ args.push_back(tmp_file);
+ std::string sh_args = Join(args, ' ');
+
+ args.clear();
+ args.push_back("/bin/sh");
+ args.push_back("-c");
+ args.push_back(sh_args);
+
+ std::string error_msg;
+ if (!Exec(args, &error_msg)) {
+ EXPECT_TRUE(false) << error_msg;
+ UNREACHABLE();
+ }
+
+ std::ifstream in(tmp_file.c_str());
+ if (in) {
+ LOG(ERROR) << in.rdbuf();
+ }
+ }
+
// Use a consistent tmpnam, so store it.
std::string GetTmpnam() {
if (tmpnam_.length() == 0) {
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
index c170313..d083eb4 100644
--- a/compiler/utils/mips64/assembler_mips64.h
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -46,6 +46,20 @@
kStoreDoubleword
};
+// Used to test the values returned by ClassS/ClassD.
+enum FPClassMaskType {
+ kSignalingNaN = 0x001,
+ kQuietNaN = 0x002,
+ kNegativeInfinity = 0x004,
+ kNegativeNormal = 0x008,
+ kNegativeSubnormal = 0x010,
+ kNegativeZero = 0x020,
+ kPositiveInfinity = 0x040,
+ kPositiveNormal = 0x080,
+ kPositiveSubnormal = 0x100,
+ kPositiveZero = 0x200,
+};
+
class Mips64Assembler FINAL : public Assembler {
public:
Mips64Assembler() {}
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
new file mode 100644
index 0000000..2071aca
--- /dev/null
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2015 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 "assembler_mips64.h"
+
+#include <inttypes.h>
+#include <map>
+#include <random>
+
+#include "base/bit_utils.h"
+#include "base/stl_util.h"
+#include "utils/assembler_test.h"
+
+namespace art {
+
+struct MIPS64CpuRegisterCompare {
+ bool operator()(const mips64::GpuRegister& a, const mips64::GpuRegister& b) const {
+ return a < b;
+ }
+};
+
+class AssemblerMIPS64Test : public AssemblerTest<mips64::Mips64Assembler,
+ mips64::GpuRegister,
+ mips64::FpuRegister,
+ uint32_t> {
+ public:
+ typedef AssemblerTest<mips64::Mips64Assembler,
+ mips64::GpuRegister,
+ mips64::FpuRegister,
+ uint32_t> Base;
+
+ protected:
+ // Get the typically used name for this architecture, e.g., aarch64, x86-64, ...
+ std::string GetArchitectureString() OVERRIDE {
+ return "mips64";
+ }
+
+ std::string GetAssemblerParameters() OVERRIDE {
+ return " --no-warn -march=mips64r6";
+ }
+
+ std::string GetDisassembleParameters() OVERRIDE {
+ return " -D -bbinary -mmips:isa64r6";
+ }
+
+ void SetUpHelpers() OVERRIDE {
+ if (registers_.size() == 0) {
+ registers_.push_back(new mips64::GpuRegister(mips64::ZERO));
+ registers_.push_back(new mips64::GpuRegister(mips64::AT));
+ registers_.push_back(new mips64::GpuRegister(mips64::V0));
+ registers_.push_back(new mips64::GpuRegister(mips64::V1));
+ registers_.push_back(new mips64::GpuRegister(mips64::A0));
+ registers_.push_back(new mips64::GpuRegister(mips64::A1));
+ registers_.push_back(new mips64::GpuRegister(mips64::A2));
+ registers_.push_back(new mips64::GpuRegister(mips64::A3));
+ registers_.push_back(new mips64::GpuRegister(mips64::A4));
+ registers_.push_back(new mips64::GpuRegister(mips64::A5));
+ registers_.push_back(new mips64::GpuRegister(mips64::A6));
+ registers_.push_back(new mips64::GpuRegister(mips64::A7));
+ registers_.push_back(new mips64::GpuRegister(mips64::T0));
+ registers_.push_back(new mips64::GpuRegister(mips64::T1));
+ registers_.push_back(new mips64::GpuRegister(mips64::T2));
+ registers_.push_back(new mips64::GpuRegister(mips64::T3));
+ registers_.push_back(new mips64::GpuRegister(mips64::S0));
+ registers_.push_back(new mips64::GpuRegister(mips64::S1));
+ registers_.push_back(new mips64::GpuRegister(mips64::S2));
+ registers_.push_back(new mips64::GpuRegister(mips64::S3));
+ registers_.push_back(new mips64::GpuRegister(mips64::S4));
+ registers_.push_back(new mips64::GpuRegister(mips64::S5));
+ registers_.push_back(new mips64::GpuRegister(mips64::S6));
+ registers_.push_back(new mips64::GpuRegister(mips64::S7));
+ registers_.push_back(new mips64::GpuRegister(mips64::T8));
+ registers_.push_back(new mips64::GpuRegister(mips64::T9));
+ registers_.push_back(new mips64::GpuRegister(mips64::K0));
+ registers_.push_back(new mips64::GpuRegister(mips64::K1));
+ registers_.push_back(new mips64::GpuRegister(mips64::GP));
+ registers_.push_back(new mips64::GpuRegister(mips64::SP));
+ registers_.push_back(new mips64::GpuRegister(mips64::S8));
+ registers_.push_back(new mips64::GpuRegister(mips64::RA));
+
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::ZERO), "zero");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::AT), "at");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::V0), "v0");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::V1), "v1");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::A0), "a0");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::A1), "a1");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::A2), "a2");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::A3), "a3");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::A4), "a4");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::A5), "a5");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::A6), "a6");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::A7), "a7");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::T0), "t0");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::T1), "t1");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::T2), "t2");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::T3), "t3");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::S0), "s0");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::S1), "s1");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::S2), "s2");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::S3), "s3");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::S4), "s4");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::S5), "s5");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::S6), "s6");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::S7), "s7");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::T8), "t8");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::T9), "t9");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::K0), "k0");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::K1), "k1");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::GP), "gp");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::SP), "sp");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::S8), "s8");
+ secondary_register_names_.emplace(mips64::GpuRegister(mips64::RA), "ra");
+
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F0));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F1));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F2));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F3));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F4));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F5));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F6));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F7));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F8));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F9));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F10));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F11));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F12));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F13));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F14));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F15));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F16));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F17));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F18));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F19));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F20));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F21));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F22));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F23));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F24));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F25));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F26));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F27));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F28));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F29));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F30));
+ fp_registers_.push_back(new mips64::FpuRegister(mips64::F31));
+ }
+ }
+
+ void TearDown() OVERRIDE {
+ AssemblerTest::TearDown();
+ STLDeleteElements(®isters_);
+ STLDeleteElements(&fp_registers_);
+ }
+
+ std::vector<mips64::GpuRegister*> GetRegisters() OVERRIDE {
+ return registers_;
+ }
+
+ std::vector<mips64::FpuRegister*> GetFPRegisters() OVERRIDE {
+ return fp_registers_;
+ }
+
+ uint32_t CreateImmediate(int64_t imm_value) OVERRIDE {
+ return imm_value;
+ }
+
+ std::string GetSecondaryRegisterName(const mips64::GpuRegister& reg) OVERRIDE {
+ CHECK(secondary_register_names_.find(reg) != secondary_register_names_.end());
+ return secondary_register_names_[reg];
+ }
+
+ private:
+ std::vector<mips64::GpuRegister*> registers_;
+ std::map<mips64::GpuRegister, std::string, MIPS64CpuRegisterCompare> secondary_register_names_;
+
+ std::vector<mips64::FpuRegister*> fp_registers_;
+};
+
+
+TEST_F(AssemblerMIPS64Test, Toolchain) {
+ EXPECT_TRUE(CheckTools());
+}
+
+
+///////////////////
+// FP Operations //
+///////////////////
+
+TEST_F(AssemblerMIPS64Test, SqrtS) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::SqrtS, "sqrt.s ${reg1}, ${reg2}"), "sqrt.s");
+}
+
+TEST_F(AssemblerMIPS64Test, SqrtD) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::SqrtD, "sqrt.d ${reg1}, ${reg2}"), "sqrt.d");
+}
+
+TEST_F(AssemblerMIPS64Test, AbsS) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::AbsS, "abs.s ${reg1}, ${reg2}"), "abs.s");
+}
+
+TEST_F(AssemblerMIPS64Test, AbsD) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::AbsD, "abs.d ${reg1}, ${reg2}"), "abs.d");
+}
+
+TEST_F(AssemblerMIPS64Test, RoundLS) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::RoundLS, "round.l.s ${reg1}, ${reg2}"), "round.l.s");
+}
+
+TEST_F(AssemblerMIPS64Test, RoundLD) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::RoundLD, "round.l.d ${reg1}, ${reg2}"), "round.l.d");
+}
+
+TEST_F(AssemblerMIPS64Test, RoundWS) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::RoundWS, "round.w.s ${reg1}, ${reg2}"), "round.w.s");
+}
+
+TEST_F(AssemblerMIPS64Test, RoundWD) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::RoundWD, "round.w.d ${reg1}, ${reg2}"), "round.w.d");
+}
+
+TEST_F(AssemblerMIPS64Test, CeilLS) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::CeilLS, "ceil.l.s ${reg1}, ${reg2}"), "ceil.l.s");
+}
+
+TEST_F(AssemblerMIPS64Test, CeilLD) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::CeilLD, "ceil.l.d ${reg1}, ${reg2}"), "ceil.l.d");
+}
+
+TEST_F(AssemblerMIPS64Test, CeilWS) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::CeilWS, "ceil.w.s ${reg1}, ${reg2}"), "ceil.w.s");
+}
+
+TEST_F(AssemblerMIPS64Test, CeilWD) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::CeilWD, "ceil.w.d ${reg1}, ${reg2}"), "ceil.w.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FloorLS) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::FloorLS, "floor.l.s ${reg1}, ${reg2}"), "floor.l.s");
+}
+
+TEST_F(AssemblerMIPS64Test, FloorLD) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::FloorLD, "floor.l.d ${reg1}, ${reg2}"), "floor.l.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FloorWS) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::FloorWS, "floor.w.s ${reg1}, ${reg2}"), "floor.w.s");
+}
+
+TEST_F(AssemblerMIPS64Test, FloorWD) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::FloorWD, "floor.w.d ${reg1}, ${reg2}"), "floor.w.d");
+}
+
+TEST_F(AssemblerMIPS64Test, SelS) {
+ DriverStr(RepeatFFF(&mips64::Mips64Assembler::SelS, "sel.s ${reg1}, ${reg2}, ${reg3}"), "sel.s");
+}
+
+TEST_F(AssemblerMIPS64Test, SelD) {
+ DriverStr(RepeatFFF(&mips64::Mips64Assembler::SelD, "sel.d ${reg1}, ${reg2}, ${reg3}"), "sel.d");
+}
+
+TEST_F(AssemblerMIPS64Test, RintS) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::RintS, "rint.s ${reg1}, ${reg2}"), "rint.s");
+}
+
+TEST_F(AssemblerMIPS64Test, RintD) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::RintD, "rint.d ${reg1}, ${reg2}"), "rint.d");
+}
+
+TEST_F(AssemblerMIPS64Test, ClassS) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::ClassS, "class.s ${reg1}, ${reg2}"), "class.s");
+}
+
+TEST_F(AssemblerMIPS64Test, ClassD) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::ClassD, "class.d ${reg1}, ${reg2}"), "class.d");
+}
+
+TEST_F(AssemblerMIPS64Test, MinS) {
+ DriverStr(RepeatFFF(&mips64::Mips64Assembler::MinS, "min.s ${reg1}, ${reg2}, ${reg3}"), "min.s");
+}
+
+TEST_F(AssemblerMIPS64Test, MinD) {
+ DriverStr(RepeatFFF(&mips64::Mips64Assembler::MinD, "min.d ${reg1}, ${reg2}, ${reg3}"), "min.d");
+}
+
+TEST_F(AssemblerMIPS64Test, MaxS) {
+ DriverStr(RepeatFFF(&mips64::Mips64Assembler::MaxS, "max.s ${reg1}, ${reg2}, ${reg3}"), "max.s");
+}
+
+TEST_F(AssemblerMIPS64Test, MaxD) {
+ DriverStr(RepeatFFF(&mips64::Mips64Assembler::MaxD, "max.d ${reg1}, ${reg2}, ${reg3}"), "max.d");
+}
+
+TEST_F(AssemblerMIPS64Test, CvtDL) {
+ DriverStr(RepeatFF(&mips64::Mips64Assembler::Cvtdl, "cvt.d.l ${reg1}, ${reg2}"), "cvt.d.l");
+}
+
+//////////
+// MISC //
+//////////
+
+TEST_F(AssemblerMIPS64Test, Bitswap) {
+ DriverStr(RepeatRR(&mips64::Mips64Assembler::Bitswap, "bitswap ${reg1}, ${reg2}"), "bitswap");
+}
+
+TEST_F(AssemblerMIPS64Test, Dbitswap) {
+ DriverStr(RepeatRR(&mips64::Mips64Assembler::Dbitswap, "dbitswap ${reg1}, ${reg2}"), "dbitswap");
+}
+
+TEST_F(AssemblerMIPS64Test, Dsbh) {
+ DriverStr(RepeatRR(&mips64::Mips64Assembler::Dsbh, "dsbh ${reg1}, ${reg2}"), "dsbh");
+}
+
+TEST_F(AssemblerMIPS64Test, Dshd) {
+ DriverStr(RepeatRR(&mips64::Mips64Assembler::Dshd, "dshd ${reg1}, ${reg2}"), "dshd");
+}
+
+TEST_F(AssemblerMIPS64Test, Wsbh) {
+ DriverStr(RepeatRR(&mips64::Mips64Assembler::Wsbh, "wsbh ${reg1}, ${reg2}"), "wsbh");
+}
+
+TEST_F(AssemblerMIPS64Test, Sc) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sc, -9, "sc ${reg1}, {imm}(${reg2})"), "sc");
+}
+
+TEST_F(AssemblerMIPS64Test, Scd) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Scd, -9, "scd ${reg1}, {imm}(${reg2})"), "scd");
+}
+
+TEST_F(AssemblerMIPS64Test, Ll) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Ll, -9, "ll ${reg1}, {imm}(${reg2})"), "ll");
+}
+
+TEST_F(AssemblerMIPS64Test, Lld) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Lld, -9, "lld ${reg1}, {imm}(${reg2})"), "lld");
+}
+
+TEST_F(AssemblerMIPS64Test, Rotr) {
+ DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Rotr, 5, "rotr ${reg1}, ${reg2}, {imm}"), "rotr");
+}
+
+TEST_F(AssemblerMIPS64Test, Seleqz) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Seleqz, "seleqz ${reg1}, ${reg2}, ${reg3}"),
+ "seleqz");
+}
+
+TEST_F(AssemblerMIPS64Test, Selnez) {
+ DriverStr(RepeatRRR(&mips64::Mips64Assembler::Selnez, "selnez ${reg1}, ${reg2}, ${reg3}"),
+ "selnez");
+}
+
+TEST_F(AssemblerMIPS64Test, Clz) {
+ DriverStr(RepeatRR(&mips64::Mips64Assembler::Clz, "clz ${reg1}, ${reg2}"), "clz");
+}
+
+TEST_F(AssemblerMIPS64Test, Clo) {
+ DriverStr(RepeatRR(&mips64::Mips64Assembler::Clo, "clo ${reg1}, ${reg2}"), "clo");
+}
+
+TEST_F(AssemblerMIPS64Test, Dclz) {
+ DriverStr(RepeatRR(&mips64::Mips64Assembler::Dclz, "dclz ${reg1}, ${reg2}"), "dclz");
+}
+
+TEST_F(AssemblerMIPS64Test, Dclo) {
+ DriverStr(RepeatRR(&mips64::Mips64Assembler::Dclo, "dclo ${reg1}, ${reg2}"), "dclo");
+}
+
+} // namespace art
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index cc32da1..680e2d7 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -235,11 +235,7 @@
UsageError(" --compiler-backend=(Quick|Optimizing): select compiler backend");
UsageError(" set.");
UsageError(" Example: --compiler-backend=Optimizing");
- if (kUseOptimizingCompiler) {
- UsageError(" Default: Optimizing");
- } else {
- UsageError(" Default: Quick");
- }
+ UsageError(" Default: Optimizing");
UsageError("");
UsageError(" --compiler-filter="
"(verify-none"
@@ -503,7 +499,7 @@
class Dex2Oat FINAL {
public:
explicit Dex2Oat(TimingLogger* timings) :
- compiler_kind_(kUseOptimizingCompiler ? Compiler::kOptimizing : Compiler::kQuick),
+ compiler_kind_(Compiler::kOptimizing),
instruction_set_(kRuntimeISA),
// Take the default set of instruction features from the build.
verification_results_(nullptr),
@@ -752,10 +748,9 @@
void ProcessOptions(ParserOptions* parser_options) {
image_ = (!image_filename_.empty());
- if (!parser_options->requested_specific_compiler && !kUseOptimizingCompiler) {
- // If no specific compiler is requested, the current behavior is
- // to compile the boot image with Quick, and the rest with Optimizing.
- compiler_kind_ = image_ ? Compiler::kQuick : Compiler::kOptimizing;
+ if (image_) {
+ // We need the boot image to always be debuggable.
+ parser_options->debuggable = true;
}
if (oat_filename_.empty() && oat_fd_ == -1) {
diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc
index 1ac7930..82bc8b9 100644
--- a/imgdiag/imgdiag_test.cc
+++ b/imgdiag/imgdiag_test.cc
@@ -109,11 +109,12 @@
std::string boot_image_location_;
};
-#if defined (ART_TARGET)
+#if defined (ART_TARGET) && !defined(__mips__)
TEST_F(ImgDiagTest, ImageDiffPidSelf) {
#else
// Can't run this test on the host, it will fail when trying to open /proc/kpagestats
// because it's root read-only.
+// Also test fails on mips. b/24596015.
TEST_F(ImgDiagTest, DISABLED_ImageDiffPidSelf) {
#endif
// Invoke 'img_diag' against the current process.
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 059c4cd..8d81f2a 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -67,6 +67,7 @@
gc/heap.cc \
gc/reference_processor.cc \
gc/reference_queue.cc \
+ gc/scoped_gc_critical_section.cc \
gc/space/bump_pointer_space.cc \
gc/space/dlmalloc_space.cc \
gc/space/image_space.cc \
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index dc1cf8a..d09631b 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -839,13 +839,12 @@
TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
/*
* Called by managed code to resolve a static field and store a 64-bit primitive value.
- * On entry r0 holds field index, r1:r2 hold new_val
+ * On entry r0 holds field index, r2:r3 hold new_val
*/
.extern artSet64StaticFromCode
ENTRY art_quick_set64_static
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC
- mov r3, r2 @ pass one half of wide argument
- mov r2, r1 @ pass other half of wide argument
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r12 @ save callee saves in case of GC
+ @ r2:r3 contain the wide argument
ldr r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer
str r9, [sp, #-16]! @ expand the frame and pass Thread::Current
.cfi_adjust_cfa_offset 16
@@ -870,6 +869,7 @@
.extern artSet64InstanceFromCode
ENTRY art_quick_set64_instance
SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r12, lr @ save callee saves in case of GC
+ @ r2:r3 contain the wide argument
ldr r12, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer
str r9, [sp, #-12]! @ expand the frame and pass Thread::Current
.cfi_adjust_cfa_offset 12
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index 1f2ce02..395cee8 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -30,7 +30,7 @@
// Look for variants that need a fix for a53 erratum 835769.
static const char* arm64_variants_with_a53_835769_bug[] = {
- "default", "generic" // Pessimistically assume all generic ARM64s are A53s.
+ "default", "generic", "cortex-a53" // Pessimistically assume all generic ARM64s are A53s.
};
bool needs_a53_835769_fix = FindVariantInArray(arm64_variants_with_a53_835769_bug,
arraysize(arm64_variants_with_a53_835769_bug),
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 6812178..be5a15e 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1421,9 +1421,8 @@
.extern artSet64StaticFromCode
ENTRY art_quick_set64_static
SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC
- mov x3, x1 // Store value
ldr x1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] // Load referrer
- mov x2, x3 // Put value param
+ // x2 contains the parameter
mov x3, xSELF // pass Thread::Current
bl artSet64StaticFromCode
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index ce1b2f3..68156ae 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1244,7 +1244,7 @@
.extern artSet64StaticFromCode
ENTRY art_quick_set64_static
SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
- move $a2, $a1 # pass new_val
+ # a2 contains the new val
ld $a1, FRAME_SIZE_REFS_ONLY_CALLEE_SAVE($sp) # pass referrer's Method*
jal artSet64StaticFromCode # (field_idx, referrer, new_val, Thread*)
move $a3, rSELF # pass Thread::Current
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index f10799c..0d2457e 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -774,22 +774,6 @@
return result;
}
- // Method with 32b arg0, 64b arg1
- size_t Invoke3UWithReferrer(size_t arg0, uint64_t arg1, uintptr_t code, Thread* self,
- ArtMethod* referrer) {
-#if (defined(__x86_64__) && !defined(__APPLE__)) || (defined(__mips__) && defined(__LP64__)) || \
- defined(__aarch64__)
- // Just pass through.
- return Invoke3WithReferrer(arg0, arg1, 0U, code, self, referrer);
-#else
- // Need to split up arguments.
- uint32_t lower = static_cast<uint32_t>(arg1 & 0xFFFFFFFF);
- uint32_t upper = static_cast<uint32_t>((arg1 >> 32) & 0xFFFFFFFF);
-
- return Invoke3WithReferrer(arg0, lower, upper, code, self, referrer);
-#endif
- }
-
static uintptr_t GetEntrypoint(Thread* self, QuickEntrypointEnum entrypoint) {
int32_t offset;
#ifdef __LP64__
@@ -1974,21 +1958,23 @@
}
-// TODO: Complete these tests for 32b architectures.
+// TODO: Complete these tests for 32b architectures
static void GetSet64Static(ArtField* f, Thread* self, ArtMethod* referrer,
StubTest* test)
SHARED_REQUIRES(Locks::mutator_lock_) {
-#if (defined(__x86_64__) && !defined(__APPLE__)) || (defined(__mips__) && defined(__LP64__)) || \
- defined(__aarch64__)
+#if (defined(__x86_64__) && !defined(__APPLE__)) || (defined(__mips__) && defined(__LP64__)) \
+ || defined(__aarch64__)
uint64_t values[] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF, 0xFFFFFFFFFFFF };
for (size_t i = 0; i < arraysize(values); ++i) {
- test->Invoke3UWithReferrer(static_cast<size_t>(f->GetDexFieldIndex()),
- values[i],
- StubTest::GetEntrypoint(self, kQuickSet64Static),
- self,
- referrer);
+ // 64 bit FieldSet stores the set value in the second register.
+ test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()),
+ 0U,
+ values[i],
+ StubTest::GetEntrypoint(self, kQuickSet64Static),
+ self,
+ referrer);
size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()),
0U, 0U,
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 5394c46..4a106e4 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1254,15 +1254,18 @@
// Call artSet64StaticFromCode with 3 word size arguments plus with the referrer in the 2nd position
// so that new_val is aligned on even registers were we passing arguments in registers.
DEFINE_FUNCTION art_quick_set64_static
+ // TODO: Implement SETUP_GOT_NOSAVE for got_reg = ecx to avoid moving around the registers.
+ movd %ebx, %xmm0
SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC
- mov FRAME_SIZE_REFS_ONLY_CALLEE_SAVE(%esp), %ebx // get referrer
+ movd %xmm0, %ebx
+ mov FRAME_SIZE_REFS_ONLY_CALLEE_SAVE(%esp), %ecx // get referrer
subl LITERAL(12), %esp // alignment padding
CFI_ADJUST_CFA_OFFSET(12)
pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
CFI_ADJUST_CFA_OFFSET(4)
- PUSH edx // pass high half of new_val
- PUSH ecx // pass low half of new_val
- PUSH ebx // pass referrer
+ PUSH ebx // pass high half of new_val
+ PUSH edx // pass low half of new_val
+ PUSH ecx // pass referrer
PUSH eax // pass field_idx
call SYMBOL(artSet64StaticFromCode) // (field_idx, referrer, new_val, Thread*)
addl LITERAL(32), %esp // pop arguments
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 8a0a29b..5c413d2 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1203,7 +1203,7 @@
// This is singled out as the argument order is different.
DEFINE_FUNCTION art_quick_set64_static
- movq %rsi, %rdx // pass new_val
+ // new_val is already in %rdx
movq 8(%rsp), %rsi // pass referrer
SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
// field_idx is in rdi
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 345428c..6f2aa46 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -23,7 +23,6 @@
#include "mem_map.h"
#include "mutex.h"
#include "thread-inl.h"
-#include "base/memory_tool.h"
namespace art {
@@ -290,8 +289,7 @@
begin_(nullptr),
end_(nullptr),
ptr_(nullptr),
- arena_head_(nullptr),
- is_running_on_memory_tool_(RUNNING_ON_MEMORY_TOOL) {
+ arena_head_(nullptr) {
}
void ArenaAllocator::UpdateBytesAllocated() {
@@ -302,14 +300,13 @@
}
}
-void* ArenaAllocator::AllocValgrind(size_t bytes, ArenaAllocKind kind) {
+void* ArenaAllocator::AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind) {
size_t rounded_bytes = RoundUp(bytes + kMemoryToolRedZoneBytes, 8);
if (UNLIKELY(ptr_ + rounded_bytes > end_)) {
// Obtain a new block.
ObtainNewArenaForAllocation(rounded_bytes);
- if (UNLIKELY(ptr_ == nullptr)) {
- return nullptr;
- }
+ CHECK(ptr_ != nullptr);
+ MEMORY_TOOL_MAKE_UNDEFINED(ptr_, end_ - ptr_);
}
ArenaAllocatorStats::RecordAlloc(rounded_bytes, kind);
uint8_t* ret = ptr_;
@@ -318,6 +315,7 @@
for (uint8_t* ptr = ret; ptr < ptr_; ++ptr) {
CHECK_EQ(*ptr, 0U);
}
+ MEMORY_TOOL_MAKE_DEFINED(ret, bytes);
MEMORY_TOOL_MAKE_NOACCESS(ret + bytes, rounded_bytes - bytes);
return ret;
}
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index b4f19ee..565b416 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -21,6 +21,7 @@
#include <stddef.h>
#include "base/bit_utils.h"
+#include "base/memory_tool.h"
#include "debug_stack.h"
#include "macros.h"
#include "mutex.h"
@@ -149,6 +150,52 @@
typedef ArenaAllocatorStatsImpl<kArenaAllocatorCountAllocations> ArenaAllocatorStats;
+template <bool kAvailable, bool kValgrind>
+class ArenaAllocatorMemoryToolCheckImpl {
+ // This is the generic template but since there is a partial specialization
+ // for kValgrind == false, this can be instantiated only for kValgrind == true.
+ static_assert(kValgrind, "This template can be instantiated only for Valgrind.");
+ static_assert(kAvailable, "Valgrind implies memory tool availability.");
+
+ public:
+ ArenaAllocatorMemoryToolCheckImpl() : is_running_on_valgrind_(RUNNING_ON_MEMORY_TOOL) { }
+ bool IsRunningOnMemoryTool() { return is_running_on_valgrind_; }
+
+ private:
+ const bool is_running_on_valgrind_;
+};
+
+template <bool kAvailable>
+class ArenaAllocatorMemoryToolCheckImpl<kAvailable, false> {
+ public:
+ ArenaAllocatorMemoryToolCheckImpl() { }
+ bool IsRunningOnMemoryTool() { return kAvailable; }
+};
+
+typedef ArenaAllocatorMemoryToolCheckImpl<kMemoryToolIsAvailable, kMemoryToolIsValgrind>
+ ArenaAllocatorMemoryToolCheck;
+
+class ArenaAllocatorMemoryTool : private ArenaAllocatorMemoryToolCheck {
+ public:
+ using ArenaAllocatorMemoryToolCheck::IsRunningOnMemoryTool;
+
+ void MakeDefined(void* ptr, size_t size) {
+ if (IsRunningOnMemoryTool()) {
+ MEMORY_TOOL_MAKE_DEFINED(ptr, size);
+ }
+ }
+ void MakeUndefined(void* ptr, size_t size) {
+ if (IsRunningOnMemoryTool()) {
+ MEMORY_TOOL_MAKE_UNDEFINED(ptr, size);
+ }
+ }
+ void MakeInaccessible(void* ptr, size_t size) {
+ if (IsRunningOnMemoryTool()) {
+ MEMORY_TOOL_MAKE_NOACCESS(ptr, size);
+ }
+ }
+};
+
class Arena {
public:
static constexpr size_t kDefaultSize = 128 * KB;
@@ -233,18 +280,24 @@
DISALLOW_COPY_AND_ASSIGN(ArenaPool);
};
-class ArenaAllocator : private DebugStackRefCounter, private ArenaAllocatorStats {
+class ArenaAllocator
+ : private DebugStackRefCounter, private ArenaAllocatorStats, private ArenaAllocatorMemoryTool {
public:
explicit ArenaAllocator(ArenaPool* pool);
~ArenaAllocator();
+ using ArenaAllocatorMemoryTool::IsRunningOnMemoryTool;
+ using ArenaAllocatorMemoryTool::MakeDefined;
+ using ArenaAllocatorMemoryTool::MakeUndefined;
+ using ArenaAllocatorMemoryTool::MakeInaccessible;
+
// Get adapter for use in STL containers. See arena_containers.h .
ArenaAllocatorAdapter<void> Adapter(ArenaAllocKind kind = kArenaAllocSTL);
// Returns zeroed memory.
void* Alloc(size_t bytes, ArenaAllocKind kind = kArenaAllocMisc) ALWAYS_INLINE {
- if (UNLIKELY(is_running_on_memory_tool_)) {
- return AllocValgrind(bytes, kind);
+ if (UNLIKELY(IsRunningOnMemoryTool())) {
+ return AllocWithMemoryTool(bytes, kind);
}
bytes = RoundUp(bytes, kAlignment);
if (UNLIKELY(ptr_ + bytes > end_)) {
@@ -268,6 +321,7 @@
auto* end = reinterpret_cast<uint8_t*>(ptr) + ptr_size;
// If we haven't allocated anything else, we can safely extend.
if (end == ptr_) {
+ DCHECK(!IsRunningOnMemoryTool()); // Red zone prevents end == ptr_.
const size_t size_delta = new_size - ptr_size;
// Check remain space.
const size_t remain = end_ - ptr_;
@@ -288,7 +342,7 @@
return static_cast<T*>(Alloc(length * sizeof(T), kind));
}
- void* AllocValgrind(size_t bytes, ArenaAllocKind kind);
+ void* AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind);
void ObtainNewArenaForAllocation(size_t allocation_size);
@@ -316,7 +370,6 @@
uint8_t* end_;
uint8_t* ptr_;
Arena* arena_head_;
- bool is_running_on_memory_tool_;
template <typename U>
friend class ArenaAllocatorAdapter;
diff --git a/runtime/base/arena_containers.h b/runtime/base/arena_containers.h
index 810c1c4..e7ea09d 100644
--- a/runtime/base/arena_containers.h
+++ b/runtime/base/arena_containers.h
@@ -161,7 +161,7 @@
return arena_allocator_->AllocArray<T>(n, ArenaAllocatorAdapterKind::Kind());
}
void deallocate(pointer p, size_type n) {
- UNUSED(p, n);
+ arena_allocator_->MakeInaccessible(p, sizeof(T) * n);
}
void construct(pointer p, const_reference val) {
diff --git a/runtime/base/bit_utils.h b/runtime/base/bit_utils.h
index 1b0d774..9c78ee5 100644
--- a/runtime/base/bit_utils.h
+++ b/runtime/base/bit_utils.h
@@ -263,6 +263,33 @@
: static_cast<unsigned_type>(value));
}
+// Generate maximum/minimum values for signed/unsigned n-bit integers
+template <typename T>
+static constexpr T MaxInt(size_t bits) {
+ return
+ DCHECK_CONSTEXPR(bits > 0, "bits cannot be zero", 0)
+ DCHECK_CONSTEXPR(bits <= BitSizeOf<T>(), "kBits must be < max.", 0)
+ bits == BitSizeOf<T>()
+ ? std::numeric_limits<T>::max()
+ : std::is_signed<T>::value
+ ? (bits == 1
+ ? 0
+ : static_cast<T>(MaxInt<typename std::make_unsigned<T>::type>(bits - 1)))
+ : static_cast<T>(UINT64_C(1) << bits) - static_cast<T>(1);
+}
+
+template <typename T>
+static constexpr T MinInt(size_t bits) {
+ return
+ DCHECK_CONSTEXPR(bits > 0, "bits cannot be zero", 0)
+ DCHECK_CONSTEXPR(bits <= BitSizeOf<T>(), "kBits must be < max.", 0)
+ bits == BitSizeOf<T>()
+ ? std::numeric_limits<T>::min()
+ : std::is_signed<T>::value
+ ? (bits == 1 ? -1 : static_cast<T>(-1) - MaxInt<T>(bits))
+ : static_cast<T>(0);
+}
+
// Using the Curiously Recurring Template Pattern to implement everything shared
// by LowToHighBitIterator and HighToLowBitIterator, i.e. everything but operator*().
template <typename T, typename Iter>
diff --git a/runtime/base/memory_tool.h b/runtime/base/memory_tool.h
index e0bdcfe..e1a2e07 100644
--- a/runtime/base/memory_tool.h
+++ b/runtime/base/memory_tool.h
@@ -32,10 +32,12 @@
#define MEMORY_TOOL_MAKE_NOACCESS(p, s) __asan_poison_memory_region(p, s)
#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) __asan_unpoison_memory_region(p, s)
#define MEMORY_TOOL_MAKE_DEFINED(p, s) __asan_unpoison_memory_region(p, s)
+constexpr bool kMemoryToolIsAvailable = true;
#else
#define MEMORY_TOOL_MAKE_NOACCESS(p, s) do { (void)(p); (void)(s); } while (0)
#define MEMORY_TOOL_MAKE_UNDEFINED(p, s) do { (void)(p); (void)(s); } while (0)
#define MEMORY_TOOL_MAKE_DEFINED(p, s) do { (void)(p); (void)(s); } while (0)
+constexpr bool kMemoryToolIsAvailable = false;
#endif
#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
@@ -54,6 +56,7 @@
#define MEMORY_TOOL_MAKE_DEFINED(p, s) VALGRIND_MAKE_MEM_DEFINED(p, s)
#define ATTRIBUTE_NO_SANITIZE_ADDRESS
#define RUNNING_ON_MEMORY_TOOL RUNNING_ON_VALGRIND
+constexpr bool kMemoryToolIsAvailable = true;
constexpr bool kMemoryToolIsValgrind = true;
constexpr bool kMemoryToolDetectsLeaks = true;
constexpr bool kMemoryToolAddsRedzones = true;
diff --git a/runtime/base/scoped_arena_allocator.cc b/runtime/base/scoped_arena_allocator.cc
index d823edd..31f96e4 100644
--- a/runtime/base/scoped_arena_allocator.cc
+++ b/runtime/base/scoped_arena_allocator.cc
@@ -29,8 +29,7 @@
bottom_arena_(nullptr),
top_arena_(nullptr),
top_ptr_(nullptr),
- top_end_(nullptr),
- is_running_on_memory_tool_(RUNNING_ON_MEMORY_TOOL > 0) {
+ top_end_(nullptr) {
}
ArenaStack::~ArenaStack() {
@@ -91,7 +90,7 @@
}
}
-void* ArenaStack::AllocValgrind(size_t bytes, ArenaAllocKind kind) {
+void* ArenaStack::AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind) {
size_t rounded_bytes = RoundUp(bytes + kMemoryToolRedZoneBytes, 8);
uint8_t* ptr = top_ptr_;
if (UNLIKELY(static_cast<size_t>(top_end_ - ptr) < rounded_bytes)) {
diff --git a/runtime/base/scoped_arena_allocator.h b/runtime/base/scoped_arena_allocator.h
index ca514e4..4f51370 100644
--- a/runtime/base/scoped_arena_allocator.h
+++ b/runtime/base/scoped_arena_allocator.h
@@ -32,11 +32,16 @@
class ScopedArenaAllocatorAdapter;
// Holds a list of Arenas for use by ScopedArenaAllocator stack.
-class ArenaStack : private DebugStackRefCounter {
+class ArenaStack : private DebugStackRefCounter, private ArenaAllocatorMemoryTool {
public:
explicit ArenaStack(ArenaPool* arena_pool);
~ArenaStack();
+ using ArenaAllocatorMemoryTool::IsRunningOnMemoryTool;
+ using ArenaAllocatorMemoryTool::MakeDefined;
+ using ArenaAllocatorMemoryTool::MakeUndefined;
+ using ArenaAllocatorMemoryTool::MakeInaccessible;
+
void Reset();
size_t PeakBytesAllocated() {
@@ -64,8 +69,8 @@
// Private - access via ScopedArenaAllocator or ScopedArenaAllocatorAdapter.
void* Alloc(size_t bytes, ArenaAllocKind kind) ALWAYS_INLINE {
- if (UNLIKELY(is_running_on_memory_tool_)) {
- return AllocValgrind(bytes, kind);
+ if (UNLIKELY(IsRunningOnMemoryTool())) {
+ return AllocWithMemoryTool(bytes, kind);
}
size_t rounded_bytes = RoundUp(bytes, 8);
uint8_t* ptr = top_ptr_;
@@ -80,7 +85,7 @@
uint8_t* AllocateFromNextArena(size_t rounded_bytes);
void UpdatePeakStatsAndRestore(const ArenaAllocatorStats& restore_stats);
void UpdateBytesAllocated();
- void* AllocValgrind(size_t bytes, ArenaAllocKind kind);
+ void* AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind);
StatsAndPool stats_and_pool_;
Arena* bottom_arena_;
@@ -88,8 +93,6 @@
uint8_t* top_ptr_;
uint8_t* top_end_;
- const bool is_running_on_memory_tool_;
-
friend class ScopedArenaAllocator;
template <typename T>
friend class ScopedArenaAllocatorAdapter;
diff --git a/runtime/base/scoped_arena_containers.h b/runtime/base/scoped_arena_containers.h
index 82db60e..eecc55f 100644
--- a/runtime/base/scoped_arena_containers.h
+++ b/runtime/base/scoped_arena_containers.h
@@ -153,9 +153,8 @@
ArenaAllocatorAdapterKind::Kind()));
}
void deallocate(pointer p, size_type n) {
- UNUSED(p);
- UNUSED(n);
DebugStackIndirectTopRef::CheckTop();
+ arena_stack_->MakeInaccessible(p, sizeof(T) * n);
}
void construct(pointer p, const_reference val) {
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index b6ad547..beabce3 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -2463,6 +2463,9 @@
ScopedCheck sc(kFlag_Default, __FUNCTION__);
JniValueType args[2] = {{.E = env}, {.L = obj}};
if (sc.Check(soa, true, "EL", args)) {
+ if (obj != nullptr) {
+ down_cast<JNIEnvExt*>(env)->RecordMonitorEnter(obj);
+ }
JniValueType result;
result.i = baseEnv(env)->MonitorEnter(env, obj);
if (sc.Check(soa, false, "i", &result)) {
@@ -2477,6 +2480,9 @@
ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
JniValueType args[2] = {{.E = env}, {.L = obj}};
if (sc.Check(soa, true, "EL", args)) {
+ if (obj != nullptr) {
+ down_cast<JNIEnvExt*>(env)->CheckMonitorRelease(obj);
+ }
JniValueType result;
result.i = baseEnv(env)->MonitorExit(env, obj);
if (sc.Check(soa, false, "i", &result)) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index dbc5cec..acb39c5 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -79,6 +79,7 @@
#include "scoped_thread_state_change.h"
#include "handle_scope-inl.h"
#include "thread-inl.h"
+#include "trace.h"
#include "utils.h"
#include "utils/dex_cache_arrays_layout-inl.h"
#include "verifier/method_verifier.h"
@@ -1299,6 +1300,9 @@
}
void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) {
+ // Acquire tracing_enabled before locking class linker lock to prevent lock order violation. Since
+ // enabling tracing requires the mutator lock, there are no race conditions here.
+ const bool tracing_enabled = Trace::IsTracingEnabled();
Thread* const self = Thread::Current();
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
@@ -1320,6 +1324,14 @@
// Need to make sure to not copy ArtMethods without doing read barriers since the roots are
// marked concurrently and we don't hold the classlinker_classes_lock_ when we do the copy.
boot_class_table_.VisitRoots(buffered_visitor);
+
+ // If tracing is enabled, then mark all the class loaders to prevent unloading.
+ if (tracing_enabled) {
+ for (const ClassLoaderData& data : class_loaders_) {
+ GcRoot<mirror::Object> root(GcRoot<mirror::Object>(self->DecodeJObject(data.weak_root)));
+ root.VisitRoot(visitor, RootInfo(kRootVMInternal));
+ }
+ }
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_class_roots_) {
mirror::Class* old_ref = root.Read<kWithoutReadBarrier>();
@@ -2650,10 +2662,8 @@
const DexFile& dex_file,
bool allow_failure) {
// Search assuming unique-ness of dex file.
- JavaVMExt* const vm = self->GetJniEnv()->vm;
for (jweak weak_root : dex_caches_) {
- mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(
- vm->DecodeWeakGlobal(self, weak_root));
+ mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(self->DecodeJObject(weak_root));
if (dex_cache != nullptr && dex_cache->GetDexFile() == &dex_file) {
return dex_cache;
}
@@ -6202,10 +6212,9 @@
void ClassLinker::VisitClassLoaders(ClassLoaderVisitor* visitor) const {
Thread* const self = Thread::Current();
- JavaVMExt* const vm = self->GetJniEnv()->vm;
for (const ClassLoaderData& data : class_loaders_) {
- auto* const class_loader = down_cast<mirror::ClassLoader*>(
- vm->DecodeWeakGlobal(self, data.weak_root));
+ // Need to use DecodeJObject so that we get null for cleared JNI weak globals.
+ auto* const class_loader = down_cast<mirror::ClassLoader*>(self->DecodeJObject(data.weak_root));
if (class_loader != nullptr) {
visitor->Visit(class_loader);
}
@@ -6218,8 +6227,8 @@
JavaVMExt* const vm = Runtime::Current()->GetJavaVM();
for (auto it = class_loaders_.begin(); it != class_loaders_.end(); ) {
const ClassLoaderData& data = *it;
- auto* const class_loader = down_cast<mirror::ClassLoader*>(
- vm->DecodeWeakGlobal(self, data.weak_root));
+ // Need to use DecodeJObject so that we get null for cleared JNI weak globals.
+ auto* const class_loader = down_cast<mirror::ClassLoader*>(self->DecodeJObject(data.weak_root));
if (class_loader != nullptr) {
++it;
} else {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 739403f..7f3e938 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -883,6 +883,7 @@
friend class ImageWriter; // for GetClassRoots
friend class ImageDumper; // for FindOpenedOatFileFromOatLocation
friend class JniCompilerTest; // for GetRuntimeQuickGenericJniStub
+ friend class JniInternalTest; // for GetRuntimeQuickGenericJniStub
ART_FRIEND_TEST(mirror::DexCacheTest, Open); // for AllocDexCache
DISALLOW_COPY_AND_ASSIGN(ClassLinker);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 450031a..b19381d 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -30,6 +30,7 @@
#include "dex_instruction.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/allocation_record.h"
+#include "gc/scoped_gc_critical_section.h"
#include "gc/space/large_object_space.h"
#include "gc/space/space-inl.h"
#include "handle_scope.h"
@@ -559,14 +560,15 @@
return;
}
+ Thread* const self = Thread::Current();
{
// TODO: dalvik only warned if there were breakpoints left over. clear in Dbg::Disconnected?
- ReaderMutexLock mu(Thread::Current(), *Locks::breakpoint_lock_);
+ ReaderMutexLock mu(self, *Locks::breakpoint_lock_);
CHECK_EQ(gBreakpoints.size(), 0U);
}
{
- MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_);
+ MutexLock mu(self, *Locks::deoptimization_lock_);
CHECK_EQ(deoptimization_requests_.size(), 0U);
CHECK_EQ(full_deoptimization_event_count_, 0U);
CHECK_EQ(dex_pc_change_event_ref_count_, 0U);
@@ -579,15 +581,11 @@
Runtime* runtime = Runtime::Current();
ScopedSuspendAll ssa(__FUNCTION__);
- Thread* self = Thread::Current();
- ThreadState old_state = self->SetStateUnsafe(kRunnable);
- CHECK_NE(old_state, kRunnable);
if (RequiresDeoptimization()) {
runtime->GetInstrumentation()->EnableDeoptimization();
}
instrumentation_events_ = 0;
gDebuggerActive = true;
- CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
LOG(INFO) << "Debugger is active";
}
@@ -602,6 +600,10 @@
Runtime* runtime = Runtime::Current();
Thread* self = Thread::Current();
{
+ // Required for DisableDeoptimization.
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseInstrumentation,
+ gc::kCollectorTypeInstrumentation);
ScopedSuspendAll ssa(__FUNCTION__);
ThreadState old_state = self->SetStateUnsafe(kRunnable);
// Debugger may not be active at this point.
@@ -3166,6 +3168,10 @@
}
CHECK_EQ(self->GetState(), kRunnable);
ScopedThreadSuspension sts(self, kWaitingForDeoptimization);
+ // Required for ProcessDeoptimizationRequest.
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseInstrumentation,
+ gc::kCollectorTypeInstrumentation);
// We need to suspend mutator threads first.
ScopedSuspendAll ssa(__FUNCTION__);
const ThreadState old_state = self->SetStateUnsafe(kRunnable);
diff --git a/runtime/debugger.h b/runtime/debugger.h
index b4d42de..b3617e4 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -731,7 +731,7 @@
SHARED_REQUIRES(Locks::mutator_lock_);
static void ProcessDeoptimizationRequest(const DeoptimizationRequest& request)
- REQUIRES(Locks::mutator_lock_);
+ REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_);
static void RequestDeoptimizationLocked(const DeoptimizationRequest& req)
REQUIRES(Locks::deoptimization_lock_) SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 8ae0b07..f66628d 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -411,12 +411,6 @@
ThrowNullPointerExceptionForMethodAccess(method_idx, type);
return nullptr; // Failure.
} else if (access_check) {
- // Incompatible class change should have been handled in resolve method.
- if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
- ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method,
- referrer);
- return nullptr; // Failure.
- }
mirror::Class* methods_class = resolved_method->GetDeclaringClass();
mirror::Class* referring_class = referrer->GetDeclaringClass();
bool can_access_resolved_method =
@@ -426,6 +420,12 @@
DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
return nullptr; // Failure.
}
+ // Incompatible class change should have been handled in resolve method.
+ if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
+ ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method,
+ referrer);
+ return nullptr; // Failure.
+ }
}
switch (type) {
case kStatic:
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index fc5c52e..58f256a 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -65,6 +65,9 @@
static void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
JNIEnvExt* env = self->GetJniEnv();
+ if (UNLIKELY(env->check_jni)) {
+ env->CheckNoHeldMonitors();
+ }
env->locals.SetSegmentState(env->local_ref_cookie);
env->local_ref_cookie = saved_local_ref_cookie;
self->PopHandleScope();
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 468179c..0a7a69f 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -620,7 +620,10 @@
gc_mark_stack_->PushBack(to_ref);
} else {
CHECK_EQ(static_cast<uint32_t>(mark_stack_mode),
- static_cast<uint32_t>(kMarkStackModeGcExclusive));
+ static_cast<uint32_t>(kMarkStackModeGcExclusive))
+ << "ref=" << to_ref
+ << " self->gc_marking=" << self->GetIsGcMarking()
+ << " cc->is_marking=" << is_marking_;
CHECK(self == thread_running_gc_)
<< "Only GC-running thread should access the mark stack "
<< "in the GC exclusive mark stack mode";
diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h
index 95ba380..416510d 100644
--- a/runtime/gc/collector_type.h
+++ b/runtime/gc/collector_type.h
@@ -40,6 +40,8 @@
kCollectorTypeHeapTrim,
// A (mostly) concurrent copying collector.
kCollectorTypeCC,
+ // Instrumentation critical section fake collector.
+ kCollectorTypeInstrumentation,
// A homogeneous space compaction collector used in background transition
// when both foreground and background collector are CMS.
kCollectorTypeHomogeneousSpaceCompact,
diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc
index 6be683d..84243df 100644
--- a/runtime/gc/gc_cause.cc
+++ b/runtime/gc/gc_cause.cc
@@ -33,6 +33,7 @@
case kGcCauseDisableMovingGc: return "DisableMovingGc";
case kGcCauseHomogeneousSpaceCompact: return "HomogeneousSpaceCompact";
case kGcCauseTrim: return "HeapTrim";
+ case kGcCauseInstrumentation: return "Instrumentation";
default:
LOG(FATAL) << "Unreachable";
UNREACHABLE();
diff --git a/runtime/gc/gc_cause.h b/runtime/gc/gc_cause.h
index 0536f32d..34c7766 100644
--- a/runtime/gc/gc_cause.h
+++ b/runtime/gc/gc_cause.h
@@ -39,6 +39,8 @@
kGcCauseDisableMovingGc,
// Not a real GC cause, used when we trim the heap.
kGcCauseTrim,
+ // Not a real GC cause, used to implement exclusion between GC and instrumentation.
+ kGcCauseInstrumentation,
// GC triggered for background transition when both foreground and background collector are CMS.
kGcCauseHomogeneousSpaceCompact,
};
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 7d664fa..657fcb5 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -1312,6 +1312,13 @@
ATRACE_END();
}
+void Heap::StartGC(Thread* self, GcCause cause, CollectorType collector_type) {
+ MutexLock mu(self, *gc_complete_lock_);
+ // Ensure there is only one GC at a time.
+ WaitForGcToCompleteLocked(cause, self);
+ collector_type_running_ = collector_type;
+}
+
void Heap::TrimSpaces(Thread* self) {
{
// Need to do this before acquiring the locks since we don't want to get suspended while
@@ -1319,10 +1326,7 @@
ScopedThreadStateChange tsc(self, kWaitingForGcToComplete);
// Pretend we are doing a GC to prevent background compaction from deleting the space we are
// trimming.
- MutexLock mu(self, *gc_complete_lock_);
- // Ensure there is only one GC at a time.
- WaitForGcToCompleteLocked(kGcCauseTrim, self);
- collector_type_running_ = kCollectorTypeHeapTrim;
+ StartGC(self, kGcCauseTrim, kCollectorTypeHeapTrim);
}
ATRACE_BEGIN(__FUNCTION__);
const uint64_t start_ns = NanoTime();
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index d0d0be3..cc48172 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -775,6 +775,8 @@
REQUIRES(Locks::mutator_lock_);
void LogGC(GcCause gc_cause, collector::GarbageCollector* collector);
+ void StartGC(Thread* self, GcCause cause, CollectorType collector_type)
+ REQUIRES(!*gc_complete_lock_);
void FinishGC(Thread* self, collector::GcType gc_type) REQUIRES(!*gc_complete_lock_);
// Create a mem map with a preferred base address.
@@ -1325,6 +1327,7 @@
friend class collector::MarkSweep;
friend class collector::SemiSpace;
friend class ReferenceQueue;
+ friend class ScopedGCCriticalSection;
friend class VerifyReferenceCardVisitor;
friend class VerifyReferenceVisitor;
friend class VerifyObjectVisitor;
diff --git a/runtime/gc/scoped_gc_critical_section.cc b/runtime/gc/scoped_gc_critical_section.cc
new file mode 100644
index 0000000..e7786a1
--- /dev/null
+++ b/runtime/gc/scoped_gc_critical_section.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 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 "scoped_gc_critical_section.h"
+
+#include "gc/collector_type.h"
+#include "gc/heap.h"
+#include "runtime.h"
+#include "thread-inl.h"
+
+namespace art {
+namespace gc {
+
+ScopedGCCriticalSection::ScopedGCCriticalSection(Thread* self,
+ GcCause cause,
+ CollectorType collector_type)
+ : self_(self) {
+ Runtime::Current()->GetHeap()->StartGC(self, cause, collector_type);
+ old_cause_ = self->StartAssertNoThreadSuspension("ScopedGCCriticalSection");
+}
+ScopedGCCriticalSection::~ScopedGCCriticalSection() {
+ self_->EndAssertNoThreadSuspension(old_cause_);
+ Runtime::Current()->GetHeap()->FinishGC(self_, collector::kGcTypeNone);
+}
+
+} // namespace gc
+} // namespace art
+
diff --git a/runtime/gc/scoped_gc_critical_section.h b/runtime/gc/scoped_gc_critical_section.h
new file mode 100644
index 0000000..ec93bca
--- /dev/null
+++ b/runtime/gc/scoped_gc_critical_section.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 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_GC_SCOPED_GC_CRITICAL_SECTION_H_
+#define ART_RUNTIME_GC_SCOPED_GC_CRITICAL_SECTION_H_
+
+#include "base/mutex.h"
+#include "collector_type.h"
+#include "gc_cause.h"
+
+namespace art {
+
+class Thread;
+
+namespace gc {
+
+// Wait until the GC is finished and then prevent GC from starting until the destructor. Used
+// to prevent deadlocks in places where we call ClassLinker::VisitClass with all th threads
+// suspended.
+class ScopedGCCriticalSection {
+ public:
+ ScopedGCCriticalSection(Thread* self, GcCause cause, CollectorType collector_type)
+ ACQUIRE(Roles::uninterruptible_);
+ ~ScopedGCCriticalSection() RELEASE(Roles::uninterruptible_);
+
+ private:
+ Thread* const self_;
+ const char* old_cause_;
+};
+
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_SCOPED_GC_CRITICAL_SECTION_H_
diff --git a/runtime/globals.h b/runtime/globals.h
index d70f3ab..987a94e 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -58,12 +58,6 @@
static constexpr bool kIsTargetBuild = false;
#endif
-#if defined(ART_USE_OPTIMIZING_COMPILER)
-static constexpr bool kUseOptimizingCompiler = true;
-#else
-static constexpr bool kUseOptimizingCompiler = false;
-#endif
-
// Garbage collector constants.
static constexpr bool kMovingCollector = true;
static constexpr bool kMarkCompactSupport = false && kMovingCollector;
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index c9ba6cf..a5b63b4 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -17,6 +17,7 @@
#include "indirect_reference_table-inl.h"
#include "jni_internal.h"
+#include "nth_caller_visitor.h"
#include "reference_table.h"
#include "runtime.h"
#include "scoped_thread_state_change.h"
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 8046056..612ca14 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -146,9 +146,13 @@
// Deoptimization.
void EnableDeoptimization()
- REQUIRES(Locks::mutator_lock_, !deoptimized_methods_lock_);
+ REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!deoptimized_methods_lock_);
+ // Calls UndeoptimizeEverything which may visit class linker classes through ConfigureStubs.
void DisableDeoptimization(const char* key)
- REQUIRES(Locks::mutator_lock_, !deoptimized_methods_lock_);
+ REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
+ REQUIRES(!deoptimized_methods_lock_);
+
bool AreAllMethodsDeoptimized() const {
return interpreter_stubs_installed_;
}
@@ -156,12 +160,17 @@
// Executes everything with interpreter.
void DeoptimizeEverything(const char* key)
- REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_,
+ REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
+ REQUIRES(!Locks::thread_list_lock_,
+ !Locks::classlinker_classes_lock_,
!deoptimized_methods_lock_);
- // Executes everything with compiled code (or interpreter if there is no code).
+ // Executes everything with compiled code (or interpreter if there is no code). May visit class
+ // linker classes through ConfigureStubs.
void UndeoptimizeEverything(const char* key)
- REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_,
+ REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
+ REQUIRES(!Locks::thread_list_lock_,
+ !Locks::classlinker_classes_lock_,
!deoptimized_methods_lock_);
// Deoptimize a method by forcing its execution with the interpreter. Nevertheless, a static
@@ -183,12 +192,16 @@
// Enable method tracing by installing instrumentation entry/exit stubs or interpreter.
void EnableMethodTracing(const char* key,
bool needs_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners)
- REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_,
+ REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
+ REQUIRES(!Locks::thread_list_lock_,
+ !Locks::classlinker_classes_lock_,
!deoptimized_methods_lock_);
// Disable method tracing by uninstalling instrumentation entry/exit stubs or interpreter.
void DisableMethodTracing(const char* key)
- REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_,
+ REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
+ REQUIRES(!Locks::thread_list_lock_,
+ !Locks::classlinker_classes_lock_,
!deoptimized_methods_lock_);
InterpreterHandlerTable GetInterpreterHandlerTable() const
@@ -393,7 +406,9 @@
// instrumentation level it needs. Therefore the current instrumentation level
// becomes the highest instrumentation level required by a client.
void ConfigureStubs(const char* key, InstrumentationLevel desired_instrumentation_level)
- REQUIRES(Locks::mutator_lock_, !deoptimized_methods_lock_, !Locks::thread_list_lock_,
+ REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
+ REQUIRES(!deoptimized_methods_lock_,
+ !Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_);
void UpdateInterpreterHandlerTable() REQUIRES(Locks::mutator_lock_) {
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index d98d246..e4688a2 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -20,6 +20,7 @@
#include "common_throws.h"
#include "class_linker-inl.h"
#include "dex_file.h"
+#include "gc/scoped_gc_critical_section.h"
#include "handle_scope-inl.h"
#include "jvalue.h"
#include "runtime.h"
@@ -151,6 +152,9 @@
ScopedObjectAccess soa(Thread::Current());
instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
ScopedThreadSuspension sts(soa.Self(), kSuspended);
+ gc::ScopedGCCriticalSection gcs(soa.Self(),
+ gc::kGcCauseInstrumentation,
+ gc::kCollectorTypeInstrumentation);
ScopedSuspendAll ssa("Instrumentation::ConfigureStubs");
instr->ConfigureStubs(key, level);
}
@@ -203,6 +207,9 @@
Runtime* runtime = Runtime::Current();
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
ScopedThreadSuspension sts(self, kSuspended);
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseInstrumentation,
+ gc::kCollectorTypeInstrumentation);
ScopedSuspendAll ssa("Single method deoptimization");
if (enable_deoptimization) {
instrumentation->EnableDeoptimization();
@@ -216,6 +223,9 @@
Runtime* runtime = Runtime::Current();
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
ScopedThreadSuspension sts(self, kSuspended);
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseInstrumentation,
+ gc::kCollectorTypeInstrumentation);
ScopedSuspendAll ssa("Single method undeoptimization");
instrumentation->Undeoptimize(method);
if (disable_deoptimization) {
@@ -228,6 +238,9 @@
Runtime* runtime = Runtime::Current();
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
ScopedThreadSuspension sts(self, kSuspended);
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseInstrumentation,
+ gc::kCollectorTypeInstrumentation);
ScopedSuspendAll ssa("Full deoptimization");
if (enable_deoptimization) {
instrumentation->EnableDeoptimization();
@@ -240,6 +253,9 @@
Runtime* runtime = Runtime::Current();
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
ScopedThreadSuspension sts(self, kSuspended);
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseInstrumentation,
+ gc::kCollectorTypeInstrumentation);
ScopedSuspendAll ssa("Full undeoptimization");
instrumentation->UndeoptimizeEverything(key);
if (disable_deoptimization) {
@@ -252,6 +268,9 @@
Runtime* runtime = Runtime::Current();
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
ScopedThreadSuspension sts(self, kSuspended);
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseInstrumentation,
+ gc::kCollectorTypeInstrumentation);
ScopedSuspendAll ssa("EnableMethodTracing");
instrumentation->EnableMethodTracing(key, needs_interpreter);
}
@@ -261,6 +280,9 @@
Runtime* runtime = Runtime::Current();
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
ScopedThreadSuspension sts(self, kSuspended);
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseInstrumentation,
+ gc::kCollectorTypeInstrumentation);
ScopedSuspendAll ssa("EnableMethodTracing");
instrumentation->DisableMethodTracing(key);
}
diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc
index e9c16c1..9b9c5d2 100644
--- a/runtime/jit/jit_instrumentation.cc
+++ b/runtime/jit/jit_instrumentation.cc
@@ -111,6 +111,9 @@
DCHECK(this_object != nullptr);
ProfilingInfo* info = caller->GetProfilingInfo(sizeof(void*));
if (info != nullptr) {
+ // Since the instrumentation is marked from the declaring class we need to mark the card so
+ // that mod-union tables and card rescanning know about the update.
+ Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(caller->GetDeclaringClass());
info->AddInvokeInfo(thread, dex_pc, this_object->GetClass());
}
}
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index b18b430..4104d7a 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -16,10 +16,17 @@
#include "jni_env_ext.h"
+#include <algorithm>
+#include <vector>
+
#include "check_jni.h"
#include "indirect_reference_table.h"
#include "java_vm_ext.h"
#include "jni_internal.h"
+#include "lock_word.h"
+#include "mirror/object-inl.h"
+#include "nth_caller_visitor.h"
+#include "thread-inl.h"
namespace art {
@@ -63,14 +70,14 @@
JNIEnvExt::~JNIEnvExt() {
}
-jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_) {
+jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) {
if (obj == nullptr) {
return nullptr;
}
return reinterpret_cast<jobject>(locals.Add(local_ref_cookie, obj));
}
-void JNIEnvExt::DeleteLocalRef(jobject obj) SHARED_REQUIRES(Locks::mutator_lock_) {
+void JNIEnvExt::DeleteLocalRef(jobject obj) {
if (obj != nullptr) {
locals.Remove(local_ref_cookie, reinterpret_cast<IndirectRef>(obj));
}
@@ -86,14 +93,14 @@
monitors.Dump(os);
}
-void JNIEnvExt::PushFrame(int capacity) SHARED_REQUIRES(Locks::mutator_lock_) {
+void JNIEnvExt::PushFrame(int capacity) {
UNUSED(capacity); // cpplint gets confused with (int) and thinks its a cast.
// TODO: take 'capacity' into account.
stacked_local_ref_cookies.push_back(local_ref_cookie);
local_ref_cookie = locals.GetSegmentState();
}
-void JNIEnvExt::PopFrame() SHARED_REQUIRES(Locks::mutator_lock_) {
+void JNIEnvExt::PopFrame() {
locals.SetSegmentState(local_ref_cookie);
local_ref_cookie = stacked_local_ref_cookies.back();
stacked_local_ref_cookies.pop_back();
@@ -104,4 +111,118 @@
IndirectReferenceTable::SegmentStateOffset().Int32Value());
}
+// Use some defining part of the caller's frame as the identifying mark for the JNI segment.
+static uintptr_t GetJavaCallFrame(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) {
+ NthCallerVisitor zeroth_caller(self, 0, false);
+ zeroth_caller.WalkStack();
+ if (zeroth_caller.caller == nullptr) {
+ // No Java code, must be from pure native code.
+ return 0;
+ } else if (zeroth_caller.GetCurrentQuickFrame() == nullptr) {
+ // Shadow frame = interpreter. Use the actual shadow frame's address.
+ DCHECK(zeroth_caller.GetCurrentShadowFrame() != nullptr);
+ return reinterpret_cast<uintptr_t>(zeroth_caller.GetCurrentShadowFrame());
+ } else {
+ // Quick frame = compiled code. Use the bottom of the frame.
+ return reinterpret_cast<uintptr_t>(zeroth_caller.GetCurrentQuickFrame());
+ }
+}
+
+void JNIEnvExt::RecordMonitorEnter(jobject obj) {
+ locked_objects_.push_back(std::make_pair(GetJavaCallFrame(self), obj));
+}
+
+static std::string ComputeMonitorDescription(Thread* self,
+ jobject obj) SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::Object* o = self->DecodeJObject(obj);
+ if ((o->GetLockWord(false).GetState() == LockWord::kThinLocked) &&
+ Locks::mutator_lock_->IsExclusiveHeld(self)) {
+ // Getting the identity hashcode here would result in lock inflation and suspension of the
+ // current thread, which isn't safe if this is the only runnable thread.
+ return StringPrintf("<@addr=0x%" PRIxPTR "> (a %s)",
+ reinterpret_cast<intptr_t>(o),
+ PrettyTypeOf(o).c_str());
+ } else {
+ // IdentityHashCode can cause thread suspension, which would invalidate o if it moved. So
+ // we get the pretty type before we call IdentityHashCode.
+ const std::string pretty_type(PrettyTypeOf(o));
+ return StringPrintf("<0x%08x> (a %s)", o->IdentityHashCode(), pretty_type.c_str());
+ }
+}
+
+static void RemoveMonitors(Thread* self,
+ uintptr_t frame,
+ ReferenceTable* monitors,
+ std::vector<std::pair<uintptr_t, jobject>>* locked_objects)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ auto kept_end = std::remove_if(
+ locked_objects->begin(),
+ locked_objects->end(),
+ [self, frame, monitors](const std::pair<uintptr_t, jobject>& pair)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (frame == pair.first) {
+ mirror::Object* o = self->DecodeJObject(pair.second);
+ monitors->Remove(o);
+ return true;
+ }
+ return false;
+ });
+ locked_objects->erase(kept_end, locked_objects->end());
+}
+
+void JNIEnvExt::CheckMonitorRelease(jobject obj) {
+ uintptr_t current_frame = GetJavaCallFrame(self);
+ std::pair<uintptr_t, jobject> exact_pair = std::make_pair(current_frame, obj);
+ auto it = std::find(locked_objects_.begin(), locked_objects_.end(), exact_pair);
+ bool will_abort = false;
+ if (it != locked_objects_.end()) {
+ locked_objects_.erase(it);
+ } else {
+ // Check whether this monitor was locked in another JNI "session."
+ mirror::Object* mirror_obj = self->DecodeJObject(obj);
+ for (std::pair<uintptr_t, jobject>& pair : locked_objects_) {
+ if (self->DecodeJObject(pair.second) == mirror_obj) {
+ std::string monitor_descr = ComputeMonitorDescription(self, pair.second);
+ vm->JniAbortF("<JNI MonitorExit>",
+ "Unlocking monitor that wasn't locked here: %s",
+ monitor_descr.c_str());
+ will_abort = true;
+ break;
+ }
+ }
+ }
+
+ // When we abort, also make sure that any locks from the current "session" are removed from
+ // the monitors table, otherwise we may visit local objects in GC during abort (which won't be
+ // valid anymore).
+ if (will_abort) {
+ RemoveMonitors(self, current_frame, &monitors, &locked_objects_);
+ }
+}
+
+void JNIEnvExt::CheckNoHeldMonitors() {
+ uintptr_t current_frame = GetJavaCallFrame(self);
+ // The locked_objects_ are grouped by their stack frame component, as this enforces structured
+ // locking, and the groups form a stack. So the current frame entries are at the end. Check
+ // whether the vector is empty, and when there are elements, whether the last element belongs
+ // to this call - this signals that there are unlocked monitors.
+ if (!locked_objects_.empty()) {
+ std::pair<uintptr_t, jobject>& pair = locked_objects_[locked_objects_.size() - 1];
+ if (pair.first == current_frame) {
+ std::string monitor_descr = ComputeMonitorDescription(self, pair.second);
+ vm->JniAbortF("<JNI End>",
+ "Still holding a locked object on JNI end: %s",
+ monitor_descr.c_str());
+ // When we abort, also make sure that any locks from the current "session" are removed from
+ // the monitors table, otherwise we may visit local objects in GC during abort.
+ RemoveMonitors(self, current_frame, &monitors, &locked_objects_);
+ } else if (kIsDebugBuild) {
+ // Make sure there are really no other entries and our checking worked as expected.
+ for (std::pair<uintptr_t, jobject>& check_pair : locked_objects_) {
+ CHECK_NE(check_pair.first, current_frame);
+ }
+ }
+ }
+}
+
} // namespace art
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index 9b55536..3828ff0 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -43,8 +43,8 @@
void SetCheckJniEnabled(bool enabled);
- void PushFrame(int capacity);
- void PopFrame();
+ void PushFrame(int capacity) SHARED_REQUIRES(Locks::mutator_lock_);
+ void PopFrame() SHARED_REQUIRES(Locks::mutator_lock_);
template<typename T>
T AddLocalReference(mirror::Object* obj)
@@ -89,10 +89,27 @@
// Used by -Xcheck:jni.
const JNINativeInterface* unchecked_functions;
+ // Functions to keep track of monitor lock and unlock operations. Used to ensure proper locking
+ // rules in CheckJNI mode.
+
+ // Record locking of a monitor.
+ void RecordMonitorEnter(jobject obj) SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Check the release, that is, that the release is performed in the same JNI "segment."
+ void CheckMonitorRelease(jobject obj) SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Check that no monitors are held that have been acquired in this JNI "segment."
+ void CheckNoHeldMonitors() SHARED_REQUIRES(Locks::mutator_lock_);
+
private:
// The constructor should not be called directly. It may leave the object in an erronuous state,
// and the result needs to be checked.
JNIEnvExt(Thread* self, JavaVMExt* vm);
+
+ // All locked objects, with the (Java caller) stack frame that locked them. Used in CheckJNI
+ // to ensure that only monitors locked in this native frame are being unlocked, and that at
+ // the end all are unlocked.
+ std::vector<std::pair<uintptr_t, jobject>> locked_objects_;
};
// Used to save and restore the JNIEnvExt state when not going through code created by the JNI
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 2a0cb28..41b368e 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -607,11 +607,64 @@
EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
}
+ void SetUpForTest(bool direct, const char* method_name, const char* method_sig,
+ void* native_fnptr) {
+ // Initialize class loader and set generic JNI entrypoint.
+ // Note: this code is adapted from the jni_compiler_test, and taken with minimal modifications.
+ if (!runtime_->IsStarted()) {
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ class_loader_ = LoadDex("MyClassNatives");
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader_)));
+ mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader);
+ const auto pointer_size = class_linker_->GetImagePointerSize();
+ ArtMethod* method = direct ? c->FindDirectMethod(method_name, method_sig, pointer_size) :
+ c->FindVirtualMethod(method_name, method_sig, pointer_size);
+ ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig;
+ method->SetEntryPointFromQuickCompiledCode(class_linker_->GetRuntimeQuickGenericJniStub());
+ }
+ // Start runtime.
+ Thread::Current()->TransitionFromSuspendedToRunnable();
+ bool started = runtime_->Start();
+ CHECK(started);
+ }
+ // JNI operations after runtime start.
+ env_ = Thread::Current()->GetJniEnv();
+ jklass_ = env_->FindClass("MyClassNatives");
+ ASSERT_TRUE(jklass_ != nullptr) << method_name << " " << method_sig;
+
+ if (direct) {
+ jmethod_ = env_->GetStaticMethodID(jklass_, method_name, method_sig);
+ } else {
+ jmethod_ = env_->GetMethodID(jklass_, method_name, method_sig);
+ }
+ ASSERT_TRUE(jmethod_ != nullptr) << method_name << " " << method_sig;
+
+ if (native_fnptr != nullptr) {
+ JNINativeMethod methods[] = { { method_name, method_sig, native_fnptr } };
+ ASSERT_EQ(JNI_OK, env_->RegisterNatives(jklass_, methods, 1))
+ << method_name << " " << method_sig;
+ } else {
+ env_->UnregisterNatives(jklass_);
+ }
+
+ jmethodID constructor = env_->GetMethodID(jklass_, "<init>", "()V");
+ jobj_ = env_->NewObject(jklass_, constructor);
+ ASSERT_TRUE(jobj_ != nullptr) << method_name << " " << method_sig;
+ }
+
JavaVMExt* vm_;
JNIEnv* env_;
jclass aioobe_;
jclass ase_;
jclass sioobe_;
+
+ jclass jklass_;
+ jobject jobj_;
+ jobject class_loader_;
+ jmethodID jmethod_;
};
TEST_F(JniInternalTest, AllocObject) {
@@ -2111,4 +2164,38 @@
}
}
+void Java_MyClassNatives_foo_exit(JNIEnv* env, jobject thisObj) {
+ // Release the monitor on self. This should trigger an abort.
+ env->MonitorExit(thisObj);
+}
+
+TEST_F(JniInternalTest, MonitorExitLockedInDifferentCall) {
+ SetUpForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClassNatives_foo_exit));
+ ASSERT_NE(jobj_, nullptr);
+
+ env_->MonitorEnter(jobj_);
+ EXPECT_FALSE(env_->ExceptionCheck());
+
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
+ check_jni_abort_catcher.Check("Unlocking monitor that wasn't locked here");
+}
+
+void Java_MyClassNatives_foo_enter_no_exit(JNIEnv* env, jobject thisObj) {
+ // Acquire but don't release the monitor on self. This should trigger an abort on return.
+ env->MonitorEnter(thisObj);
+}
+
+TEST_F(JniInternalTest, MonitorExitNotAllUnlocked) {
+ SetUpForTest(false,
+ "foo",
+ "()V",
+ reinterpret_cast<void*>(&Java_MyClassNatives_foo_enter_no_exit));
+ ASSERT_NE(jobj_, nullptr);
+
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
+ check_jni_abort_catcher.Check("Still holding a locked object on JNI end");
+}
+
} // namespace art
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 3b84bfa..4aebc2c 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -192,28 +192,38 @@
}
}
-static void DexFile_closeDexFile(JNIEnv* env, jclass, jobject cookie) {
- std::unique_ptr<std::vector<const DexFile*>> dex_files = ConvertJavaArrayToNative(env, cookie);
- if (dex_files.get() == nullptr) {
- DCHECK(env->ExceptionCheck());
- return;
- }
-
+static jboolean DexFile_closeDexFile(JNIEnv* env, jclass, jobject cookie) {
ScopedObjectAccess soa(env);
+ mirror::Object* dex_files_object = soa.Decode<mirror::Object*>(cookie);
+ if (dex_files_object == nullptr) {
+ ThrowNullPointerException("cookie == null");
+ return JNI_FALSE;
+ }
+ mirror::LongArray* dex_files = dex_files_object->AsLongArray();
- // The Runtime currently never unloads classes, which means any registered
- // dex files must be kept around forever in case they are used. We
- // accomplish this here by explicitly leaking those dex files that are
- // registered.
- //
- // TODO: The Runtime should support unloading of classes and freeing of the
- // dex files for those unloaded classes rather than leaking dex files here.
+ // Delete dex files associated with this dalvik.system.DexFile since there should not be running
+ // code using it. dex_files is a vector due to multidex.
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- for (const DexFile* dex_file : *dex_files) {
+ bool all_deleted = true;
+ for (int32_t i = 0, count = dex_files->GetLength(); i < count; ++i) {
+ auto* dex_file = reinterpret_cast<DexFile*>(dex_files->Get(i));
+ if (dex_file == nullptr) {
+ continue;
+ }
+ // Only delete the dex file if the dex cache is not found to prevent runtime crashes if there
+ // are calls to DexFile.close while the ART DexFile is still in use.
if (class_linker->FindDexCache(soa.Self(), *dex_file, true) == nullptr) {
+ // Clear the element in the array so that we can call close again.
+ dex_files->Set(i, 0);
delete dex_file;
+ } else {
+ all_deleted = false;
}
}
+
+ // TODO: Also unmap the OatFile for this dalvik.system.DexFile.
+
+ return all_deleted ? JNI_TRUE : JNI_FALSE;
}
static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,
@@ -379,7 +389,7 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)V"),
+ NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
NATIVE_METHOD(DexFile, defineClassNative,
"(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Object;)Ljava/lang/Class;"),
NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 8fd6849..5da15df 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -229,6 +229,65 @@
return nullptr;
}
+static mirror::Field* GetPublicFieldRecursive(
+ Thread* self, mirror::Class* clazz, mirror::String* name)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(clazz != nullptr);
+ DCHECK(name != nullptr);
+ DCHECK(self != nullptr);
+
+ StackHandleScope<1> hs(self);
+ MutableHandle<mirror::Class> h_clazz(hs.NewHandle(clazz));
+
+ // We search the current class, its direct interfaces then its superclass.
+ while (h_clazz.Get() != nullptr) {
+ mirror::Field* result = GetDeclaredField(self, h_clazz.Get(), name);
+ if ((result != nullptr) && (result->GetAccessFlags() & kAccPublic)) {
+ return result;
+ } else if (UNLIKELY(self->IsExceptionPending())) {
+ // Something went wrong. Bail out.
+ return nullptr;
+ }
+
+ uint32_t num_direct_interfaces = h_clazz->NumDirectInterfaces();
+ for (uint32_t i = 0; i < num_direct_interfaces; i++) {
+ mirror::Class *iface = mirror::Class::GetDirectInterface(self, h_clazz, i);
+ if (UNLIKELY(iface == nullptr)) {
+ self->AssertPendingException();
+ return nullptr;
+ }
+ result = GetPublicFieldRecursive(self, iface, name);
+ if (result != nullptr) {
+ DCHECK(result->GetAccessFlags() & kAccPublic);
+ return result;
+ } else if (UNLIKELY(self->IsExceptionPending())) {
+ // Something went wrong. Bail out.
+ return nullptr;
+ }
+ }
+
+ // We don't try the superclass if we are an interface.
+ if (h_clazz->IsInterface()) {
+ break;
+ }
+
+ // Get the next class.
+ h_clazz.Assign(h_clazz->GetSuperClass());
+ }
+ return nullptr;
+}
+
+static jobject Class_getPublicFieldRecursive(JNIEnv* env, jobject javaThis, jstring name) {
+ ScopedFastNativeObjectAccess soa(env);
+ auto* name_string = soa.Decode<mirror::String*>(name);
+ if (UNLIKELY(name_string == nullptr)) {
+ ThrowNullPointerException("name == null");
+ return nullptr;
+ }
+ return soa.AddLocalReference<jobject>(
+ GetPublicFieldRecursive(soa.Self(), DecodeClass(soa, javaThis), name_string));
+}
+
static jobject Class_getDeclaredFieldInternal(JNIEnv* env, jobject javaThis, jstring name) {
ScopedFastNativeObjectAccess soa(env);
auto* name_string = soa.Decode<mirror::String*>(name);
@@ -678,6 +737,7 @@
"!([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;"),
NATIVE_METHOD(Class, getDeclaredConstructorsInternal, "!(Z)[Ljava/lang/reflect/Constructor;"),
NATIVE_METHOD(Class, getDeclaredField, "!(Ljava/lang/String;)Ljava/lang/reflect/Field;"),
+ NATIVE_METHOD(Class, getPublicFieldRecursive, "!(Ljava/lang/String;)Ljava/lang/reflect/Field;"),
NATIVE_METHOD(Class, getDeclaredFieldInternal, "!(Ljava/lang/String;)Ljava/lang/reflect/Field;"),
NATIVE_METHOD(Class, getDeclaredFields, "!()[Ljava/lang/reflect/Field;"),
NATIVE_METHOD(Class, getDeclaredFieldsUnchecked, "!(Z)[Ljava/lang/reflect/Field;"),
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index e1e9ceb..45b9484 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -103,10 +103,17 @@
// If caller is null, then we called from JNI, just avoid the check since JNI avoids most
// access checks anyways. TODO: Investigate if this the correct behavior.
if (caller != nullptr && !caller->CanAccess(c.Get())) {
- soa.Self()->ThrowNewExceptionF(
- "Ljava/lang/IllegalAccessException;", "%s is not accessible from %s",
- PrettyClass(c.Get()).c_str(), PrettyClass(caller).c_str());
- return nullptr;
+ if (PrettyDescriptor(c.Get()) == "dalvik.system.DexPathList$Element") {
+ // b/20699073.
+ LOG(WARNING) << "The dalvik.system.DexPathList$Element constructor is not accessible by "
+ "default. This is a temporary workaround for backwards compatibility "
+ "with class-loader hacks. Please update your application.";
+ } else {
+ soa.Self()->ThrowNewExceptionF(
+ "Ljava/lang/IllegalAccessException;", "%s is not accessible from %s",
+ PrettyClass(c.Get()).c_str(), PrettyClass(caller).c_str());
+ return nullptr;
+ }
}
}
if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(soa.Self(), c, true, true)) {
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index daae401..85ac4aa 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -62,8 +62,10 @@
if (heap != nullptr && heap->GetReadBarrierTable()->IsSet(old_ref)) {
ref = reinterpret_cast<MirrorType*>(Mark(old_ref));
// Update the field atomically. This may fail if mutator updates before us, but it's ok.
- obj->CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier<false, false>(
- offset, old_ref, ref);
+ if (ref != old_ref) {
+ obj->CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier<false, false>(
+ offset, old_ref, ref);
+ }
}
AssertToSpaceInvariant(obj, offset, ref);
return ref;
@@ -90,17 +92,17 @@
// To be implemented.
return ref;
} else if (with_read_barrier && kUseTableLookupReadBarrier) {
- if (kMaybeDuringStartup && IsDuringStartup()) {
- // During startup, the heap may not be initialized yet. Just
- // return the given ref.
- return ref;
- }
- if (Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) {
+ Thread* self = Thread::Current();
+ if (self != nullptr &&
+ self->GetIsGcMarking() &&
+ Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) {
MirrorType* old_ref = ref;
ref = reinterpret_cast<MirrorType*>(Mark(old_ref));
// Update the field atomically. This may fail if mutator updates before us, but it's ok.
- Atomic<mirror::Object*>* atomic_root = reinterpret_cast<Atomic<mirror::Object*>*>(root);
- atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, ref);
+ if (ref != old_ref) {
+ Atomic<mirror::Object*>* atomic_root = reinterpret_cast<Atomic<mirror::Object*>*>(root);
+ atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, ref);
+ }
}
AssertToSpaceInvariant(gc_root_source, ref);
return ref;
@@ -127,19 +129,19 @@
// To be implemented.
return ref;
} else if (with_read_barrier && kUseTableLookupReadBarrier) {
- if (kMaybeDuringStartup && IsDuringStartup()) {
- // During startup, the heap may not be initialized yet. Just
- // return the given ref.
- return ref;
- }
- if (Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) {
+ Thread* self = Thread::Current();
+ if (self != nullptr &&
+ self->GetIsGcMarking() &&
+ Runtime::Current()->GetHeap()->GetReadBarrierTable()->IsSet(ref)) {
auto old_ref = mirror::CompressedReference<MirrorType>::FromMirrorPtr(ref);
ref = reinterpret_cast<MirrorType*>(Mark(ref));
auto new_ref = mirror::CompressedReference<MirrorType>::FromMirrorPtr(ref);
// Update the field atomically. This may fail if mutator updates before us, but it's ok.
- auto* atomic_root =
- reinterpret_cast<Atomic<mirror::CompressedReference<MirrorType>>*>(root);
- atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, new_ref);
+ if (new_ref.AsMirrorPtr() != old_ref.AsMirrorPtr()) {
+ auto* atomic_root =
+ reinterpret_cast<Atomic<mirror::CompressedReference<MirrorType>>*>(root);
+ atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, new_ref);
+ }
}
AssertToSpaceInvariant(gc_root_source, ref);
return ref;
diff --git a/runtime/read_barrier_c.h b/runtime/read_barrier_c.h
index 710c21f..8e5b187 100644
--- a/runtime/read_barrier_c.h
+++ b/runtime/read_barrier_c.h
@@ -26,10 +26,16 @@
// table-lookup read barriers.
#ifdef ART_USE_READ_BARRIER
+#if ART_READ_BARRIER_TYPE_IS_BAKER
#define USE_BAKER_READ_BARRIER
-// #define USE_BROOKS_READ_BARRIER
-// #define USE_TABLE_LOOKUP_READ_BARRIER
+#elif ART_READ_BARRIER_TYPE_IS_BROOKS
+#define USE_BROOKS_READ_BARRIER
+#elif ART_READ_BARRIER_TYPE_IS_TABLELOOKUP
+#define USE_TABLE_LOOKUP_READ_BARRIER
+#else
+#error "ART read barrier type must be set"
#endif
+#endif // ART_USE_READ_BARRIER
#ifdef ART_HEAP_POISONING
#define USE_HEAP_POISONING
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 7f72f8a..1d21a64 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -110,7 +110,7 @@
}
InlineInfo StackVisitor::GetCurrentInlineInfo() const {
- ArtMethod* outer_method = *GetCurrentQuickFrame();
+ ArtMethod* outer_method = GetOuterMethod();
uint32_t native_pc_offset = outer_method->NativeQuickPcOffset(cur_quick_frame_pc_);
CodeInfo code_info = outer_method->GetOptimizedCodeInfo();
StackMapEncoding encoding = code_info.ExtractEncoding();
@@ -194,11 +194,12 @@
}
bool StackVisitor::IsReferenceVReg(ArtMethod* m, uint16_t vreg) {
+ DCHECK_EQ(m, GetMethod());
// Process register map (which native and runtime methods don't have)
if (m->IsNative() || m->IsRuntimeMethod() || m->IsProxyMethod()) {
return false;
}
- if (m->IsOptimized(sizeof(void*))) {
+ if (GetOuterMethod()->IsOptimized(sizeof(void*))) {
return true; // TODO: Implement.
}
const uint8_t* native_gc_map = m->GetNativeGcMap(sizeof(void*));
@@ -251,7 +252,7 @@
if (GetVRegFromDebuggerShadowFrame(vreg, kind, val)) {
return true;
}
- if (m->IsOptimized(sizeof(void*))) {
+ if (GetOuterMethod()->IsOptimized(sizeof(void*))) {
return GetVRegFromOptimizedCode(m, vreg, kind, val);
} else {
return GetVRegFromQuickCode(m, vreg, kind, val);
@@ -288,15 +289,15 @@
bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKind kind,
uint32_t* val) const {
+ ArtMethod* outer_method = GetOuterMethod();
+ const void* code_pointer = outer_method->GetQuickOatCodePointer(sizeof(void*));
+ DCHECK(code_pointer != nullptr);
DCHECK_EQ(m, GetMethod());
const DexFile::CodeItem* code_item = m->GetCodeItem();
DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be null or how would we compile
// its instructions?
uint16_t number_of_dex_registers = code_item->registers_size_;
DCHECK_LT(vreg, code_item->registers_size_);
- ArtMethod* outer_method = *GetCurrentQuickFrame();
- const void* code_pointer = outer_method->GetQuickOatCodePointer(sizeof(void*));
- DCHECK(code_pointer != nullptr);
CodeInfo code_info = outer_method->GetOptimizedCodeInfo();
StackMapEncoding encoding = code_info.ExtractEncoding();
@@ -405,7 +406,7 @@
if (cur_quick_frame_ != nullptr) {
DCHECK(context_ != nullptr); // You can't reliably read registers without a context.
DCHECK(m == GetMethod());
- if (m->IsOptimized(sizeof(void*))) {
+ if (GetOuterMethod()->IsOptimized(sizeof(void*))) {
return GetVRegPairFromOptimizedCode(m, vreg, kind_lo, kind_hi, val);
} else {
return GetVRegPairFromQuickCode(m, vreg, kind_lo, kind_hi, val);
@@ -481,7 +482,7 @@
if (cur_quick_frame_ != nullptr) {
DCHECK(context_ != nullptr); // You can't reliably write registers without a context.
DCHECK(m == GetMethod());
- if (m->IsOptimized(sizeof(void*))) {
+ if (GetOuterMethod()->IsOptimized(sizeof(void*))) {
return false;
} else {
return SetVRegFromQuickCode(m, vreg, new_value, kind);
@@ -590,7 +591,7 @@
if (cur_quick_frame_ != nullptr) {
DCHECK(context_ != nullptr); // You can't reliably write registers without a context.
DCHECK(m == GetMethod());
- if (m->IsOptimized(sizeof(void*))) {
+ if (GetOuterMethod()->IsOptimized(sizeof(void*))) {
return false;
} else {
return SetVRegPairFromQuickCode(m, vreg, new_value, kind_lo, kind_hi);
@@ -724,14 +725,14 @@
uintptr_t StackVisitor::GetReturnPc() const {
uint8_t* sp = reinterpret_cast<uint8_t*>(GetCurrentQuickFrame());
DCHECK(sp != nullptr);
- uint8_t* pc_addr = sp + GetMethod()->GetReturnPcOffset().SizeValue();
+ uint8_t* pc_addr = sp + GetOuterMethod()->GetReturnPcOffset().SizeValue();
return *reinterpret_cast<uintptr_t*>(pc_addr);
}
void StackVisitor::SetReturnPc(uintptr_t new_ret_pc) {
uint8_t* sp = reinterpret_cast<uint8_t*>(GetCurrentQuickFrame());
CHECK(sp != nullptr);
- uint8_t* pc_addr = sp + GetMethod()->GetReturnPcOffset().SizeValue();
+ uint8_t* pc_addr = sp + GetOuterMethod()->GetReturnPcOffset().SizeValue();
*reinterpret_cast<uintptr_t*>(pc_addr) = new_ret_pc;
}
diff --git a/runtime/stack.h b/runtime/stack.h
index 292c745..31acf0e 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -473,6 +473,10 @@
ArtMethod* GetMethod() const SHARED_REQUIRES(Locks::mutator_lock_);
+ ArtMethod* GetOuterMethod() const {
+ return *GetCurrentQuickFrame();
+ }
+
bool IsShadowFrame() const {
return cur_shadow_frame_ != nullptr;
}
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index b3dd2eb..f5d20bd 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -118,11 +118,8 @@
}
}
-inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) {
- AssertThreadSuspensionIsAllowable();
+inline void Thread::TransitionToSuspendedAndRunCheckpoints(ThreadState new_state) {
DCHECK_NE(new_state, kRunnable);
- DCHECK_EQ(this, Thread::Current());
- // Change to non-runnable state, thereby appearing suspended to the system.
DCHECK_EQ(GetState(), kRunnable);
union StateAndFlags old_state_and_flags;
union StateAndFlags new_state_and_flags;
@@ -145,12 +142,9 @@
break;
}
}
+}
- // Change to non-runnable state, thereby appearing suspended to the system.
- // Mark the release of the share of the mutator_lock_.
- Locks::mutator_lock_->TransitionFromRunnableToSuspended(this);
-
- // Once suspended - check the active suspend barrier flag
+inline void Thread::PassActiveSuspendBarriers() {
while (true) {
uint16_t current_flags = tls32_.state_and_flags.as_struct.flags;
if (LIKELY((current_flags & (kCheckpointRequest | kActiveSuspendBarrier)) == 0)) {
@@ -159,11 +153,22 @@
PassActiveSuspendBarriers(this);
} else {
// Impossible
- LOG(FATAL) << "Fatal, thread transited into suspended without running the checkpoint";
+ LOG(FATAL) << "Fatal, thread transitioned into suspended without running the checkpoint";
}
}
}
+inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) {
+ AssertThreadSuspensionIsAllowable();
+ DCHECK_EQ(this, Thread::Current());
+ // Change to non-runnable state, thereby appearing suspended to the system.
+ TransitionToSuspendedAndRunCheckpoints(new_state);
+ // Mark the release of the share of the mutator_lock_.
+ Locks::mutator_lock_->TransitionFromRunnableToSuspended(this);
+ // Once suspended - check the active suspend barrier flag
+ PassActiveSuspendBarriers();
+}
+
inline ThreadState Thread::TransitionFromSuspendedToRunnable() {
union StateAndFlags old_state_and_flags;
old_state_and_flags.as_int = tls32_.state_and_flags.as_int;
diff --git a/runtime/thread.h b/runtime/thread.h
index d21644d..d262c62 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -247,17 +247,15 @@
SHARED_REQUIRES(Locks::mutator_lock_);
// Transition from non-runnable to runnable state acquiring share on mutator_lock_.
- ThreadState TransitionFromSuspendedToRunnable()
+ ALWAYS_INLINE ThreadState TransitionFromSuspendedToRunnable()
REQUIRES(!Locks::thread_suspend_count_lock_)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
- ALWAYS_INLINE;
+ SHARED_LOCK_FUNCTION(Locks::mutator_lock_);
// Transition from runnable into a state where mutator privileges are denied. Releases share of
// mutator lock.
- void TransitionFromRunnableToSuspended(ThreadState new_state)
+ ALWAYS_INLINE void TransitionFromRunnableToSuspended(ThreadState new_state)
REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_)
- UNLOCK_FUNCTION(Locks::mutator_lock_)
- ALWAYS_INLINE;
+ UNLOCK_FUNCTION(Locks::mutator_lock_);
// Once called thread suspension will cause an assertion failure.
const char* StartAssertNoThreadSuspension(const char* cause) ACQUIRE(Roles::uninterruptible_) {
@@ -1017,11 +1015,15 @@
// Dbg::Disconnected.
ThreadState SetStateUnsafe(ThreadState new_state) {
ThreadState old_state = GetState();
- tls32_.state_and_flags.as_struct.state = new_state;
- // if transit to a suspended state, check the pass barrier request.
- if (UNLIKELY((new_state != kRunnable) &&
- (tls32_.state_and_flags.as_struct.flags & kActiveSuspendBarrier))) {
- PassActiveSuspendBarriers(this);
+ if (old_state == kRunnable && new_state != kRunnable) {
+ // Need to run pending checkpoint and suspend barriers. Run checkpoints in runnable state in
+ // case they need to use a ScopedObjectAccess. If we are holding the mutator lock and a SOA
+ // attempts to TransitionFromSuspendedToRunnable, it results in a deadlock.
+ TransitionToSuspendedAndRunCheckpoints(new_state);
+ // Since we transitioned to a suspended state, check the pass barrier requests.
+ PassActiveSuspendBarriers();
+ } else {
+ tls32_.state_and_flags.as_struct.state = new_state;
}
return old_state;
}
@@ -1064,6 +1066,12 @@
void SetUpAlternateSignalStack();
void TearDownAlternateSignalStack();
+ ALWAYS_INLINE void TransitionToSuspendedAndRunCheckpoints(ThreadState new_state)
+ REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_);
+
+ ALWAYS_INLINE void PassActiveSuspendBarriers()
+ REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_);
+
// 32 bits of atomically changed state and flags. Keeping as 32 bits allows and atomic CAS to
// change from being Suspended to Runnable without a suspend request occurring.
union PACKED(4) StateAndFlags {
diff --git a/runtime/trace.cc b/runtime/trace.cc
index e2743ce..745aa63 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -31,6 +31,7 @@
#include "common_throws.h"
#include "debugger.h"
#include "dex_file-inl.h"
+#include "gc/scoped_gc_critical_section.h"
#include "instrumentation.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache-inl.h"
@@ -350,6 +351,10 @@
// Create Trace object.
{
+ // Required since EnableMethodTracing calls ConfigureStubs which visits class linker classes.
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseInstrumentation,
+ gc::kCollectorTypeInstrumentation);
ScopedSuspendAll ssa(__FUNCTION__);
MutexLock mu(self, *Locks::trace_lock_);
if (the_trace_ != nullptr) {
@@ -464,9 +469,10 @@
Runtime* runtime = Runtime::Current();
Trace* the_trace = nullptr;
+ Thread* const self = Thread::Current();
pthread_t sampling_pthread = 0U;
{
- MutexLock mu(Thread::Current(), *Locks::trace_lock_);
+ MutexLock mu(self, *Locks::trace_lock_);
if (the_trace_ == nullptr) {
LOG(ERROR) << "Trace pause requested, but no trace currently running";
return;
@@ -478,23 +484,26 @@
if (sampling_pthread != 0U) {
{
- MutexLock mu(Thread::Current(), *Locks::trace_lock_);
+ MutexLock mu(self, *Locks::trace_lock_);
the_trace_ = nullptr;
}
CHECK_PTHREAD_CALL(pthread_join, (sampling_pthread, nullptr), "sampling thread shutdown");
sampling_pthread_ = 0U;
{
- MutexLock mu(Thread::Current(), *Locks::trace_lock_);
+ MutexLock mu(self, *Locks::trace_lock_);
the_trace_ = the_trace;
}
}
if (the_trace != nullptr) {
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseInstrumentation,
+ gc::kCollectorTypeInstrumentation);
ScopedSuspendAll ssa(__FUNCTION__);
stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0;
if (the_trace->trace_mode_ == TraceMode::kSampling) {
- MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ MutexLock mu(self, *Locks::thread_list_lock_);
runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);
} else {
runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
@@ -530,6 +539,9 @@
bool enable_stats = (the_trace->flags_ && kTraceCountAllocs) != 0;
{
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseInstrumentation,
+ gc::kCollectorTypeInstrumentation);
ScopedSuspendAll ssa(__FUNCTION__);
// Reenable.
@@ -1046,4 +1058,9 @@
return the_trace_->buffer_size_;
}
+bool Trace::IsTracingEnabled() {
+ MutexLock mu(Thread::Current(), *Locks::trace_lock_);
+ return the_trace_ != nullptr;
+}
+
} // namespace art
diff --git a/runtime/trace.h b/runtime/trace.h
index 87a691d..356a81f 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -183,6 +183,9 @@
static TraceMode GetMode() REQUIRES(!Locks::trace_lock_);
static size_t GetBufferSize() REQUIRES(!Locks::trace_lock_);
+ // Used by class linker to prevent class unloading.
+ static bool IsTracingEnabled() REQUIRES(!Locks::trace_lock_);
+
private:
Trace(File* trace_file, const char* trace_name, size_t buffer_size, int flags,
TraceOutputMode output_mode, TraceMode trace_mode);
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index dd88db0..ee3a3b9 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -120,7 +120,7 @@
private static void testRemoveLocalObject() {
removeLocalObject(new Object());
}
-
+
private static native short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
short s8, short s9, short s10);
diff --git a/test/024-illegal-access/expected.txt b/test/024-illegal-access/expected.txt
index 5f951f4..0ae4a77 100644
--- a/test/024-illegal-access/expected.txt
+++ b/test/024-illegal-access/expected.txt
@@ -1,2 +1,5 @@
Got expected failure 1
Got expected failure 2
+Got expected failure 3
+Got expected failure 4
+Got expected failure 5
diff --git a/test/024-illegal-access/src/Main.java b/test/024-illegal-access/src/Main.java
index bde73e9..84c7114 100644
--- a/test/024-illegal-access/src/Main.java
+++ b/test/024-illegal-access/src/Main.java
@@ -17,7 +17,7 @@
public class Main {
static public void main(String[] args) {
try {
- PublicAccess.main();
+ PublicAccess.accessStaticField();
System.err.println("ERROR: call 1 not expected to succeed");
} catch (VerifyError ve) {
// dalvik
@@ -28,14 +28,41 @@
}
try {
- CheckInstanceof.main(new Object());
+ PublicAccess.accessStaticMethod();
System.err.println("ERROR: call 2 not expected to succeed");
- } catch (VerifyError ve) {
- // dalvik
- System.out.println("Got expected failure 2");
} catch (IllegalAccessError iae) {
// reference
System.out.println("Got expected failure 2");
}
+
+ try {
+ PublicAccess.accessInstanceField();
+ System.err.println("ERROR: call 3 not expected to succeed");
+ } catch (VerifyError ve) {
+ // dalvik
+ System.out.println("Got expected failure 3");
+ } catch (IllegalAccessError iae) {
+ // reference
+ System.out.println("Got expected failure 3");
+ }
+
+ try {
+ PublicAccess.accessInstanceMethod();
+ System.err.println("ERROR: call 4 not expected to succeed");
+ } catch (IllegalAccessError iae) {
+ // reference
+ System.out.println("Got expected failure 4");
+ }
+
+ try {
+ CheckInstanceof.main(new Object());
+ System.err.println("ERROR: call 5 not expected to succeed");
+ } catch (VerifyError ve) {
+ // dalvik
+ System.out.println("Got expected failure 5");
+ } catch (IllegalAccessError iae) {
+ // reference
+ System.out.println("Got expected failure 5");
+ }
}
}
diff --git a/test/024-illegal-access/src/PublicAccess.java b/test/024-illegal-access/src/PublicAccess.java
index 4e72cd4..e3fef85 100644
--- a/test/024-illegal-access/src/PublicAccess.java
+++ b/test/024-illegal-access/src/PublicAccess.java
@@ -18,8 +18,20 @@
* Some stuff for access checks.
*/
public class PublicAccess {
- public static void main() {
- String shouldFail = SemiPrivate.mPrivvy;
+ public static void accessStaticField() {
+ String shouldFail = SemiPrivate.mStaticPrivvy;
+ System.out.println("Got " + shouldFail);
+ }
+ public static void accessStaticMethod() {
+ String shouldFail = SemiPrivate.privvyStaticMethod();
+ System.out.println("Got " + shouldFail);
+ }
+ public static void accessInstanceField() {
+ String shouldFail = new SemiPrivate().mInstancePrivvy;
+ System.out.println("Got " + shouldFail);
+ }
+ public static void accessInstanceMethod() {
+ String shouldFail = new SemiPrivate().privvyInstanceMethod();
System.out.println("Got " + shouldFail);
}
}
diff --git a/test/024-illegal-access/src/SemiPrivate.java b/test/024-illegal-access/src/SemiPrivate.java
index 06b16c4..62e0d05 100644
--- a/test/024-illegal-access/src/SemiPrivate.java
+++ b/test/024-illegal-access/src/SemiPrivate.java
@@ -18,5 +18,15 @@
* Version with package scope access.
*/
public class SemiPrivate {
- /* not private */ static String mPrivvy = "stuff";
+ /* not private */ static String mStaticPrivvy = "stuff";
+
+ /* not private */ static String privvyStaticMethod() {
+ return "stuff";
+ }
+
+ /* not private */ String mInstancePrivvy = "stuff";
+
+ /* not private */ String privvyInstanceMethod() {
+ return "stuff";
+ }
}
diff --git a/test/024-illegal-access/src2/SemiPrivate.java b/test/024-illegal-access/src2/SemiPrivate.java
index 064265a..4f36a07 100644
--- a/test/024-illegal-access/src2/SemiPrivate.java
+++ b/test/024-illegal-access/src2/SemiPrivate.java
@@ -18,5 +18,15 @@
* Version with private access.
*/
public class SemiPrivate {
- private static String mPrivvy = "stuff";
+ private static String mStaticPrivvy = "stuff";
+
+ private static String privvyStaticMethod() {
+ return "stuff";
+ }
+
+ private String mInstancePrivvy = "stuff";
+
+ private String privvyInstanceMethod() {
+ return "stuff";
+ }
}
diff --git a/test/087-gc-after-link/src/Main.java b/test/087-gc-after-link/src/Main.java
index e0a187a..2f6d496 100644
--- a/test/087-gc-after-link/src/Main.java
+++ b/test/087-gc-after-link/src/Main.java
@@ -155,6 +155,12 @@
* See if we can GC after a failed load.
*/
static void testFailLoadAndGc() throws TestFailed {
+ processFailLoadAndGc();
+ Runtime.getRuntime().gc();
+ System.out.println("GC complete.");
+ }
+
+ private static void processFailLoadAndGc() throws TestFailed {
try {
BrokenDexLoader loader;
@@ -170,7 +176,5 @@
ite.printStackTrace();
}
}
- Runtime.getRuntime().gc();
- System.out.println("GC complete.");
}
}
diff --git a/test/088-monitor-verification/smali/NotStructuredOverUnlock.smali b/test/088-monitor-verification/smali/NotStructuredOverUnlock.smali
index aa0c2d5..0dc492f 100644
--- a/test/088-monitor-verification/smali/NotStructuredOverUnlock.smali
+++ b/test/088-monitor-verification/smali/NotStructuredOverUnlock.smali
@@ -5,7 +5,7 @@
.method public static run(Ljava/lang/Object;)V
.registers 3
- invoke-static {}, LMain;->assertCallerIsInterpreted()V
+ invoke-static {}, LMain;->assertIsInterpreted()V
# Lock twice, but unlock thrice.
diff --git a/test/088-monitor-verification/smali/NotStructuredUnderUnlock.smali b/test/088-monitor-verification/smali/NotStructuredUnderUnlock.smali
index 2c31fda..df6e168 100644
--- a/test/088-monitor-verification/smali/NotStructuredUnderUnlock.smali
+++ b/test/088-monitor-verification/smali/NotStructuredUnderUnlock.smali
@@ -5,7 +5,7 @@
.method public static run(Ljava/lang/Object;)V
.registers 3
- invoke-static {}, LMain;->assertCallerIsInterpreted()V
+ invoke-static {}, LMain;->assertIsInterpreted()V
# Lock thrice, but only unlock twice.
diff --git a/test/088-monitor-verification/smali/OK.smali b/test/088-monitor-verification/smali/OK.smali
index 596798d..a43ecb0 100644
--- a/test/088-monitor-verification/smali/OK.smali
+++ b/test/088-monitor-verification/smali/OK.smali
@@ -20,7 +20,7 @@
.method public static runNoMonitors(Ljava/lang/Object;Ljava/lang/Object;)V
.registers 3
- invoke-static {}, LMain;->assertCallerIsManaged()V
+ invoke-static {}, LMain;->assertIsManaged()V
return-void
@@ -29,7 +29,7 @@
.method public static runStraightLine(Ljava/lang/Object;Ljava/lang/Object;)V
.registers 3
- invoke-static {}, LMain;->assertCallerIsManaged()V
+ invoke-static {}, LMain;->assertIsManaged()V
monitor-enter v1 # 1
monitor-enter v2 # 2
@@ -44,7 +44,7 @@
.method public static runBalancedJoin(Ljava/lang/Object;Ljava/lang/Object;)V
.registers 3
- invoke-static {}, LMain;->assertCallerIsManaged()V
+ invoke-static {}, LMain;->assertIsManaged()V
monitor-enter v1 # 1
diff --git a/test/088-monitor-verification/smali/TooDeep.smali b/test/088-monitor-verification/smali/TooDeep.smali
index 1a8f2f0..a1e3281 100644
--- a/test/088-monitor-verification/smali/TooDeep.smali
+++ b/test/088-monitor-verification/smali/TooDeep.smali
@@ -7,7 +7,7 @@
# Lock depth is 33, which is more than the verifier supports. This should have been punted to
# the interpreter.
- invoke-static {}, LMain;->assertCallerIsInterpreted()V
+ invoke-static {}, LMain;->assertIsInterpreted()V
monitor-enter v2 # 1
monitor-enter v2 # 2
diff --git a/test/088-monitor-verification/smali/UnbalancedJoin.smali b/test/088-monitor-verification/smali/UnbalancedJoin.smali
index da8f773..993f32c 100644
--- a/test/088-monitor-verification/smali/UnbalancedJoin.smali
+++ b/test/088-monitor-verification/smali/UnbalancedJoin.smali
@@ -5,7 +5,7 @@
.method public static run(Ljava/lang/Object;Ljava/lang/Object;)V
.registers 3
- invoke-static {}, LMain;->assertCallerIsInterpreted()V
+ invoke-static {}, LMain;->assertIsInterpreted()V
if-eqz v2, :Lnull
diff --git a/test/088-monitor-verification/smali/UnbalancedStraight.smali b/test/088-monitor-verification/smali/UnbalancedStraight.smali
index 68edb6c..cbb8bcc 100644
--- a/test/088-monitor-verification/smali/UnbalancedStraight.smali
+++ b/test/088-monitor-verification/smali/UnbalancedStraight.smali
@@ -5,7 +5,7 @@
.method public static run(Ljava/lang/Object;Ljava/lang/Object;)V
.registers 3
- invoke-static {}, LMain;->assertCallerIsInterpreted()V
+ invoke-static {}, LMain;->assertIsInterpreted()V
monitor-enter v1 # 1
monitor-enter v2 # 2
diff --git a/test/088-monitor-verification/src/Main.java b/test/088-monitor-verification/src/Main.java
index fc5755b..2188055 100644
--- a/test/088-monitor-verification/src/Main.java
+++ b/test/088-monitor-verification/src/Main.java
@@ -220,7 +220,7 @@
// Smali testing code.
private static void runSmaliTests() {
- if (!hasOatFile() || runtimeIsSoftFail() || isCallerInterpreted()) {
+ if (!hasOatFile() || runtimeIsSoftFail() || isInterpreted()) {
// Skip test, this seems to be a non-compiled code test configuration.
return;
}
@@ -277,9 +277,9 @@
}
// Helpers for the smali code.
- public static native void assertCallerIsInterpreted();
- public static native void assertCallerIsManaged();
+ public static native void assertIsInterpreted();
+ public static native void assertIsManaged();
public static native boolean hasOatFile();
public static native boolean runtimeIsSoftFail();
- public static native boolean isCallerInterpreted();
+ public static native boolean isInterpreted();
}
diff --git a/test/449-checker-bce/expected.txt b/test/449-checker-bce/expected.txt
index e114c50..4665d7a 100644
--- a/test/449-checker-bce/expected.txt
+++ b/test/449-checker-bce/expected.txt
@@ -1 +1,2 @@
+JNI_OnLoad called
java.lang.ArrayIndexOutOfBoundsException: length=5; index=82
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index f06c250..22829cd 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -265,6 +265,7 @@
// A helper into which the actual throwing function should be inlined.
static void constantIndexingForward6(int[] array) {
+ assertIsManaged();
constantIndexing6(array);
}
@@ -618,13 +619,17 @@
static int foo() {
try {
+ assertIsManaged();
// This will cause AIOOBE.
constantIndexing2(new int[3]);
} catch (ArrayIndexOutOfBoundsException e) {
+ assertIsManaged(); // This is to ensure that single-frame deoptimization works.
+ // Will need to be updated if constantIndexing2 is inlined.
try {
// This will cause AIOOBE.
constantIndexingForward6(new int[3]);
} catch (ArrayIndexOutOfBoundsException e2) {
+ assertIsManaged();
return 99;
}
}
@@ -634,13 +639,13 @@
int sum;
- /// CHECK-START: void Main.foo1(int[], int, int) BCE (before)
+ /// CHECK-START: void Main.foo1(int[], int, int, boolean) BCE (before)
/// CHECK: BoundsCheck
/// CHECK: ArraySet
/// CHECK-NOT: BoundsCheck
/// CHECK: ArrayGet
- /// CHECK-START: void Main.foo1(int[], int, int) BCE (after)
+ /// CHECK-START: void Main.foo1(int[], int, int, boolean) BCE (after)
/// CHECK: Phi
/// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
@@ -657,25 +662,30 @@
/// CHECK: Phi
/// CHECK: Goto
- void foo1(int[] array, int start, int end) {
+ void foo1(int[] array, int start, int end, boolean expectInterpreter) {
// Three HDeoptimize will be added. One for
// start >= 0, one for end <= array.length,
// and one for null check on array (to hoist null
// check and array.length out of loop).
for (int i = start ; i < end; i++) {
+ if (expectInterpreter) {
+ assertIsInterpreted();
+ } else {
+ assertIsManaged();
+ }
array[i] = 1;
sum += array[i];
}
}
- /// CHECK-START: void Main.foo2(int[], int, int) BCE (before)
+ /// CHECK-START: void Main.foo2(int[], int, int, boolean) BCE (before)
/// CHECK: BoundsCheck
/// CHECK: ArraySet
/// CHECK-NOT: BoundsCheck
/// CHECK: ArrayGet
- /// CHECK-START: void Main.foo2(int[], int, int) BCE (after)
+ /// CHECK-START: void Main.foo2(int[], int, int, boolean) BCE (after)
/// CHECK: Phi
/// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
@@ -692,25 +702,30 @@
/// CHECK: Phi
/// CHECK: Goto
- void foo2(int[] array, int start, int end) {
+ void foo2(int[] array, int start, int end, boolean expectInterpreter) {
// Three HDeoptimize will be added. One for
// start >= 0, one for end <= array.length,
// and one for null check on array (to hoist null
// check and array.length out of loop).
for (int i = start ; i <= end; i++) {
+ if (expectInterpreter) {
+ assertIsInterpreted();
+ } else {
+ assertIsManaged();
+ }
array[i] = 1;
sum += array[i];
}
}
- /// CHECK-START: void Main.foo3(int[], int) BCE (before)
+ /// CHECK-START: void Main.foo3(int[], int, boolean) BCE (before)
/// CHECK: BoundsCheck
/// CHECK: ArraySet
/// CHECK-NOT: BoundsCheck
/// CHECK: ArrayGet
- /// CHECK-START: void Main.foo3(int[], int) BCE (after)
+ /// CHECK-START: void Main.foo3(int[], int, boolean) BCE (after)
/// CHECK: Phi
/// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
@@ -726,24 +741,29 @@
/// CHECK: Phi
/// CHECK: Goto
- void foo3(int[] array, int end) {
+ void foo3(int[] array, int end, boolean expectInterpreter) {
// Two HDeoptimize will be added. One for end < array.length,
// and one for null check on array (to hoist null check
// and array.length out of loop).
for (int i = 3 ; i <= end; i++) {
+ if (expectInterpreter) {
+ assertIsInterpreted();
+ } else {
+ assertIsManaged();
+ }
array[i] = 1;
sum += array[i];
}
}
- /// CHECK-START: void Main.foo4(int[], int) BCE (before)
+ /// CHECK-START: void Main.foo4(int[], int, boolean) BCE (before)
/// CHECK: BoundsCheck
/// CHECK: ArraySet
/// CHECK-NOT: BoundsCheck
/// CHECK: ArrayGet
- /// CHECK-START: void Main.foo4(int[], int) BCE (after)
+ /// CHECK-START: void Main.foo4(int[], int, boolean) BCE (after)
/// CHECK: Phi
/// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
@@ -759,18 +779,23 @@
/// CHECK: Phi
/// CHECK: Goto
- void foo4(int[] array, int end) {
+ void foo4(int[] array, int end, boolean expectInterpreter) {
// Two HDeoptimize will be added. One for end <= array.length,
// and one for null check on array (to hoist null check
// and array.length out of loop).
for (int i = end ; i > 0; i--) {
+ if (expectInterpreter) {
+ assertIsInterpreted();
+ } else {
+ assertIsManaged();
+ }
array[i - 1] = 1;
sum += array[i - 1];
}
}
- /// CHECK-START: void Main.foo5(int[], int) BCE (before)
+ /// CHECK-START: void Main.foo5(int[], int, boolean) BCE (before)
/// CHECK: BoundsCheck
/// CHECK: ArraySet
/// CHECK: BoundsCheck
@@ -780,7 +805,7 @@
/// CHECK: BoundsCheck
/// CHECK: ArrayGet
- /// CHECK-START: void Main.foo5(int[], int) BCE (after)
+ /// CHECK-START: void Main.foo5(int[], int, boolean) BCE (after)
/// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
/// CHECK: Phi
@@ -800,7 +825,7 @@
/// CHECK-NOT: Phi
/// CHECK: Goto
- void foo5(int[] array, int end) {
+ void foo5(int[] array, int end, boolean expectInterpreter) {
// Bounds check in this loop can be eliminated without deoptimization.
for (int i = array.length - 1 ; i >= 0; i--) {
array[i] = 1;
@@ -808,6 +833,11 @@
// One HDeoptimize will be added.
// It's for (end - 2 <= array.length - 2).
for (int i = end - 2 ; i > 0; i--) {
+ if (expectInterpreter) {
+ assertIsInterpreted();
+ } else {
+ assertIsManaged();
+ }
sum += array[i - 1];
sum += array[i];
sum += array[i + 1];
@@ -815,7 +845,7 @@
}
- /// CHECK-START: void Main.foo6(int[], int, int) BCE (before)
+ /// CHECK-START: void Main.foo6(int[], int, int, boolean) BCE (before)
/// CHECK: BoundsCheck
/// CHECK: ArrayGet
/// CHECK: BoundsCheck
@@ -829,7 +859,7 @@
/// CHECK-NOT: BoundsCheck
/// CHECK: ArraySet
- /// CHECK-START: void Main.foo6(int[], int, int) BCE (after)
+ /// CHECK-START: void Main.foo6(int[], int, int, boolean) BCE (after)
/// CHECK: Phi
/// CHECK-NOT: BoundsCheck
/// CHECK: ArrayGet
@@ -855,12 +885,17 @@
/// CHECK: Goto
/// CHECK-NOT: Deoptimize
- void foo6(int[] array, int start, int end) {
+ void foo6(int[] array, int start, int end, boolean expectInterpreter) {
// Three HDeoptimize will be added. One for
// start >= 2, one for end <= array.length - 3,
// and one for null check on array (to hoist null
// check and array.length out of loop).
for (int i = end; i >= start; i--) {
+ if (expectInterpreter) {
+ assertIsInterpreted();
+ } else {
+ assertIsManaged();
+ }
array[i] = (array[i-2] + array[i-1] + array[i] + array[i+1] + array[i+2]) / 5;
}
}
@@ -948,12 +983,12 @@
}
- /// CHECK-START: void Main.foo9(int[]) BCE (before)
+ /// CHECK-START: void Main.foo9(int[], boolean) BCE (before)
/// CHECK: NullCheck
/// CHECK: BoundsCheck
/// CHECK: ArrayGet
- /// CHECK-START: void Main.foo9(int[]) BCE (after)
+ /// CHECK-START: void Main.foo9(int[], boolean) BCE (after)
// The loop is guaranteed to be entered. No need to transform the
// loop for loop body entry test.
/// CHECK: Deoptimize
@@ -964,10 +999,15 @@
/// CHECK-NOT: BoundsCheck
/// CHECK: ArrayGet
- void foo9(int[] array) {
+ void foo9(int[] array, boolean expectInterpreter) {
// Two HDeoptimize will be added. One for
// 10 <= array.length, and one for null check on array.
for (int i = 0 ; i < 10; i++) {
+ if (expectInterpreter) {
+ assertIsInterpreted();
+ } else {
+ assertIsManaged();
+ }
sum += array[i];
}
}
@@ -999,7 +1039,7 @@
static void testUnknownBounds() {
boolean caught = false;
Main main = new Main();
- main.foo1(new int[10], 0, 10);
+ main.foo1(new int[10], 0, 10, false);
if (main.sum != 10) {
System.out.println("foo1 failed!");
}
@@ -1007,7 +1047,7 @@
caught = false;
main = new Main();
try {
- main.foo1(new int[10], 0, 11);
+ main.foo1(new int[10], 0, 11, true);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
@@ -1016,7 +1056,7 @@
}
main = new Main();
- main.foo2(new int[10], 0, 9);
+ main.foo2(new int[10], 0, 9, false);
if (main.sum != 10) {
System.out.println("foo2 failed!");
}
@@ -1024,7 +1064,7 @@
caught = false;
main = new Main();
try {
- main.foo2(new int[10], 0, 10);
+ main.foo2(new int[10], 0, 10, true);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
@@ -1033,7 +1073,7 @@
}
main = new Main();
- main.foo3(new int[10], 9);
+ main.foo3(new int[10], 9, false);
if (main.sum != 7) {
System.out.println("foo3 failed!");
}
@@ -1041,7 +1081,7 @@
caught = false;
main = new Main();
try {
- main.foo3(new int[10], 10);
+ main.foo3(new int[10], 10, true);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
@@ -1050,7 +1090,7 @@
}
main = new Main();
- main.foo4(new int[10], 10);
+ main.foo4(new int[10], 10, false);
if (main.sum != 10) {
System.out.println("foo4 failed!");
}
@@ -1058,7 +1098,7 @@
caught = false;
main = new Main();
try {
- main.foo4(new int[10], 11);
+ main.foo4(new int[10], 11, true);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
@@ -1067,7 +1107,7 @@
}
main = new Main();
- main.foo5(new int[10], 10);
+ main.foo5(new int[10], 10, false);
if (main.sum != 24) {
System.out.println("foo5 failed!");
}
@@ -1075,7 +1115,7 @@
caught = false;
main = new Main();
try {
- main.foo5(new int[10], 11);
+ main.foo5(new int[10], 11, true);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
@@ -1084,11 +1124,11 @@
}
main = new Main();
- main.foo6(new int[10], 2, 7);
+ main.foo6(new int[10], 2, 7, false);
main = new Main();
int[] array9 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
- main.foo9(array9);
+ main.foo9(array9, false);
if (main.sum != 45) {
System.out.println("foo9 failed!");
}
@@ -1104,7 +1144,7 @@
caught = false;
main = new Main();
try {
- main.foo6(new int[10], 2, 8);
+ main.foo6(new int[10], 2, 8, true);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
@@ -1115,7 +1155,7 @@
caught = false;
main = new Main();
try {
- main.foo6(new int[10], 1, 7);
+ main.foo6(new int[10], 1, 7, true);
} catch (ArrayIndexOutOfBoundsException e) {
caught = true;
}
@@ -1152,6 +1192,15 @@
/// CHECK: ParallelMove
public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+
+ if (!compiledWithOptimizing() ||
+ !hasOatFile() ||
+ runtimeIsSoftFail() ||
+ isInterpreted()) {
+ disableStackFrameAsserts();
+ }
+
sieve(20);
int[] array = {5, 2, 3, 7, 0, 1, 6, 4};
@@ -1190,4 +1239,11 @@
new Main().testExceptionMessage();
}
+ public static native boolean compiledWithOptimizing();
+ public static native void disableStackFrameAsserts();
+ public static native void assertIsManaged();
+ public static native void assertIsInterpreted();
+ public static native boolean hasOatFile();
+ public static native boolean runtimeIsSoftFail();
+ public static native boolean isInterpreted();
}
diff --git a/test/529-checker-unresolved/expected.txt b/test/529-checker-unresolved/expected.txt
index 358048c..1e7dbfe 100644
--- a/test/529-checker-unresolved/expected.txt
+++ b/test/529-checker-unresolved/expected.txt
@@ -3,3 +3,5 @@
UnresolvedClass.virtualMethod()
UnresolvedClass.interfaceMethod()
UnresolvedClass.superMethod()
+instanceof ok
+checkcast ok
diff --git a/test/529-checker-unresolved/src/Main.java b/test/529-checker-unresolved/src/Main.java
index 6f04797..5219c04 100644
--- a/test/529-checker-unresolved/src/Main.java
+++ b/test/529-checker-unresolved/src/Main.java
@@ -44,13 +44,141 @@
super.superMethod();
}
+ /// CHECK-START: void Main.callUnresolvedStaticFieldAccess() register (before)
+ /// CHECK: UnresolvedStaticFieldSet field_type:PrimByte
+ /// CHECK: UnresolvedStaticFieldSet field_type:PrimChar
+ /// CHECK: UnresolvedStaticFieldSet field_type:PrimInt
+ /// CHECK: UnresolvedStaticFieldSet field_type:PrimLong
+ /// CHECK: UnresolvedStaticFieldSet field_type:PrimFloat
+ /// CHECK: UnresolvedStaticFieldSet field_type:PrimDouble
+ /// CHECK: UnresolvedStaticFieldSet field_type:PrimNot
+
+ /// CHECK: UnresolvedStaticFieldGet field_type:PrimByte
+ /// CHECK: UnresolvedStaticFieldGet field_type:PrimChar
+ /// CHECK: UnresolvedStaticFieldGet field_type:PrimInt
+ /// CHECK: UnresolvedStaticFieldGet field_type:PrimLong
+ /// CHECK: UnresolvedStaticFieldGet field_type:PrimFloat
+ /// CHECK: UnresolvedStaticFieldGet field_type:PrimDouble
+ /// CHECK: UnresolvedStaticFieldGet field_type:PrimNot
+ static public void callUnresolvedStaticFieldAccess() {
+ Object o = new Object();
+ UnresolvedClass.staticByte = (byte)1;
+ UnresolvedClass.staticChar = '1';
+ UnresolvedClass.staticInt = 123456789;
+ UnresolvedClass.staticLong = 123456789123456789l;
+ UnresolvedClass.staticFloat = 123456789123456789f;
+ UnresolvedClass.staticDouble = 123456789123456789d;
+ UnresolvedClass.staticObject = o;
+
+ expectEquals((byte)1, UnresolvedClass.staticByte);
+ expectEquals('1', UnresolvedClass.staticChar);
+ expectEquals(123456789, UnresolvedClass.staticInt);
+ expectEquals(123456789123456789l, UnresolvedClass.staticLong);
+ expectEquals(123456789123456789f, UnresolvedClass.staticFloat);
+ expectEquals(123456789123456789d, UnresolvedClass.staticDouble);
+ expectEquals(o, UnresolvedClass.staticObject);
+ }
+
+ /// CHECK-START: void Main.callUnresolvedInstanceFieldAccess(UnresolvedClass) register (before)
+ /// CHECK: UnresolvedInstanceFieldSet field_type:PrimByte
+ /// CHECK: UnresolvedInstanceFieldSet field_type:PrimChar
+ /// CHECK: UnresolvedInstanceFieldSet field_type:PrimInt
+ /// CHECK: UnresolvedInstanceFieldSet field_type:PrimLong
+ /// CHECK: UnresolvedInstanceFieldSet field_type:PrimFloat
+ /// CHECK: UnresolvedInstanceFieldSet field_type:PrimDouble
+ /// CHECK: UnresolvedInstanceFieldSet field_type:PrimNot
+
+ /// CHECK: UnresolvedInstanceFieldGet field_type:PrimByte
+ /// CHECK: UnresolvedInstanceFieldGet field_type:PrimChar
+ /// CHECK: UnresolvedInstanceFieldGet field_type:PrimInt
+ /// CHECK: UnresolvedInstanceFieldGet field_type:PrimLong
+ /// CHECK: UnresolvedInstanceFieldGet field_type:PrimFloat
+ /// CHECK: UnresolvedInstanceFieldGet field_type:PrimDouble
+ /// CHECK: UnresolvedInstanceFieldGet field_type:PrimNot
+ static public void callUnresolvedInstanceFieldAccess(UnresolvedClass c) {
+ Object o = new Object();
+ c.instanceByte = (byte)1;
+ c.instanceChar = '1';
+ c.instanceInt = 123456789;
+ c.instanceLong = 123456789123456789l;
+ c.instanceFloat = 123456789123456789f;
+ c.instanceDouble = 123456789123456789d;
+ c.instanceObject = o;
+
+ expectEquals((byte)1, c.instanceByte);
+ expectEquals('1', c.instanceChar);
+ expectEquals(123456789, c.instanceInt);
+ expectEquals(123456789123456789l, c.instanceLong);
+ expectEquals(123456789123456789f, c.instanceFloat);
+ expectEquals(123456789123456789d, c.instanceDouble);
+ expectEquals(o, c.instanceObject);
+ }
+
+ static public void testInstanceOf(Object o) {
+ if (o instanceof UnresolvedSuperClass) {
+ System.out.println("instanceof ok");
+ }
+ }
+
+ static public UnresolvedSuperClass testCheckCast(Object o) {
+ UnresolvedSuperClass c = (UnresolvedSuperClass) o;
+ System.out.println("checkcast ok");
+ return c;
+ }
/// CHECK-START: void Main.main(java.lang.String[]) register (before)
/// CHECK: InvokeUnresolved invoke_type:direct
static public void main(String[] args) {
UnresolvedClass c = new UnresolvedClass();
+ Main m = new Main();
callInvokeUnresolvedStatic();
callInvokeUnresolvedVirtual(c);
callInvokeUnresolvedInterface(c);
- callInvokeUnresolvedSuper(new Main());
+ callInvokeUnresolvedSuper(m);
+ callUnresolvedStaticFieldAccess();
+ callUnresolvedInstanceFieldAccess(c);
+ testInstanceOf(m);
+ testCheckCast(m);
+ }
+
+ public static void expectEquals(byte expected, byte result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void expectEquals(char expected, char result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void expectEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void expectEquals(float expected, float result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void expectEquals(double expected, double result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void expectEquals(Object expected, Object result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
}
}
diff --git a/test/529-checker-unresolved/src/Unresolved.java b/test/529-checker-unresolved/src/Unresolved.java
index 5bf92dd..20ac6e0 100644
--- a/test/529-checker-unresolved/src/Unresolved.java
+++ b/test/529-checker-unresolved/src/Unresolved.java
@@ -40,15 +40,21 @@
public void interfaceMethod() {
System.out.println("UnresolvedClass.interfaceMethod()");
}
-}
-final class UnresolvedFinalClass {
- public void directMethod() {
- System.out.println("UnresolvedFinalClass.directMethod()");
- }
-}
+ public static byte staticByte;
+ public static char staticChar;
+ public static int staticInt;
+ public static long staticLong;
+ public static float staticFloat;
+ public static double staticDouble;
+ public static Object staticObject;
-class UnresolvedAtRuntime {
- public void unresolvedAtRuntime() { }
+ public byte instanceByte;
+ public char instanceChar;
+ public int instanceInt;
+ public long instanceLong;
+ public float instanceFloat;
+ public double instanceDouble;
+ public Object instanceObject;
}
diff --git a/test/535-deopt-and-inlining/expected.txt b/test/535-deopt-and-inlining/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/535-deopt-and-inlining/expected.txt
diff --git a/test/535-deopt-and-inlining/info.txt b/test/535-deopt-and-inlining/info.txt
new file mode 100644
index 0000000..717612a
--- /dev/null
+++ b/test/535-deopt-and-inlining/info.txt
@@ -0,0 +1,2 @@
+Stress test for deoptimization and JIT, to ensure the
+stack visitor uses the right ArtMethod when deopting.
diff --git a/test/535-deopt-and-inlining/src/Main.java b/test/535-deopt-and-inlining/src/Main.java
new file mode 100644
index 0000000..c231bf0
--- /dev/null
+++ b/test/535-deopt-and-inlining/src/Main.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+public class Main {
+
+ public static void run() {
+ // Loop enough to get JIT compilation.
+ for (int i = 0; i < 10000; ++i) {
+ doCall(new int[0]);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ run();
+ }
+
+ public static void doCall(int[] array) {
+ try {
+ deopt(array);
+ } catch (IndexOutOfBoundsException ioobe) {
+ // Expected
+ }
+ }
+
+ public static void deopt(int[] array) {
+ // Invoke `deopt` much more than `$inline$deopt` so that only `deopt` gets
+ // initially JITted.
+ if (call == 100) {
+ call = 0;
+ $inline$deopt(array);
+ } else {
+ call++;
+ }
+ }
+
+ public static void $inline$deopt(int[] array) {
+ array[0] = 1;
+ array[1] = 1;
+ }
+
+ static int call = 0;
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 49778cb..db16b97 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -369,6 +369,7 @@
# This test dynamically enables tracing to force a deoptimization. This makes the test meaningless
# when already tracing, and writes an error message that we do not want to check for.
TEST_ART_BROKEN_TRACING_RUN_TESTS := \
+ 087-gc-after-link \
137-cfi \
141-class-unload \
802-deoptimization
@@ -490,14 +491,6 @@
$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
endif
-# If ART_USE_OPTIMIZING_COMPILER is set to true, then the default core.art has been
-# compiled with the optimizing compiler.
-ifeq ($(ART_USE_OPTIMIZING_COMPILER),true)
- ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
- default,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
- $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
-endif
-
TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS :=
# Tests that should fail when the optimizing compiler compiles them non-debuggable.
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 042b03b..082c9b3 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -66,4 +66,54 @@
return Runtime::Current()->IsImageDex2OatEnabled();
}
+// public static native boolean compiledWithOptimizing();
+// Did we use the optimizing compiler to compile this?
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_compiledWithOptimizing(JNIEnv* env, jclass cls) {
+ ScopedObjectAccess soa(env);
+
+ mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
+ const DexFile& dex_file = klass->GetDexFile();
+ const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
+ if (oat_dex_file == nullptr) {
+ // Could be JIT, which also uses optimizing, but conservatively say no.
+ return JNI_FALSE;
+ }
+ const OatFile* oat_file = oat_dex_file->GetOatFile();
+ CHECK(oat_file != nullptr);
+
+ const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey);
+ CHECK(cmd_line != nullptr); // Huh? This should not happen.
+
+ // Check the backend.
+ constexpr const char* kCompilerBackend = "--compiler-backend=";
+ const char* backend = strstr(cmd_line, kCompilerBackend);
+ if (backend != nullptr) {
+ // If it's set, make sure it's optimizing.
+ backend += strlen(kCompilerBackend);
+ if (strncmp(backend, "Optimizing", strlen("Optimizing")) != 0) {
+ return JNI_FALSE;
+ }
+ }
+
+ // Check the filter.
+ constexpr const char* kCompilerFilter = "--compiler-filter=";
+ const char* filter = strstr(cmd_line, kCompilerFilter);
+ if (filter != nullptr) {
+ // If it's set, make sure it's not interpret-only|verify-none|verify-at-runtime.
+ // Note: The space filter might have an impact on the test, but ignore that for now.
+ filter += strlen(kCompilerFilter);
+ constexpr const char* kInterpretOnly = "interpret-only";
+ constexpr const char* kVerifyNone = "verify-none";
+ constexpr const char* kVerifyAtRuntime = "verify-at-runtime";
+ if (strncmp(filter, kInterpretOnly, strlen(kInterpretOnly)) == 0 ||
+ strncmp(filter, kVerifyNone, strlen(kVerifyNone)) == 0 ||
+ strncmp(filter, kVerifyAtRuntime, strlen(kVerifyAtRuntime)) == 0) {
+ return JNI_FALSE;
+ }
+ }
+
+ return JNI_TRUE;
+}
+
} // namespace art
diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc
index d22cf52..922eae6 100644
--- a/test/common/stack_inspect.cc
+++ b/test/common/stack_inspect.cc
@@ -27,9 +27,20 @@
namespace art {
-// public static native boolean isCallerInterpreted();
+static bool asserts_enabled = true;
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isCallerInterpreted(JNIEnv* env, jclass) {
+// public static native void disableStackFrameAsserts();
+// Note: to globally disable asserts in unsupported configurations.
+
+extern "C" JNIEXPORT void JNICALL Java_Main_disableStackFrameAsserts(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass cls ATTRIBUTE_UNUSED) {
+ asserts_enabled = false;
+}
+
+
+// public static native boolean isInterpreted();
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterpreted(JNIEnv* env, jclass) {
ScopedObjectAccess soa(env);
NthCallerVisitor caller(soa.Self(), 1, false);
caller.WalkStack();
@@ -37,16 +48,18 @@
return caller.GetCurrentShadowFrame() != nullptr ? JNI_TRUE : JNI_FALSE;
}
-// public static native void assertCallerIsInterpreted();
+// public static native void assertIsInterpreted();
-extern "C" JNIEXPORT void JNICALL Java_Main_assertCallerIsInterpreted(JNIEnv* env, jclass klass) {
- CHECK(Java_Main_isCallerInterpreted(env, klass));
+extern "C" JNIEXPORT void JNICALL Java_Main_assertIsInterpreted(JNIEnv* env, jclass klass) {
+ if (asserts_enabled) {
+ CHECK(Java_Main_isInterpreted(env, klass));
+ }
}
-// public static native boolean isCallerManaged();
+// public static native boolean isManaged();
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isCallerManaged(JNIEnv* env, jclass cls) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isManaged(JNIEnv* env, jclass cls) {
ScopedObjectAccess soa(env);
mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
@@ -65,10 +78,12 @@
return caller.GetCurrentShadowFrame() != nullptr ? JNI_FALSE : JNI_TRUE;
}
-// public static native void assertCallerIsManaged();
+// public static native void assertIsManaged();
-extern "C" JNIEXPORT void JNICALL Java_Main_assertCallerIsManaged(JNIEnv* env, jclass cls) {
- CHECK(Java_Main_isCallerManaged(env, cls));
+extern "C" JNIEXPORT void JNICALL Java_Main_assertIsManaged(JNIEnv* env, jclass cls) {
+ if (asserts_enabled) {
+ CHECK(Java_Main_isManaged(env, cls));
+ }
}
} // namespace art
diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk
index 3c1522c..71366c1 100644
--- a/tools/ahat/Android.mk
+++ b/tools/ahat/Android.mk
@@ -16,6 +16,8 @@
LOCAL_PATH := $(call my-dir)
+include art/build/Android.common_test.mk
+
# --- ahat.jar ----------------
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -44,7 +46,7 @@
ahat: $(LOCAL_BUILT_MODULE)
-# --- ahat-test.jar --------------
+# --- ahat-tests.jar --------------
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under, test)
LOCAL_JAR_MANIFEST := test/manifest.txt
@@ -53,6 +55,42 @@
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE := ahat-tests
include $(BUILD_HOST_JAVA_LIBRARY)
+AHAT_TEST_JAR := $(LOCAL_BUILT_MODULE)
-ahat-test: $(LOCAL_BUILT_MODULE)
- java -jar $<
+# --- ahat-test-dump.jar --------------
+include $(CLEAR_VARS)
+LOCAL_MODULE := ahat-test-dump
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, test-dump)
+include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
+
+# Determine the location of the test-dump.jar and test-dump.hprof files.
+# These use variables set implicitly by the include of
+# BUILD_HOST_DALVIK_JAVA_LIBRARY above.
+AHAT_TEST_DUMP_JAR := $(LOCAL_BUILT_MODULE)
+AHAT_TEST_DUMP_HPROF := $(intermediates.COMMON)/test-dump.hprof
+
+# Run ahat-test-dump.jar to generate test-dump.hprof
+AHAT_TEST_DUMP_DEPENDENCIES := \
+ $(ART_HOST_EXECUTABLES) \
+ $(HOST_OUT_EXECUTABLES)/art \
+ $(HOST_CORE_IMG_OUT_BASE)$(CORE_IMG_SUFFIX)
+
+$(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_ART := $(HOST_OUT_EXECUTABLES)/art
+$(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_DUMP_JAR := $(AHAT_TEST_DUMP_JAR)
+$(AHAT_TEST_DUMP_HPROF): PRIVATE_AHAT_TEST_DUMP_DEPENDENCIES := $(AHAT_TEST_DUMP_DEPENDENCIES)
+$(AHAT_TEST_DUMP_HPROF): $(AHAT_TEST_DUMP_JAR) $(AHAT_TEST_DUMP_DEPENDENCIES)
+ $(PRIVATE_AHAT_TEST_ART) -cp $(PRIVATE_AHAT_TEST_DUMP_JAR) Main $@
+
+.PHONY: ahat-test
+ahat-test: PRIVATE_AHAT_TEST_DUMP_HPROF := $(AHAT_TEST_DUMP_HPROF)
+ahat-test: PRIVATE_AHAT_TEST_JAR := $(AHAT_TEST_JAR)
+ahat-test: $(AHAT_TEST_JAR) $(AHAT_TEST_DUMP_HPROF)
+ java -Dahat.test.dump.hprof=$(PRIVATE_AHAT_TEST_DUMP_HPROF) -jar $(PRIVATE_AHAT_TEST_JAR)
+
+# Clean up local variables.
+AHAT_TEST_DUMP_DEPENDENCIES :=
+AHAT_TEST_DUMP_HPROF :=
+AHAT_TEST_DUMP_JAR :=
+AHAT_TEST_JAR :=
+
diff --git a/tools/ahat/src/AhatSnapshot.java b/tools/ahat/src/AhatSnapshot.java
index 2437d03..3035ef7 100644
--- a/tools/ahat/src/AhatSnapshot.java
+++ b/tools/ahat/src/AhatSnapshot.java
@@ -18,13 +18,18 @@
import com.android.tools.perflib.heap.ClassObj;
import com.android.tools.perflib.heap.Heap;
+import com.android.tools.perflib.heap.HprofParser;
import com.android.tools.perflib.heap.Instance;
import com.android.tools.perflib.heap.RootObj;
import com.android.tools.perflib.heap.Snapshot;
import com.android.tools.perflib.heap.StackFrame;
import com.android.tools.perflib.heap.StackTrace;
+import com.android.tools.perflib.heap.io.HprofBuffer;
+import com.android.tools.perflib.heap.io.MemoryMappedFileBuffer;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
+import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -47,7 +52,22 @@
private Site mRootSite;
private Map<Heap, Long> mHeapSizes;
- public AhatSnapshot(Snapshot snapshot) {
+ /**
+ * Create an AhatSnapshot from an hprof file.
+ */
+ public static AhatSnapshot fromHprof(File hprof) throws IOException {
+ HprofBuffer buffer = new MemoryMappedFileBuffer(hprof);
+ Snapshot snapshot = (new HprofParser(buffer)).parse();
+ snapshot.computeDominators();
+ return new AhatSnapshot(snapshot);
+ }
+
+ /**
+ * Construct an AhatSnapshot for the given perflib snapshot.
+ * Ther user is responsible for calling snapshot.computeDominators before
+ * calling this AhatSnapshot constructor.
+ */
+ private AhatSnapshot(Snapshot snapshot) {
mSnapshot = snapshot;
mHeaps = new ArrayList<Heap>(mSnapshot.getHeaps());
mDominated = new HashMap<Instance, List<Instance>>();
@@ -92,6 +112,11 @@
}
}
+ // Note: This method is exposed for testing purposes.
+ public ClassObj findClass(String name) {
+ return mSnapshot.findClass(name);
+ }
+
public Instance findInstance(long id) {
return mSnapshot.findInstance(id);
}
diff --git a/tools/ahat/src/InstanceUtils.java b/tools/ahat/src/InstanceUtils.java
index 7ee3ff2..a6ac3b8 100644
--- a/tools/ahat/src/InstanceUtils.java
+++ b/tools/ahat/src/InstanceUtils.java
@@ -32,7 +32,7 @@
* given name.
*/
public static boolean isInstanceOfClass(Instance inst, String className) {
- ClassObj cls = inst.getClassObj();
+ ClassObj cls = (inst == null) ? null : inst.getClassObj();
return (cls != null && className.equals(cls.getClassName()));
}
@@ -132,7 +132,7 @@
* Read a field of an instance.
* Returns null if the field value is null or if the field couldn't be read.
*/
- private static Object getField(Instance inst, String fieldName) {
+ public static Object getField(Instance inst, String fieldName) {
if (!(inst instanceof ClassInstance)) {
return null;
}
diff --git a/tools/ahat/src/Main.java b/tools/ahat/src/Main.java
index 2e2ddd2..1563aa0 100644
--- a/tools/ahat/src/Main.java
+++ b/tools/ahat/src/Main.java
@@ -16,10 +16,6 @@
package com.android.ahat;
-import com.android.tools.perflib.heap.HprofParser;
-import com.android.tools.perflib.heap.Snapshot;
-import com.android.tools.perflib.heap.io.HprofBuffer;
-import com.android.tools.perflib.heap.io.MemoryMappedFileBuffer;
import com.sun.net.httpserver.HttpServer;
import java.io.File;
import java.io.IOException;
@@ -71,15 +67,8 @@
return;
}
- System.out.println("Reading hprof file...");
- HprofBuffer buffer = new MemoryMappedFileBuffer(hprof);
- Snapshot snapshot = (new HprofParser(buffer)).parse();
-
- System.out.println("Computing Dominators...");
- snapshot.computeDominators();
-
- System.out.println("Processing snapshot for ahat...");
- AhatSnapshot ahat = new AhatSnapshot(snapshot);
+ System.out.println("Processing hprof file...");
+ AhatSnapshot ahat = AhatSnapshot.fromHprof(hprof);
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, port);
diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/test-dump/Main.java
new file mode 100644
index 0000000..cea1dc1
--- /dev/null
+++ b/tools/ahat/test-dump/Main.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import dalvik.system.VMDebug;
+import java.io.IOException;
+
+/**
+ * Program used to create a heap dump for test purposes.
+ */
+public class Main {
+ // Keep a reference to the DumpedStuff instance so that it is not garbage
+ // collected before we take the heap dump.
+ public static DumpedStuff stuff;
+
+ // We will take a heap dump that includes a single instance of this
+ // DumpedStuff class. Objects stored as fields in this class can be easily
+ // found in the hprof dump by searching for the instance of the DumpedStuff
+ // class and reading the desired field.
+ public static class DumpedStuff {
+ public String basicString = "hello, world";
+ public String nullString = null;
+ public Object anObject = new Object();
+ }
+
+ public static void main(String[] args) throws IOException {
+ if (args.length < 1) {
+ System.err.println("no output file specified");
+ return;
+ }
+ String file = args[0];
+
+ // Allocate the instance of DumpedStuff.
+ stuff = new DumpedStuff();
+
+ // Take a heap dump that will include that instance of DumpedStuff.
+ System.err.println("Dumping hprof data to " + file);
+ VMDebug.dumpHprofData(file);
+ }
+}
diff --git a/tools/ahat/test/InstanceUtilsTest.java b/tools/ahat/test/InstanceUtilsTest.java
new file mode 100644
index 0000000..7613df4
--- /dev/null
+++ b/tools/ahat/test/InstanceUtilsTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.ahat;
+
+import com.android.tools.perflib.heap.Instance;
+import java.io.IOException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import org.junit.Test;
+
+public class InstanceUtilsTest {
+ @Test
+ public void basicString() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance str = (Instance)dump.getDumpedThing("basicString");
+ assertEquals("hello, world", InstanceUtils.asString(str));
+ }
+
+ @Test
+ public void nullString() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance obj = (Instance)dump.getDumpedThing("nullString");
+ assertNull(InstanceUtils.asString(obj));
+ }
+
+ @Test
+ public void notString() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+ Instance obj = (Instance)dump.getDumpedThing("anObject");
+ assertNotNull(obj);
+ assertNull(InstanceUtils.asString(obj));
+ }
+}
diff --git a/tools/ahat/test/TestDump.java b/tools/ahat/test/TestDump.java
new file mode 100644
index 0000000..c3a76e4
--- /dev/null
+++ b/tools/ahat/test/TestDump.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package com.android.ahat;
+
+import com.android.tools.perflib.heap.ClassObj;
+import com.android.tools.perflib.heap.Field;
+import com.android.tools.perflib.heap.Instance;
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * The TestDump class is used to get an AhatSnapshot for the test-dump
+ * program.
+ */
+public class TestDump {
+ // It can take on the order of a second to parse and process the test-dump
+ // hprof. To avoid repeating this overhead for each test case, we cache the
+ // loaded instance of TestDump and reuse it when possible. In theory the
+ // test cases should not be able to modify the cached snapshot in a way that
+ // is visible to other test cases.
+ private static TestDump mCachedTestDump = null;
+
+ private AhatSnapshot mSnapshot = null;
+
+ /**
+ * Load the test-dump.hprof file.
+ * The location of the file is read from the system property
+ * "ahat.test.dump.hprof", which is expected to be set on the command line.
+ * For example:
+ * java -Dahat.test.dump.hprof=test-dump.hprof -jar ahat-tests.jar
+ *
+ * An IOException is thrown if there is a failure reading the hprof file.
+ */
+ private TestDump() throws IOException {
+ String hprof = System.getProperty("ahat.test.dump.hprof");
+ mSnapshot = AhatSnapshot.fromHprof(new File(hprof));
+ }
+
+ /**
+ * Get the AhatSnapshot for the test dump program.
+ */
+ public AhatSnapshot getAhatSnapshot() {
+ return mSnapshot;
+ }
+
+ /**
+ * Return the value of a field in the DumpedStuff instance in the
+ * snapshot for the test-dump program.
+ */
+ public Object getDumpedThing(String name) {
+ ClassObj main = mSnapshot.findClass("Main");
+ Instance stuff = null;
+ for (Map.Entry<Field, Object> fields : main.getStaticFieldValues().entrySet()) {
+ if ("stuff".equals(fields.getKey().getName())) {
+ stuff = (Instance) fields.getValue();
+ }
+ }
+ return InstanceUtils.getField(stuff, name);
+ }
+
+ /**
+ * Get the test dump.
+ * An IOException is thrown if there is an error reading the test dump hprof
+ * file.
+ * To improve performance, this returns a cached instance of the TestDump
+ * when possible.
+ */
+ public static synchronized TestDump getTestDump() throws IOException {
+ if (mCachedTestDump == null) {
+ mCachedTestDump = new TestDump();
+ }
+ return mCachedTestDump;
+ }
+}
diff --git a/tools/ahat/test/Tests.java b/tools/ahat/test/Tests.java
index fb53d90..bab7121 100644
--- a/tools/ahat/test/Tests.java
+++ b/tools/ahat/test/Tests.java
@@ -22,6 +22,7 @@
public static void main(String[] args) {
if (args.length == 0) {
args = new String[]{
+ "com.android.ahat.InstanceUtilsTest",
"com.android.ahat.QueryTest",
"com.android.ahat.SortTest"
};
diff --git a/tools/art b/tools/art
index 676d6ae..304a9d0 100644
--- a/tools/art
+++ b/tools/art
@@ -89,6 +89,7 @@
invoke_with="perf record -o $ANDROID_DATA/perf.data -e cycles:u $invoke_with"
fi
+# We use the PIC core image to work with perf.
ANDROID_DATA=$ANDROID_DATA \
ANDROID_ROOT=$ANDROID_ROOT \
LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
@@ -97,7 +98,7 @@
$invoke_with $ANDROID_ROOT/bin/$DALVIKVM $lib \
-XXlib:$LIBART \
-Xnorelocate \
- -Ximage:$ANDROID_ROOT/framework/core.art \
+ -Ximage:$ANDROID_ROOT/framework/core-optimizing-pic.art \
-Xcompiler-option --generate-debug-info \
"$@"