diff options
80 files changed, 1114 insertions, 156 deletions
diff --git a/build/Android.cpplint.mk b/build/Android.cpplint.mk index 2688c049d5..247f4e3470 100644 --- a/build/Android.cpplint.mk +++ b/build/Android.cpplint.mk @@ -18,7 +18,9 @@ include art/build/Android.common_build.mk ART_CPPLINT := $(LOCAL_PATH)/tools/cpplint.py ART_CPPLINT_FILTER := --filter=-whitespace/line_length,-build/include,-readability/function,-readability/streams,-readability/todo,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn,-runtime/printf -ART_CPPLINT_FLAGS := --root=$(TOP) +# Use `pwd` instead of $TOP for root, $TOP is always . and --root doesn't seem +# to work with a relative path (b/34787652). +ART_CPPLINT_FLAGS := --root=`pwd` ART_CPPLINT_QUIET := --quiet ART_CPPLINT_INGORED := \ runtime/elf.h \ diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index bd530ac6a6..6e087c5785 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -2879,9 +2879,9 @@ void CompilerDriver::AddCompiledMethod(const MethodReference& method_ref, bool CompilerDriver::GetCompiledClass(ClassReference ref, mirror::Class::Status* status) const { DCHECK(status != nullptr); // The table doesn't know if something wasn't inserted. For this case it will return - // kStatusNotReady. To handle this, just assume anything not verified is not compiled. + // kStatusNotReady. To handle this, just assume anything we didn't try to verify is not compiled. if (!compiled_classes_.Get(DexFileReference(ref.first, ref.second), status) || - *status < mirror::Class::kStatusVerified) { + *status < mirror::Class::kStatusRetryVerificationAtRuntime) { return false; } return true; diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index fee6afb91f..4da3e0df39 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -366,6 +366,40 @@ TEST_F(CompilerDriverVerifyTest, VerifyCompilation) { CheckVerifiedClass(class_loader, "LSecond;"); } +// Test that a class of status kStatusRetryVerificationAtRuntime is indeed recorded that way in the +// driver. +TEST_F(CompilerDriverVerifyTest, RetryVerifcationStatus) { + Thread* const self = Thread::Current(); + jobject class_loader; + std::vector<const DexFile*> dex_files; + const DexFile* dex_file = nullptr; + { + ScopedObjectAccess soa(self); + class_loader = LoadDex("ProfileTestMultiDex"); + ASSERT_NE(class_loader, nullptr); + dex_files = GetDexFiles(class_loader); + ASSERT_GT(dex_files.size(), 0u); + dex_file = dex_files.front(); + } + compiler_driver_->SetDexFilesForOatFile(dex_files); + ClassReference ref(dex_file, 0u); + // Test that the status is read from the compiler driver as expected. + for (size_t i = mirror::Class::kStatusRetryVerificationAtRuntime; + i < mirror::Class::kStatusMax; + ++i) { + const mirror::Class::Status expected_status = static_cast<mirror::Class::Status>(i); + // Skip unsupported status that are not supposed to be ever recorded. + if (expected_status == mirror::Class::kStatusVerifyingAtRuntime || + expected_status == mirror::Class::kStatusInitializing) { + continue; + } + compiler_driver_->RecordClassStatus(ref, expected_status); + mirror::Class::Status status = {}; + ASSERT_TRUE(compiler_driver_->GetCompiledClass(ref, &status)); + EXPECT_EQ(status, expected_status); + } +} + // TODO: need check-cast test (when stub complete & we can throw/catch } // namespace art diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 9e4971ce75..115e722a75 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -1686,6 +1686,10 @@ void ImageWriter::CalculateNewObjectOffsets() { runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs); image_methods_[ImageHeader::kSaveEverythingMethod] = runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverything); + image_methods_[ImageHeader::kSaveEverythingMethodForClinit] = + runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForClinit); + image_methods_[ImageHeader::kSaveEverythingMethodForSuspendCheck] = + runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForSuspendCheck); // Visit image methods first to have the main runtime methods in the first image. for (auto* m : image_methods_) { CHECK(m != nullptr); diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc index c166deb406..2f96cfa382 100644 --- a/compiler/optimizing/bounds_check_elimination.cc +++ b/compiler/optimizing/bounds_check_elimination.cc @@ -1121,6 +1121,66 @@ class BCEVisitor : public HGraphVisitor { } } + void VisitRem(HRem* instruction) OVERRIDE { + HInstruction* left = instruction->GetLeft(); + HInstruction* right = instruction->GetRight(); + + // Handle 'i % CONST' format expression in array index, e.g: + // array[i % 20]; + if (right->IsIntConstant()) { + int32_t right_const = std::abs(right->AsIntConstant()->GetValue()); + if (right_const == 0) { + return; + } + // The sign of divisor CONST doesn't affect the sign final value range. + // For example: + // if (i > 0) { + // array[i % 10]; // index value range [0, 9] + // array[i % -10]; // index value range [0, 9] + // } + ValueRange* right_range = new (GetGraph()->GetArena()) ValueRange( + GetGraph()->GetArena(), + ValueBound(nullptr, 1 - right_const), + ValueBound(nullptr, right_const - 1)); + + ValueRange* left_range = LookupValueRange(left, left->GetBlock()); + if (left_range != nullptr) { + right_range = left_range->Narrow(right_range); + } + AssignRange(instruction->GetBlock(), instruction, right_range); + return; + } + + // Handle following pattern: + // i0 NullCheck + // i1 ArrayLength[i0] + // i2 DivByZeroCheck [i1] <-- right + // i3 Rem [i5, i2] <-- we are here. + // i4 BoundsCheck [i3,i1] + if (right->IsDivZeroCheck()) { + // if array_length can pass div-by-zero check, + // array_length must be > 0. + right = right->AsDivZeroCheck()->InputAt(0); + } + + // Handle 'i % array.length' format expression in array index, e.g: + // array[(i+7) % array.length]; + if (right->IsArrayLength()) { + ValueBound lower = ValueBound::Min(); // ideally, lower should be '1-array_length'. + ValueBound upper = ValueBound(right, -1); // array_length - 1 + ValueRange* right_range = new (GetGraph()->GetArena()) ValueRange( + GetGraph()->GetArena(), + lower, + upper); + ValueRange* left_range = LookupValueRange(left, left->GetBlock()); + if (left_range != nullptr) { + right_range = left_range->Narrow(right_range); + } + AssignRange(instruction->GetBlock(), instruction, right_range); + return; + } + } + void VisitNewArray(HNewArray* new_array) OVERRIDE { HInstruction* len = new_array->GetLength(); if (!len->IsIntConstant()) { diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc index 575e2fc24a..2aaf05833c 100644 --- a/compiler/optimizing/bounds_check_elimination_test.cc +++ b/compiler/optimizing/bounds_check_elimination_test.cc @@ -951,4 +951,152 @@ TEST_F(BoundsCheckEliminationTest, BubbleSortArrayBoundsElimination) { ASSERT_TRUE(IsRemoved(bounds_check6)); } +// int[] array = new int[10]; +// for (int i=0; i<200; i++) { +// array[i%10] = 10; // Can eliminate +// array[i%1] = 10; // Can eliminate +// array[i%200] = 10; // Cannot eliminate +// array[i%-10] = 10; // Can eliminate +// array[i%array.length] = 10; // Can eliminate +// array[param_i%10] = 10; // Can't eliminate, when param_i < 0 +// } +TEST_F(BoundsCheckEliminationTest, ModArrayBoundsElimination) { + HBasicBlock* entry = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(entry); + graph_->SetEntryBlock(entry); + HInstruction* param_i = new (&allocator_) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimInt); + entry->AddInstruction(param_i); + + HInstruction* constant_0 = graph_->GetIntConstant(0); + HInstruction* constant_1 = graph_->GetIntConstant(1); + HInstruction* constant_10 = graph_->GetIntConstant(10); + HInstruction* constant_200 = graph_->GetIntConstant(200); + HInstruction* constant_minus_10 = graph_->GetIntConstant(-10); + + HBasicBlock* block = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(block); + entry->AddSuccessor(block); + // We pass a bogus constant for the class to avoid mocking one. + HInstruction* new_array = new (&allocator_) HNewArray(constant_10, constant_10, 0); + block->AddInstruction(new_array); + block->AddInstruction(new (&allocator_) HGoto()); + + HBasicBlock* loop_header = new (&allocator_) HBasicBlock(graph_); + HBasicBlock* loop_body = new (&allocator_) HBasicBlock(graph_); + HBasicBlock* exit = new (&allocator_) HBasicBlock(graph_); + + graph_->AddBlock(loop_header); + graph_->AddBlock(loop_body); + graph_->AddBlock(exit); + block->AddSuccessor(loop_header); + loop_header->AddSuccessor(exit); // true successor + loop_header->AddSuccessor(loop_body); // false successor + loop_body->AddSuccessor(loop_header); + + HPhi* phi = new (&allocator_) HPhi(&allocator_, 0, 0, Primitive::kPrimInt); + HInstruction* cmp = new (&allocator_) HGreaterThanOrEqual(phi, constant_200); + HInstruction* if_inst = new (&allocator_) HIf(cmp); + loop_header->AddPhi(phi); + loop_header->AddInstruction(cmp); + loop_header->AddInstruction(if_inst); + phi->AddInput(constant_0); + + ////////////////////////////////////////////////////////////////////////////////// + // LOOP BODY: + // array[i % 10] = 10; + HRem* i_mod_10 = new (&allocator_) HRem(Primitive::kPrimInt, phi, constant_10, 0); + HBoundsCheck* bounds_check_i_mod_10 = new (&allocator_) HBoundsCheck(i_mod_10, constant_10, 0); + HInstruction* array_set = new (&allocator_) HArraySet( + new_array, bounds_check_i_mod_10, constant_10, Primitive::kPrimInt, 0); + loop_body->AddInstruction(i_mod_10); + loop_body->AddInstruction(bounds_check_i_mod_10); + loop_body->AddInstruction(array_set); + + // array[i % 1] = 10; + HRem* i_mod_1 = new (&allocator_) HRem(Primitive::kPrimInt, phi, constant_1, 0); + HBoundsCheck* bounds_check_i_mod_1 = new (&allocator_) HBoundsCheck(i_mod_1, constant_10, 0); + array_set = new (&allocator_) HArraySet( + new_array, bounds_check_i_mod_1, constant_10, Primitive::kPrimInt, 0); + loop_body->AddInstruction(i_mod_1); + loop_body->AddInstruction(bounds_check_i_mod_1); + loop_body->AddInstruction(array_set); + + // array[i % 200] = 10; + HRem* i_mod_200 = new (&allocator_) HRem(Primitive::kPrimInt, phi, constant_1, 0); + HBoundsCheck* bounds_check_i_mod_200 = new (&allocator_) HBoundsCheck(i_mod_200, constant_10, 0); + array_set = new (&allocator_) HArraySet( + new_array, bounds_check_i_mod_200, constant_10, Primitive::kPrimInt, 0); + loop_body->AddInstruction(i_mod_200); + loop_body->AddInstruction(bounds_check_i_mod_200); + loop_body->AddInstruction(array_set); + + // array[i % -10] = 10; + HRem* i_mod_minus_10 = new (&allocator_) HRem(Primitive::kPrimInt, phi, constant_minus_10, 0); + HBoundsCheck* bounds_check_i_mod_minus_10 = new (&allocator_) HBoundsCheck( + i_mod_minus_10, constant_10, 0); + array_set = new (&allocator_) HArraySet( + new_array, bounds_check_i_mod_minus_10, constant_10, Primitive::kPrimInt, 0); + loop_body->AddInstruction(i_mod_minus_10); + loop_body->AddInstruction(bounds_check_i_mod_minus_10); + loop_body->AddInstruction(array_set); + + // array[i%array.length] = 10; + HNullCheck* null_check = new (&allocator_) HNullCheck(new_array, 0); + HArrayLength* array_length = new (&allocator_) HArrayLength(null_check, 0); + HRem* i_mod_array_length = new (&allocator_) HRem(Primitive::kPrimInt, phi, array_length, 0); + HBoundsCheck* bounds_check_i_mod_array_len = new (&allocator_) HBoundsCheck( + i_mod_array_length, array_length, 0); + array_set = new (&allocator_) HArraySet( + null_check, bounds_check_i_mod_array_len, constant_10, Primitive::kPrimInt, 0); + loop_body->AddInstruction(null_check); + loop_body->AddInstruction(array_length); + loop_body->AddInstruction(i_mod_array_length); + loop_body->AddInstruction(bounds_check_i_mod_array_len); + loop_body->AddInstruction(array_set); + + // array[param_i % 10] = 10; + HRem* param_i_mod_10 = new (&allocator_) HRem(Primitive::kPrimInt, param_i, constant_10, 0); + HBoundsCheck* bounds_check_param_i_mod_10 = new (&allocator_) HBoundsCheck( + param_i_mod_10, constant_10, 0); + array_set = new (&allocator_) HArraySet( + new_array, bounds_check_param_i_mod_10, constant_10, Primitive::kPrimInt, 0); + loop_body->AddInstruction(param_i_mod_10); + loop_body->AddInstruction(bounds_check_param_i_mod_10); + loop_body->AddInstruction(array_set); + + // array[param_i%array.length] = 10; + null_check = new (&allocator_) HNullCheck(new_array, 0); + array_length = new (&allocator_) HArrayLength(null_check, 0); + HRem* param_i_mod_array_length = new (&allocator_) HRem( + Primitive::kPrimInt, param_i, array_length, 0); + HBoundsCheck* bounds_check_param_i_mod_array_len = new (&allocator_) HBoundsCheck( + param_i_mod_array_length, array_length, 0); + array_set = new (&allocator_) HArraySet( + null_check, bounds_check_param_i_mod_array_len, constant_10, Primitive::kPrimInt, 0); + loop_body->AddInstruction(null_check); + loop_body->AddInstruction(array_length); + loop_body->AddInstruction(param_i_mod_array_length); + loop_body->AddInstruction(bounds_check_param_i_mod_array_len); + loop_body->AddInstruction(array_set); + + // i++; + HInstruction* add = new (&allocator_) HAdd(Primitive::kPrimInt, phi, constant_1); + loop_body->AddInstruction(add); + loop_body->AddInstruction(new (&allocator_) HGoto()); + phi->AddInput(add); + ////////////////////////////////////////////////////////////////////////////////// + + exit->AddInstruction(new (&allocator_) HExit()); + + RunBCE(); + + ASSERT_TRUE(IsRemoved(bounds_check_i_mod_10)); + ASSERT_TRUE(IsRemoved(bounds_check_i_mod_1)); + ASSERT_TRUE(IsRemoved(bounds_check_i_mod_200)); + ASSERT_TRUE(IsRemoved(bounds_check_i_mod_minus_10)); + ASSERT_TRUE(IsRemoved(bounds_check_i_mod_array_len)); + ASSERT_FALSE(IsRemoved(bounds_check_param_i_mod_10)); +} + } // namespace art diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc index f422b9fc8b..9095ecdf16 100644 --- a/compiler/optimizing/code_generator_vector_arm64.cc +++ b/compiler/optimizing/code_generator_vector_arm64.cc @@ -15,7 +15,9 @@ */ #include "code_generator_arm64.h" + #include "mirror/array-inl.h" +#include "mirror/string.h" using namespace vixl::aarch64; // NOLINT(build/namespaces) diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc index 14782d70a1..e7aec76aff 100644 --- a/compiler/optimizing/code_generator_vector_x86.cc +++ b/compiler/optimizing/code_generator_vector_x86.cc @@ -15,7 +15,9 @@ */ #include "code_generator_x86.h" + #include "mirror/array-inl.h" +#include "mirror/string.h" namespace art { namespace x86 { diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc index 246044ebb8..c7ee81c60d 100644 --- a/compiler/optimizing/code_generator_vector_x86_64.cc +++ b/compiler/optimizing/code_generator_vector_x86_64.cc @@ -15,7 +15,9 @@ */ #include "code_generator_x86_64.h" + #include "mirror/array-inl.h" +#include "mirror/string.h" namespace art { namespace x86_64 { diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 4cea6dfdfb..2669d97d82 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -22,6 +22,7 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "intrinsics.h" #include "mirror/array-inl.h" +#include "mirror/object_array-inl.h" #include "mirror/string.h" #include "scoped_thread_state_change-inl.h" #include "thread.h" diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index d785567e0f..74be954a75 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -22,6 +22,7 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "intrinsics.h" #include "mirror/array-inl.h" +#include "mirror/object_array-inl.h" #include "mirror/string.h" #include "scoped_thread_state_change-inl.h" #include "thread.h" diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h index 86fb8e0165..a2c17944f2 100644 --- a/compiler/optimizing/load_store_analysis.h +++ b/compiler/optimizing/load_store_analysis.h @@ -466,6 +466,11 @@ class HeapLocationCollector : public HGraphVisitor { CreateReferenceInfoForReferenceType(new_instance); } + void VisitNewArray(HNewArray* new_array) OVERRIDE { + // Any references appearing in the ref_info_array_ so far cannot alias with new_array. + CreateReferenceInfoForReferenceType(new_array); + } + void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* instruction) OVERRIDE { CreateReferenceInfoForReferenceType(instruction); } @@ -478,6 +483,22 @@ class HeapLocationCollector : public HGraphVisitor { CreateReferenceInfoForReferenceType(instruction); } + void VisitInvokeUnresolved(HInvokeUnresolved* instruction) OVERRIDE { + CreateReferenceInfoForReferenceType(instruction); + } + + void VisitInvokePolymorphic(HInvokePolymorphic* instruction) OVERRIDE { + CreateReferenceInfoForReferenceType(instruction); + } + + void VisitLoadString(HLoadString* instruction) OVERRIDE { + CreateReferenceInfoForReferenceType(instruction); + } + + void VisitPhi(HPhi* instruction) OVERRIDE { + CreateReferenceInfoForReferenceType(instruction); + } + void VisitParameterValue(HParameterValue* instruction) OVERRIDE { CreateReferenceInfoForReferenceType(instruction); } diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc index 5b9350689e..b5b03d8f26 100644 --- a/compiler/optimizing/loop_optimization_test.cc +++ b/compiler/optimizing/loop_optimization_test.cc @@ -195,4 +195,44 @@ TEST_F(LoopOptimizationTest, LoopNestWithSequence) { EXPECT_EQ("[[[[[[[[[[][][][][][][][][][]]]]]]]]]]", LoopStructure()); } +// Check that SimplifyLoop() doesn't invalidate data flow when ordering loop headers' +// predecessors. +TEST_F(LoopOptimizationTest, SimplifyLoop) { + // Can't use AddLoop as we want special order for blocks predecessors. + HBasicBlock* header = new (&allocator_) HBasicBlock(graph_); + HBasicBlock* body = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(header); + graph_->AddBlock(body); + + // Control flow: make a loop back edge first in the list of predecessors. + entry_block_->RemoveSuccessor(return_block_); + body->AddSuccessor(header); + entry_block_->AddSuccessor(header); + header->AddSuccessor(body); + header->AddSuccessor(return_block_); + DCHECK(header->GetSuccessors()[1] == return_block_); + + // Data flow. + header->AddInstruction(new (&allocator_) HIf(parameter_)); + body->AddInstruction(new (&allocator_) HGoto()); + + HPhi* phi = new (&allocator_) HPhi(&allocator_, 0, 0, Primitive::kPrimInt); + HInstruction* add = new (&allocator_) HAdd(Primitive::kPrimInt, phi, parameter_); + header->AddPhi(phi); + body->AddInstruction(add); + + phi->AddInput(add); + phi->AddInput(parameter_); + + graph_->ClearLoopInformation(); + graph_->ClearDominanceInformation(); + graph_->BuildDominatorTree(); + + // Check that after optimizations in BuildDominatorTree()/SimplifyCFG() phi inputs + // are still mapped correctly to the block predecessors. + for (size_t i = 0, e = phi->InputCount(); i < e; i++) { + HInstruction* input = phi->InputAt(i); + ASSERT_TRUE(input->GetBlock()->Dominates(header->GetPredecessors()[i])); + } +} } // namespace art diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 3a1864b2ae..ddd798b4a5 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -358,6 +358,35 @@ void HGraph::SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor) { } } +// Reorder phi inputs to match reordering of the block's predecessors. +static void FixPhisAfterPredecessorsReodering(HBasicBlock* block, size_t first, size_t second) { + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { + HPhi* phi = it.Current()->AsPhi(); + HInstruction* first_instr = phi->InputAt(first); + HInstruction* second_instr = phi->InputAt(second); + phi->ReplaceInput(first_instr, second); + phi->ReplaceInput(second_instr, first); + } +} + +// Make sure that the first predecessor of a loop header is the incoming block. +void HGraph::OrderLoopHeaderPredecessors(HBasicBlock* header) { + DCHECK(header->IsLoopHeader()); + HLoopInformation* info = header->GetLoopInformation(); + if (info->IsBackEdge(*header->GetPredecessors()[0])) { + HBasicBlock* to_swap = header->GetPredecessors()[0]; + for (size_t pred = 1, e = header->GetPredecessors().size(); pred < e; ++pred) { + HBasicBlock* predecessor = header->GetPredecessors()[pred]; + if (!info->IsBackEdge(*predecessor)) { + header->predecessors_[pred] = to_swap; + header->predecessors_[0] = predecessor; + FixPhisAfterPredecessorsReodering(header, 0, pred); + break; + } + } + } +} + void HGraph::SimplifyLoop(HBasicBlock* header) { HLoopInformation* info = header->GetLoopInformation(); @@ -381,18 +410,7 @@ void HGraph::SimplifyLoop(HBasicBlock* header) { pre_header->AddSuccessor(header); } - // Make sure the first predecessor of a loop header is the incoming block. - if (info->IsBackEdge(*header->GetPredecessors()[0])) { - HBasicBlock* to_swap = header->GetPredecessors()[0]; - for (size_t pred = 1, e = header->GetPredecessors().size(); pred < e; ++pred) { - HBasicBlock* predecessor = header->GetPredecessors()[pred]; - if (!info->IsBackEdge(*predecessor)) { - header->predecessors_[pred] = to_swap; - header->predecessors_[0] = predecessor; - break; - } - } - } + OrderLoopHeaderPredecessors(header); HInstruction* first_instruction = header->GetFirstInstruction(); if (first_instruction != nullptr && first_instruction->IsSuspendCheck()) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index e4431422b2..488d47269b 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -418,6 +418,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { HBasicBlock* SplitEdge(HBasicBlock* block, HBasicBlock* successor); void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor); + void OrderLoopHeaderPredecessors(HBasicBlock* header); void SimplifyLoop(HBasicBlock* header); int32_t GetNextInstructionId() { diff --git a/compiler/optimizing/scheduler_arm.cc b/compiler/optimizing/scheduler_arm.cc index ea15790105..d6eb6e3c52 100644 --- a/compiler/optimizing/scheduler_arm.cc +++ b/compiler/optimizing/scheduler_arm.cc @@ -20,6 +20,7 @@ #include "code_generator_utils.h" #include "common_arm.h" #include "mirror/array-inl.h" +#include "mirror/string.h" namespace art { namespace arm { diff --git a/compiler/optimizing/scheduler_arm64.cc b/compiler/optimizing/scheduler_arm64.cc index f54d3f3de2..510619faf9 100644 --- a/compiler/optimizing/scheduler_arm64.cc +++ b/compiler/optimizing/scheduler_arm64.cc @@ -18,6 +18,7 @@ #include "code_generator_utils.h" #include "mirror/array-inl.h" +#include "mirror/string.h" namespace art { namespace arm64 { diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index e0ad64946e..ff193e933a 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -355,6 +355,9 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(""); UsageError(" --debuggable: Produce code debuggable with Java debugger."); UsageError(""); + UsageError(" --avoid-storing-invocation: Avoid storing the invocation args in the key value"); + UsageError(" store. Used to test determinism with different args."); + UsageError(""); UsageError(" --runtime-arg <argument>: used to specify various arguments for the runtime,"); UsageError(" such as initial heap size, maximum heap size, and verbose output."); UsageError(" Use a separate --runtime-arg switch for each argument."); @@ -611,6 +614,7 @@ class Dex2Oat FINAL { dump_passes_(false), dump_timing_(false), dump_slow_timing_(kIsDebugBuild), + avoid_storing_invocation_(false), swap_fd_(kInvalidFd), app_image_fd_(kInvalidFd), profile_file_fd_(kInvalidFd), @@ -1133,14 +1137,16 @@ class Dex2Oat FINAL { void InsertCompileOptions(int argc, char** argv) { std::ostringstream oss; - for (int i = 0; i < argc; ++i) { - if (i > 0) { - oss << ' '; + if (!avoid_storing_invocation_) { + for (int i = 0; i < argc; ++i) { + if (i > 0) { + oss << ' '; + } + oss << argv[i]; } - oss << argv[i]; + key_value_store_->Put(OatHeader::kDex2OatCmdLineKey, oss.str()); + oss.str(""); // Reset. } - key_value_store_->Put(OatHeader::kDex2OatCmdLineKey, oss.str()); - oss.str(""); // Reset. oss << kRuntimeISA; key_value_store_->Put(OatHeader::kDex2OatHostKey, oss.str()); key_value_store_->Put( @@ -1271,6 +1277,8 @@ class Dex2Oat FINAL { dump_passes_ = true; } else if (option == "--dump-stats") { dump_stats_ = true; + } else if (option == "--avoid-storing-invocation") { + avoid_storing_invocation_ = true; } else if (option.starts_with("--swap-file=")) { swap_file_name_ = option.substr(strlen("--swap-file=")).data(); } else if (option.starts_with("--swap-fd=")) { @@ -1308,7 +1316,7 @@ class Dex2Oat FINAL { } else if (option.starts_with("--class-loader-context=")) { class_loader_context_ = ClassLoaderContext::Create( option.substr(strlen("--class-loader-context=")).data()); - if (class_loader_context_== nullptr) { + if (class_loader_context_ == nullptr) { Usage("Option --class-loader-context has an incorrect format: %s", option.data()); } } else if (option.starts_with("--dirty-image-objects=")) { @@ -1576,20 +1584,12 @@ class Dex2Oat FINAL { } // Open dex files for class path. + if (class_loader_context_ == nullptr) { - // TODO(calin): Temporary workaround while we transition to use - // --class-loader-context instead of --runtime-arg -cp - if (runtime_->GetClassPathString().empty()) { - class_loader_context_ = std::unique_ptr<ClassLoaderContext>( - new ClassLoaderContext()); - } else { - std::string spec = runtime_->GetClassPathString() == OatFile::kSpecialSharedLibrary - ? OatFile::kSpecialSharedLibrary - : "PCL[" + runtime_->GetClassPathString() + "]"; - class_loader_context_ = ClassLoaderContext::Create(spec); - } + // If no context was specified use the default one (which is an empty PathClassLoader). + class_loader_context_ = std::unique_ptr<ClassLoaderContext>(ClassLoaderContext::Default()); } - CHECK(class_loader_context_ != nullptr); + DCHECK_EQ(oat_writers_.size(), 1u); // Note: Ideally we would reject context where the source dex files are also @@ -2899,6 +2899,7 @@ class Dex2Oat FINAL { bool dump_passes_; bool dump_timing_; bool dump_slow_timing_; + bool avoid_storing_invocation_; std::string swap_file_name_; int swap_fd_; size_t min_dex_files_for_swap_ = kDefaultMinDexFilesForSwap; diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 99168c9bc5..f8b1f5375a 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -89,6 +89,8 @@ const char* image_methods_descriptions_[] = { "kSaveRefsOnlyMethod", "kSaveRefsAndArgsMethod", "kSaveEverythingMethod", + "kSaveEverythingMethodForClinit", + "kSaveEverythingMethodForSuspendCheck", }; const char* image_roots_descriptions_[] = { diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc index bfac8c422d..1ba4070056 100644 --- a/runtime/arch/arch_test.cc +++ b/runtime/arch/arch_test.cc @@ -88,6 +88,11 @@ static constexpr size_t kFrameSizeSaveRefsOnly = FRAME_SIZE_SAVE_REFS_ONLY; #undef FRAME_SIZE_SAVE_REFS_ONLY static constexpr size_t kFrameSizeSaveRefsAndArgs = FRAME_SIZE_SAVE_REFS_AND_ARGS; #undef FRAME_SIZE_SAVE_REFS_AND_ARGS +static constexpr size_t kFrameSizeSaveEverythingForClinit = FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT; +#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT +static constexpr size_t kFrameSizeSaveEverythingForSuspendCheck = + FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK; +#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING; #undef FRAME_SIZE_SAVE_EVERYTHING #undef BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET @@ -109,6 +114,11 @@ static constexpr size_t kFrameSizeSaveRefsOnly = FRAME_SIZE_SAVE_REFS_ONLY; #undef FRAME_SIZE_SAVE_REFS_ONLY static constexpr size_t kFrameSizeSaveRefsAndArgs = FRAME_SIZE_SAVE_REFS_AND_ARGS; #undef FRAME_SIZE_SAVE_REFS_AND_ARGS +static constexpr size_t kFrameSizeSaveEverythingForClinit = FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT; +#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT +static constexpr size_t kFrameSizeSaveEverythingForSuspendCheck = + FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK; +#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING; #undef FRAME_SIZE_SAVE_EVERYTHING #undef BAKER_MARK_INTROSPECTION_ARRAY_SWITCH_OFFSET @@ -126,6 +136,11 @@ static constexpr size_t kFrameSizeSaveRefsOnly = FRAME_SIZE_SAVE_REFS_ONLY; #undef FRAME_SIZE_SAVE_REFS_ONLY static constexpr size_t kFrameSizeSaveRefsAndArgs = FRAME_SIZE_SAVE_REFS_AND_ARGS; #undef FRAME_SIZE_SAVE_REFS_AND_ARGS +static constexpr size_t kFrameSizeSaveEverythingForClinit = FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT; +#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT +static constexpr size_t kFrameSizeSaveEverythingForSuspendCheck = + FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK; +#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING; #undef FRAME_SIZE_SAVE_EVERYTHING #undef BAKER_MARK_INTROSPECTION_REGISTER_COUNT @@ -142,6 +157,11 @@ static constexpr size_t kFrameSizeSaveRefsOnly = FRAME_SIZE_SAVE_REFS_ONLY; #undef FRAME_SIZE_SAVE_REFS_ONLY static constexpr size_t kFrameSizeSaveRefsAndArgs = FRAME_SIZE_SAVE_REFS_AND_ARGS; #undef FRAME_SIZE_SAVE_REFS_AND_ARGS +static constexpr size_t kFrameSizeSaveEverythingForClinit = FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT; +#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT +static constexpr size_t kFrameSizeSaveEverythingForSuspendCheck = + FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK; +#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING; #undef FRAME_SIZE_SAVE_EVERYTHING #undef BAKER_MARK_INTROSPECTION_REGISTER_COUNT @@ -158,6 +178,11 @@ static constexpr size_t kFrameSizeSaveRefsOnly = FRAME_SIZE_SAVE_REFS_ONLY; #undef FRAME_SIZE_SAVE_REFS_ONLY static constexpr size_t kFrameSizeSaveRefsAndArgs = FRAME_SIZE_SAVE_REFS_AND_ARGS; #undef FRAME_SIZE_SAVE_REFS_AND_ARGS +static constexpr size_t kFrameSizeSaveEverythingForClinit = FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT; +#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT +static constexpr size_t kFrameSizeSaveEverythingForSuspendCheck = + FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK; +#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING; #undef FRAME_SIZE_SAVE_EVERYTHING } // namespace x86 @@ -170,25 +195,36 @@ static constexpr size_t kFrameSizeSaveRefsOnly = FRAME_SIZE_SAVE_REFS_ONLY; #undef FRAME_SIZE_SAVE_REFS_ONLY static constexpr size_t kFrameSizeSaveRefsAndArgs = FRAME_SIZE_SAVE_REFS_AND_ARGS; #undef FRAME_SIZE_SAVE_REFS_AND_ARGS +static constexpr size_t kFrameSizeSaveEverythingForClinit = FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT; +#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT +static constexpr size_t kFrameSizeSaveEverythingForSuspendCheck = + FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK; +#undef FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK static constexpr size_t kFrameSizeSaveEverything = FRAME_SIZE_SAVE_EVERYTHING; #undef FRAME_SIZE_SAVE_EVERYTHING } // namespace x86_64 // Check architecture specific constants are sound. -#define TEST_ARCH(Arch, arch) \ - TEST_F(ArchTest, Arch) { \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveAllCalleeSaves, \ - arch::kFrameSizeSaveAllCalleeSaves); \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveRefsOnly, \ - arch::kFrameSizeSaveRefsOnly); \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveRefsAndArgs, \ - arch::kFrameSizeSaveRefsAndArgs); \ - CheckFrameSize(InstructionSet::k##Arch, \ - CalleeSaveType::kSaveEverything, \ - arch::kFrameSizeSaveEverything); \ +#define TEST_ARCH(Arch, arch) \ + TEST_F(ArchTest, Arch) { \ + CheckFrameSize(InstructionSet::k##Arch, \ + CalleeSaveType::kSaveAllCalleeSaves, \ + arch::kFrameSizeSaveAllCalleeSaves); \ + CheckFrameSize(InstructionSet::k##Arch, \ + CalleeSaveType::kSaveRefsOnly, \ + arch::kFrameSizeSaveRefsOnly); \ + CheckFrameSize(InstructionSet::k##Arch, \ + CalleeSaveType::kSaveRefsAndArgs, \ + arch::kFrameSizeSaveRefsAndArgs); \ + CheckFrameSize(InstructionSet::k##Arch, \ + CalleeSaveType::kSaveEverything, \ + arch::kFrameSizeSaveEverything); \ + CheckFrameSize(InstructionSet::k##Arch, \ + CalleeSaveType::kSaveEverythingForClinit, \ + arch::kFrameSizeSaveEverythingForClinit); \ + CheckFrameSize(InstructionSet::k##Arch, \ + CalleeSaveType::kSaveEverythingForSuspendCheck, \ + arch::kFrameSizeSaveEverythingForSuspendCheck); \ } TEST_ARCH(Arm, arm) TEST_ARCH(Arm64, arm64) diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h index 8f2fd6ecc9..3d85872617 100644 --- a/runtime/arch/arm/asm_support_arm.h +++ b/runtime/arch/arm/asm_support_arm.h @@ -23,6 +23,8 @@ #define FRAME_SIZE_SAVE_REFS_ONLY 32 #define FRAME_SIZE_SAVE_REFS_AND_ARGS 112 #define FRAME_SIZE_SAVE_EVERYTHING 192 +#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING +#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING // The offset from the art_quick_read_barrier_mark_introspection (used for field // loads with 32-bit LDR) to the entrypoint for field loads with 16-bit LDR, diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 375768ec3f..cf2bfeeeef 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -182,7 +182,7 @@ * Runtime::CreateCalleeSaveMethod(kSaveEverything) * when core registers are already saved. */ -.macro SETUP_SAVE_EVERYTHING_FRAME_CORE_REGS_SAVED rTemp +.macro SETUP_SAVE_EVERYTHING_FRAME_CORE_REGS_SAVED rTemp, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET @ 14 words of callee saves and args already saved. vpush {d0-d15} @ 32 words, 2 for each of the 16 saved doubles. .cfi_adjust_cfa_offset 128 @@ -190,7 +190,7 @@ .cfi_adjust_cfa_offset 8 RUNTIME_CURRENT1 \rTemp @ Load Runtime::Current into rTemp. @ Load kSaveEverything Method* into rTemp. - ldr \rTemp, [\rTemp, #RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET] + ldr \rTemp, [\rTemp, #\runtime_method_offset] str \rTemp, [sp, #0] @ Place Method* at bottom of stack. str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. @@ -204,7 +204,7 @@ * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kSaveEverything) */ -.macro SETUP_SAVE_EVERYTHING_FRAME rTemp +.macro SETUP_SAVE_EVERYTHING_FRAME rTemp, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET push {r0-r12, lr} @ 14 words of callee saves and args. .cfi_adjust_cfa_offset 56 .cfi_rel_offset r0, 0 @@ -221,7 +221,7 @@ .cfi_rel_offset r11, 44 .cfi_rel_offset ip, 48 .cfi_rel_offset lr, 52 - SETUP_SAVE_EVERYTHING_FRAME_CORE_REGS_SAVED \rTemp + SETUP_SAVE_EVERYTHING_FRAME_CORE_REGS_SAVED \rTemp, \runtime_method_offset .endm .macro RESTORE_SAVE_EVERYTHING_FRAME @@ -1004,10 +1004,10 @@ END \name .endm // Macro for string and type resolution and initialization. -.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint +.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY \name - SETUP_SAVE_EVERYTHING_FRAME r1 @ save everything in case of GC + SETUP_SAVE_EVERYTHING_FRAME r1, \runtime_method_offset @ save everything in case of GC mov r1, r9 @ pass Thread::Current bl \entrypoint @ (uint32_t index, Thread*) cbz r0, 1f @ If result is null, deliver the OOME. @@ -1021,8 +1021,12 @@ ENTRY \name END \name .endm -ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode -ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode +.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT name, entrypoint + ONE_ARG_SAVE_EVERYTHING_DOWNCALL \name, \entrypoint, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET +.endm + +ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode @@ -1537,7 +1541,7 @@ ENTRY art_quick_test_suspend 1: mov rSUSPEND, #SUSPEND_CHECK_INTERVAL @ reset rSUSPEND to SUSPEND_CHECK_INTERVAL #endif - SETUP_SAVE_EVERYTHING_FRAME r0 @ save everything for GC stack crawl + SETUP_SAVE_EVERYTHING_FRAME r0, RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET @ save everything for GC stack crawl mov r0, rSELF bl artTestSuspendFromCode @ (Thread*) RESTORE_SAVE_EVERYTHING_FRAME diff --git a/runtime/arch/arm/quick_method_frame_info_arm.h b/runtime/arch/arm/quick_method_frame_info_arm.h index 39061f0d4d..5c5b81baae 100644 --- a/runtime/arch/arm/quick_method_frame_info_arm.h +++ b/runtime/arch/arm/quick_method_frame_info_arm.h @@ -56,6 +56,7 @@ static constexpr uint32_t kArmCalleeSaveFpEverythingSpills = kArmCalleeSaveFpArgSpills | kArmCalleeSaveFpAllSpills; constexpr uint32_t ArmCalleeSaveCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return kArmCalleeSaveAlwaysSpills | kArmCalleeSaveRefSpills | (type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveArgSpills : 0) | (type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveAllSpills : 0) | @@ -63,6 +64,7 @@ constexpr uint32_t ArmCalleeSaveCoreSpills(CalleeSaveType type) { } constexpr uint32_t ArmCalleeSaveFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return kArmCalleeSaveFpAlwaysSpills | kArmCalleeSaveFpRefSpills | (type == CalleeSaveType::kSaveRefsAndArgs ? kArmCalleeSaveFpArgSpills : 0) | (type == CalleeSaveType::kSaveAllCalleeSaves ? kArmCalleeSaveFpAllSpills : 0) | @@ -70,29 +72,34 @@ constexpr uint32_t ArmCalleeSaveFpSpills(CalleeSaveType type) { } constexpr uint32_t ArmCalleeSaveFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return RoundUp((POPCOUNT(ArmCalleeSaveCoreSpills(type)) /* gprs */ + POPCOUNT(ArmCalleeSaveFpSpills(type)) /* fprs */ + 1 /* Method* */) * static_cast<size_t>(kArmPointerSize), kStackAlignment); } constexpr QuickMethodFrameInfo ArmCalleeSaveMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return QuickMethodFrameInfo(ArmCalleeSaveFrameSize(type), ArmCalleeSaveCoreSpills(type), ArmCalleeSaveFpSpills(type)); } constexpr size_t ArmCalleeSaveFpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return ArmCalleeSaveFrameSize(type) - (POPCOUNT(ArmCalleeSaveCoreSpills(type)) + POPCOUNT(ArmCalleeSaveFpSpills(type))) * static_cast<size_t>(kArmPointerSize); } constexpr size_t ArmCalleeSaveGpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return ArmCalleeSaveFrameSize(type) - POPCOUNT(ArmCalleeSaveCoreSpills(type)) * static_cast<size_t>(kArmPointerSize); } constexpr size_t ArmCalleeSaveLrOffset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return ArmCalleeSaveFrameSize(type) - POPCOUNT(ArmCalleeSaveCoreSpills(type) & (-(1 << LR))) * static_cast<size_t>(kArmPointerSize); } diff --git a/runtime/arch/arm64/asm_support_arm64.h b/runtime/arch/arm64/asm_support_arm64.h index 6b7720023e..57376d0c4f 100644 --- a/runtime/arch/arm64/asm_support_arm64.h +++ b/runtime/arch/arm64/asm_support_arm64.h @@ -23,6 +23,8 @@ #define FRAME_SIZE_SAVE_REFS_ONLY 96 #define FRAME_SIZE_SAVE_REFS_AND_ARGS 224 #define FRAME_SIZE_SAVE_EVERYTHING 512 +#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING +#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING // The offset from art_quick_read_barrier_mark_introspection to the array switch cases, // i.e. art_quick_read_barrier_mark_introspection_arrays. diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index d15f5b85ec..3d8ca402cf 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -287,7 +287,7 @@ * when the SP has already been decremented by FRAME_SIZE_SAVE_EVERYTHING * and saving registers x29 and LR is handled elsewhere. */ -.macro SETUP_SAVE_EVERYTHING_FRAME_DECREMENTED_SP_SKIP_X29_LR +.macro SETUP_SAVE_EVERYTHING_FRAME_DECREMENTED_SP_SKIP_X29_LR runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_SAVE_EVERYTHING != 512) #error "FRAME_SIZE_SAVE_EVERYTHING(ARM64) size not as expected." @@ -337,7 +337,7 @@ ldr xIP0, [xIP0] // art::Runtime* xIP0 = art::Runtime::instance_; // ArtMethod* xIP0 = Runtime::instance_->callee_save_methods_[kSaveEverything]; - ldr xIP0, [xIP0, RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET] + ldr xIP0, [xIP0, \runtime_method_offset] // Store ArtMethod* Runtime::callee_save_methods_[kSaveEverything]. str xIP0, [sp] @@ -350,10 +350,10 @@ * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kSaveEverything) */ -.macro SETUP_SAVE_EVERYTHING_FRAME +.macro SETUP_SAVE_EVERYTHING_FRAME runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET INCREASE_FRAME 512 SAVE_TWO_REGS x29, xLR, 496 - SETUP_SAVE_EVERYTHING_FRAME_DECREMENTED_SP_SKIP_X29_LR + SETUP_SAVE_EVERYTHING_FRAME_DECREMENTED_SP_SKIP_X29_LR \runtime_method_offset .endm .macro RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0 @@ -398,7 +398,7 @@ .endm .macro RESTORE_SAVE_EVERYTHING_FRAME - RESTORE_REG x0, 264 + RESTORE_REG x0, 264 RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0 .endm @@ -1593,10 +1593,10 @@ END \name .endm // Macro for string and type resolution and initialization. -.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint +.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET .extern \entrypoint ENTRY \name - SETUP_SAVE_EVERYTHING_FRAME // save everything for stack crawl + SETUP_SAVE_EVERYTHING_FRAME \runtime_method_offset // save everything for stack crawl mov x1, xSELF // pass Thread::Current bl \entrypoint // (int32_t index, Thread* self) cbz w0, 1f // If result is null, deliver the OOME. @@ -1611,6 +1611,10 @@ ENTRY \name END \name .endm +.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT name, entrypoint + ONE_ARG_SAVE_EVERYTHING_DOWNCALL \name, \entrypoint, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET +.endm + .macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER cbz w0, 1f // result zero branch over ret // return @@ -1629,9 +1633,8 @@ TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, * initializer and deliver the exception on error. On success the static storage base is * returned. */ -ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode - -ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode @@ -2008,7 +2011,7 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFr */ .extern artTestSuspendFromCode ENTRY art_quick_test_suspend - SETUP_SAVE_EVERYTHING_FRAME // save callee saves for stack crawl + SETUP_SAVE_EVERYTHING_FRAME RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET // save callee saves for stack crawl mov x0, xSELF bl artTestSuspendFromCode // (Thread*) RESTORE_SAVE_EVERYTHING_FRAME diff --git a/runtime/arch/arm64/quick_method_frame_info_arm64.h b/runtime/arch/arm64/quick_method_frame_info_arm64.h index c231d4d3d4..3e6f6c6e3b 100644 --- a/runtime/arch/arm64/quick_method_frame_info_arm64.h +++ b/runtime/arch/arm64/quick_method_frame_info_arm64.h @@ -80,6 +80,7 @@ static constexpr uint32_t kArm64CalleeSaveFpEverythingSpills = (1 << art::arm64::D30) | (1 << art::arm64::D31); constexpr uint32_t Arm64CalleeSaveCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return kArm64CalleeSaveAlwaysSpills | kArm64CalleeSaveRefSpills | (type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveArgSpills : 0) | (type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveAllSpills : 0) | @@ -87,6 +88,7 @@ constexpr uint32_t Arm64CalleeSaveCoreSpills(CalleeSaveType type) { } constexpr uint32_t Arm64CalleeSaveFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return kArm64CalleeSaveFpAlwaysSpills | kArm64CalleeSaveFpRefSpills | (type == CalleeSaveType::kSaveRefsAndArgs ? kArm64CalleeSaveFpArgSpills : 0) | (type == CalleeSaveType::kSaveAllCalleeSaves ? kArm64CalleeSaveFpAllSpills : 0) | @@ -94,29 +96,34 @@ constexpr uint32_t Arm64CalleeSaveFpSpills(CalleeSaveType type) { } constexpr uint32_t Arm64CalleeSaveFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return RoundUp((POPCOUNT(Arm64CalleeSaveCoreSpills(type)) /* gprs */ + POPCOUNT(Arm64CalleeSaveFpSpills(type)) /* fprs */ + 1 /* Method* */) * static_cast<size_t>(kArm64PointerSize), kStackAlignment); } constexpr QuickMethodFrameInfo Arm64CalleeSaveMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return QuickMethodFrameInfo(Arm64CalleeSaveFrameSize(type), Arm64CalleeSaveCoreSpills(type), Arm64CalleeSaveFpSpills(type)); } constexpr size_t Arm64CalleeSaveFpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return Arm64CalleeSaveFrameSize(type) - (POPCOUNT(Arm64CalleeSaveCoreSpills(type)) + POPCOUNT(Arm64CalleeSaveFpSpills(type))) * static_cast<size_t>(kArm64PointerSize); } constexpr size_t Arm64CalleeSaveGpr1Offset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return Arm64CalleeSaveFrameSize(type) - POPCOUNT(Arm64CalleeSaveCoreSpills(type)) * static_cast<size_t>(kArm64PointerSize); } constexpr size_t Arm64CalleeSaveLrOffset(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return Arm64CalleeSaveFrameSize(type) - POPCOUNT(Arm64CalleeSaveCoreSpills(type) & (-(1 << LR))) * static_cast<size_t>(kArm64PointerSize); diff --git a/runtime/arch/mips/asm_support_mips.h b/runtime/arch/mips/asm_support_mips.h index 9d8572ffb5..2edd63f58a 100644 --- a/runtime/arch/mips/asm_support_mips.h +++ b/runtime/arch/mips/asm_support_mips.h @@ -23,6 +23,8 @@ #define FRAME_SIZE_SAVE_REFS_ONLY 48 #define FRAME_SIZE_SAVE_REFS_AND_ARGS 112 #define FRAME_SIZE_SAVE_EVERYTHING 256 +#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING +#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING // &art_quick_read_barrier_mark_introspection is the first of many entry points: // 21 entry points for long field offsets, large array indices and variable array indices diff --git a/runtime/arch/mips/quick_method_frame_info_mips.h b/runtime/arch/mips/quick_method_frame_info_mips.h index 01879a5cea..45a21ab942 100644 --- a/runtime/arch/mips/quick_method_frame_info_mips.h +++ b/runtime/arch/mips/quick_method_frame_info_mips.h @@ -65,6 +65,7 @@ static constexpr uint32_t kMipsCalleeSaveFpEverythingSpills = (1 << art::mips::F28) | (1 << art::mips::F29) | (1 << art::mips::F30) | (1u << art::mips::F31); constexpr uint32_t MipsCalleeSaveCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return kMipsCalleeSaveAlwaysSpills | kMipsCalleeSaveRefSpills | (type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveArgSpills : 0) | (type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllSpills : 0) | @@ -72,6 +73,7 @@ constexpr uint32_t MipsCalleeSaveCoreSpills(CalleeSaveType type) { } constexpr uint32_t MipsCalleeSaveFPSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return kMipsCalleeSaveFpAlwaysSpills | kMipsCalleeSaveFpRefSpills | (type == CalleeSaveType::kSaveRefsAndArgs ? kMipsCalleeSaveFpArgSpills : 0) | (type == CalleeSaveType::kSaveAllCalleeSaves ? kMipsCalleeSaveAllFPSpills : 0) | @@ -79,12 +81,14 @@ constexpr uint32_t MipsCalleeSaveFPSpills(CalleeSaveType type) { } constexpr uint32_t MipsCalleeSaveFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return RoundUp((POPCOUNT(MipsCalleeSaveCoreSpills(type)) /* gprs */ + POPCOUNT(MipsCalleeSaveFPSpills(type)) /* fprs */ + 1 /* Method* */) * static_cast<size_t>(kMipsPointerSize), kStackAlignment); } constexpr QuickMethodFrameInfo MipsCalleeSaveMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return QuickMethodFrameInfo(MipsCalleeSaveFrameSize(type), MipsCalleeSaveCoreSpills(type), MipsCalleeSaveFPSpills(type)); diff --git a/runtime/arch/mips64/asm_support_mips64.h b/runtime/arch/mips64/asm_support_mips64.h index 7185da550c..a8e907eca5 100644 --- a/runtime/arch/mips64/asm_support_mips64.h +++ b/runtime/arch/mips64/asm_support_mips64.h @@ -27,6 +27,8 @@ #define FRAME_SIZE_SAVE_REFS_AND_ARGS 208 // $f0-$f31, $at, $v0-$v1, $a0-$a7, $t0-$t3, $s0-$s7, $t8-$t9, $gp, $s8, $ra + padding + method* #define FRAME_SIZE_SAVE_EVERYTHING 496 +#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING +#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING // &art_quick_read_barrier_mark_introspection is the first of many entry points: // 20 entry points for long field offsets, large array indices and variable array indices diff --git a/runtime/arch/mips64/quick_method_frame_info_mips64.h b/runtime/arch/mips64/quick_method_frame_info_mips64.h index a55ab0e196..520f6319d5 100644 --- a/runtime/arch/mips64/quick_method_frame_info_mips64.h +++ b/runtime/arch/mips64/quick_method_frame_info_mips64.h @@ -72,6 +72,7 @@ static constexpr uint32_t kMips64CalleeSaveFpEverythingSpills = (1 << art::mips64::F30) | (1 << art::mips64::F31); constexpr uint32_t Mips64CalleeSaveCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return kMips64CalleeSaveAlwaysSpills | kMips64CalleeSaveRefSpills | (type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveArgSpills : 0) | (type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveAllSpills : 0) | @@ -79,6 +80,7 @@ constexpr uint32_t Mips64CalleeSaveCoreSpills(CalleeSaveType type) { } constexpr uint32_t Mips64CalleeSaveFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return kMips64CalleeSaveFpRefSpills | (type == CalleeSaveType::kSaveRefsAndArgs ? kMips64CalleeSaveFpArgSpills : 0) | (type == CalleeSaveType::kSaveAllCalleeSaves ? kMips64CalleeSaveFpAllSpills : 0) | @@ -86,12 +88,14 @@ constexpr uint32_t Mips64CalleeSaveFpSpills(CalleeSaveType type) { } constexpr uint32_t Mips64CalleeSaveFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return RoundUp((POPCOUNT(Mips64CalleeSaveCoreSpills(type)) /* gprs */ + POPCOUNT(Mips64CalleeSaveFpSpills(type)) /* fprs */ + + 1 /* Method* */) * static_cast<size_t>(kMips64PointerSize), kStackAlignment); } constexpr QuickMethodFrameInfo Mips64CalleeSaveMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return QuickMethodFrameInfo(Mips64CalleeSaveFrameSize(type), Mips64CalleeSaveCoreSpills(type), Mips64CalleeSaveFpSpills(type)); diff --git a/runtime/arch/x86/asm_support_x86.h b/runtime/arch/x86/asm_support_x86.h index 2bba08d571..737d736f01 100644 --- a/runtime/arch/x86/asm_support_x86.h +++ b/runtime/arch/x86/asm_support_x86.h @@ -23,5 +23,7 @@ #define FRAME_SIZE_SAVE_REFS_ONLY 32 #define FRAME_SIZE_SAVE_REFS_AND_ARGS (32 + 32) #define FRAME_SIZE_SAVE_EVERYTHING (48 + 64) +#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING +#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING #endif // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_ diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 48d2de9567..4e5e93ac5a 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -226,7 +226,7 @@ END_MACRO * Runtime::CreateCalleeSaveMethod(kSaveEverything) * when EDI and ESI are already saved. */ -MACRO2(SETUP_SAVE_EVERYTHING_FRAME_EDI_ESI_SAVED, got_reg, temp_reg) +MACRO3(SETUP_SAVE_EVERYTHING_FRAME_EDI_ESI_SAVED, got_reg, temp_reg, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) // Save core registers from highest to lowest to agree with core spills bitmap. // EDI and ESI, or at least placeholders for them, are already on the stack. PUSH ebp @@ -252,7 +252,7 @@ MACRO2(SETUP_SAVE_EVERYTHING_FRAME_EDI_ESI_SAVED, got_reg, temp_reg) movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg)), REG_VAR(temp_reg) movl (REG_VAR(temp_reg)), REG_VAR(temp_reg) // Push save everything callee-save method. - pushl RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET(REG_VAR(temp_reg)) + pushl \runtime_method_offset(REG_VAR(temp_reg)) CFI_ADJUST_CFA_OFFSET(4) // Store esp as the stop quick frame. movl %esp, %fs:THREAD_TOP_QUICK_FRAME_OFFSET @@ -269,20 +269,20 @@ END_MACRO * Runtime::CreateCalleeSaveMethod(kSaveEverything) * when EDI is already saved. */ -MACRO2(SETUP_SAVE_EVERYTHING_FRAME_EDI_SAVED, got_reg, temp_reg) +MACRO3(SETUP_SAVE_EVERYTHING_FRAME_EDI_SAVED, got_reg, temp_reg, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) // Save core registers from highest to lowest to agree with core spills bitmap. // EDI, or at least a placeholder for it, is already on the stack. PUSH esi - SETUP_SAVE_EVERYTHING_FRAME_EDI_ESI_SAVED RAW_VAR(got_reg), RAW_VAR(temp_reg) + SETUP_SAVE_EVERYTHING_FRAME_EDI_ESI_SAVED RAW_VAR(got_reg), RAW_VAR(temp_reg), \runtime_method_offset END_MACRO /* * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kSaveEverything) */ -MACRO2(SETUP_SAVE_EVERYTHING_FRAME, got_reg, temp_reg) +MACRO3(SETUP_SAVE_EVERYTHING_FRAME, got_reg, temp_reg, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) PUSH edi - SETUP_SAVE_EVERYTHING_FRAME_EDI_SAVED RAW_VAR(got_reg), RAW_VAR(temp_reg) + SETUP_SAVE_EVERYTHING_FRAME_EDI_SAVED RAW_VAR(got_reg), RAW_VAR(temp_reg), \runtime_method_offset END_MACRO MACRO0(RESTORE_SAVE_EVERYTHING_FRAME_FRPS) @@ -923,9 +923,9 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) END_MACRO // Macro for string and type resolution and initialization. -MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name) +MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) DEFINE_FUNCTION VAR(c_name) - SETUP_SAVE_EVERYTHING_FRAME ebx, ebx // save ref containing registers for GC + SETUP_SAVE_EVERYTHING_FRAME ebx, ebx, \runtime_method_offset // save ref containing registers for GC // Outgoing argument set up subl MACRO_LITERAL(8), %esp // push padding CFI_ADJUST_CFA_OFFSET(8) @@ -947,6 +947,10 @@ MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name) END_FUNCTION VAR(c_name) END_MACRO +MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT, c_name, cxx_name) + ONE_ARG_SAVE_EVERYTHING_DOWNCALL \c_name, \cxx_name, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET +END_MACRO + MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER) testl %eax, %eax // eax == 0 ? jz 1f // if eax == 0 goto 1 @@ -1270,8 +1274,8 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_tlab, artAllocArrayFr GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64 -ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode -ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode @@ -1598,7 +1602,7 @@ DEFINE_FUNCTION art_quick_memcpy END_FUNCTION art_quick_memcpy DEFINE_FUNCTION art_quick_test_suspend - SETUP_SAVE_EVERYTHING_FRAME ebx, ebx // save everything for GC + SETUP_SAVE_EVERYTHING_FRAME ebx, ebx, RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET // save everything for GC // Outgoing argument set up subl MACRO_LITERAL(12), %esp // push padding CFI_ADJUST_CFA_OFFSET(12) diff --git a/runtime/arch/x86/quick_method_frame_info_x86.h b/runtime/arch/x86/quick_method_frame_info_x86.h index 8342c9fe03..9a6633365c 100644 --- a/runtime/arch/x86/quick_method_frame_info_x86.h +++ b/runtime/arch/x86/quick_method_frame_info_x86.h @@ -57,23 +57,27 @@ static constexpr uint32_t kX86CalleeSaveFpEverythingSpills = (1 << art::x86::XMM6) | (1 << art::x86::XMM7); constexpr uint32_t X86CalleeSaveCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return kX86CalleeSaveAlwaysSpills | kX86CalleeSaveRefSpills | (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveArgSpills : 0) | (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveEverythingSpills : 0); } constexpr uint32_t X86CalleeSaveFpSpills(CalleeSaveType type) { - return (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveFpArgSpills : 0) | - (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveFpEverythingSpills : 0); + type = GetCanonicalCalleeSaveType(type); + return (type == CalleeSaveType::kSaveRefsAndArgs ? kX86CalleeSaveFpArgSpills : 0) | + (type == CalleeSaveType::kSaveEverything ? kX86CalleeSaveFpEverythingSpills : 0); } constexpr uint32_t X86CalleeSaveFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return RoundUp((POPCOUNT(X86CalleeSaveCoreSpills(type)) /* gprs */ + 2 * POPCOUNT(X86CalleeSaveFpSpills(type)) /* fprs */ + 1 /* Method* */) * static_cast<size_t>(kX86PointerSize), kStackAlignment); } constexpr QuickMethodFrameInfo X86CalleeSaveMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return QuickMethodFrameInfo(X86CalleeSaveFrameSize(type), X86CalleeSaveCoreSpills(type), X86CalleeSaveFpSpills(type)); diff --git a/runtime/arch/x86_64/asm_support_x86_64.h b/runtime/arch/x86_64/asm_support_x86_64.h index a4446d3345..51befbe7b8 100644 --- a/runtime/arch/x86_64/asm_support_x86_64.h +++ b/runtime/arch/x86_64/asm_support_x86_64.h @@ -23,5 +23,7 @@ #define FRAME_SIZE_SAVE_REFS_ONLY (64 + 4*8) #define FRAME_SIZE_SAVE_REFS_AND_ARGS (112 + 12*8) #define FRAME_SIZE_SAVE_EVERYTHING (144 + 16*8) +#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING +#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING #endif // ART_RUNTIME_ARCH_X86_64_ASM_SUPPORT_X86_64_H_ diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 0a9199e7e9..73e86100f6 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -272,7 +272,7 @@ END_MACRO * Runtime::CreateCalleeSaveMethod(kSaveEverything) * when R14 and R15 are already saved. */ -MACRO0(SETUP_SAVE_EVERYTHING_FRAME_R14_R15_SAVED) +MACRO1(SETUP_SAVE_EVERYTHING_FRAME_R14_R15_SAVED, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) #if defined(__APPLE__) int3 int3 @@ -316,7 +316,7 @@ MACRO0(SETUP_SAVE_EVERYTHING_FRAME_R14_R15_SAVED) movq %xmm14, 120(%rsp) movq %xmm15, 128(%rsp) // Push ArtMethod* for save everything frame method. - pushq RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET(%r10) + pushq \runtime_method_offset(%r10) CFI_ADJUST_CFA_OFFSET(8) // Store rsp as the top quick frame. movq %rsp, %gs:THREAD_TOP_QUICK_FRAME_OFFSET @@ -334,18 +334,18 @@ END_MACRO * Runtime::CreateCalleeSaveMethod(kSaveEverything) * when R15 is already saved. */ -MACRO0(SETUP_SAVE_EVERYTHING_FRAME_R15_SAVED) +MACRO1(SETUP_SAVE_EVERYTHING_FRAME_R15_SAVED, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) PUSH r14 - SETUP_SAVE_EVERYTHING_FRAME_R14_R15_SAVED + SETUP_SAVE_EVERYTHING_FRAME_R14_R15_SAVED \runtime_method_offset END_MACRO /* * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kSaveEverything) */ -MACRO0(SETUP_SAVE_EVERYTHING_FRAME) +MACRO1(SETUP_SAVE_EVERYTHING_FRAME, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) PUSH r15 - SETUP_SAVE_EVERYTHING_FRAME_R15_SAVED + SETUP_SAVE_EVERYTHING_FRAME_R15_SAVED \runtime_method_offset END_MACRO MACRO0(RESTORE_SAVE_EVERYTHING_FRAME_FRPS) @@ -951,9 +951,9 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) END_MACRO // Macro for string and type resolution and initialization. -MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name) +MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET) DEFINE_FUNCTION VAR(c_name) - SETUP_SAVE_EVERYTHING_FRAME // save everything for GC + SETUP_SAVE_EVERYTHING_FRAME \runtime_method_offset // save everything for GC // Outgoing argument set up movl %eax, %edi // pass string index movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current() @@ -970,6 +970,10 @@ MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name) END_FUNCTION VAR(c_name) END_MACRO +MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT, c_name, cxx_name) + ONE_ARG_SAVE_EVERYTHING_DOWNCALL \c_name, \cxx_name, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET +END_MACRO + MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER) testq %rax, %rax // rax == 0 ? jz 1f // if rax == 0 goto 1 @@ -1290,8 +1294,8 @@ DEFINE_FUNCTION art_quick_alloc_object_initialized_region_tlab ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeInitializedRegionTLAB END_FUNCTION art_quick_alloc_object_initialized_region_tlab -ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode -ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode @@ -1575,7 +1579,7 @@ DEFINE_FUNCTION art_quick_memcpy END_FUNCTION art_quick_memcpy DEFINE_FUNCTION art_quick_test_suspend - SETUP_SAVE_EVERYTHING_FRAME // save everything for GC + SETUP_SAVE_EVERYTHING_FRAME RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET // save everything for GC // Outgoing argument set up movq %gs:THREAD_SELF_OFFSET, %rdi // pass Thread::Current() call SYMBOL(artTestSuspendFromCode) // (Thread*) diff --git a/runtime/arch/x86_64/quick_method_frame_info_x86_64.h b/runtime/arch/x86_64/quick_method_frame_info_x86_64.h index 425d616e76..ebf976ef71 100644 --- a/runtime/arch/x86_64/quick_method_frame_info_x86_64.h +++ b/runtime/arch/x86_64/quick_method_frame_info_x86_64.h @@ -56,24 +56,28 @@ static constexpr uint32_t kX86_64CalleeSaveFpEverythingSpills = (1 << art::x86_64::XMM10) | (1 << art::x86_64::XMM11); constexpr uint32_t X86_64CalleeSaveCoreSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return kX86_64CalleeSaveAlwaysSpills | kX86_64CalleeSaveRefSpills | (type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveArgSpills : 0) | (type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveEverythingSpills : 0); } constexpr uint32_t X86_64CalleeSaveFpSpills(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return kX86_64CalleeSaveFpSpills | (type == CalleeSaveType::kSaveRefsAndArgs ? kX86_64CalleeSaveFpArgSpills : 0) | (type == CalleeSaveType::kSaveEverything ? kX86_64CalleeSaveFpEverythingSpills : 0); } constexpr uint32_t X86_64CalleeSaveFrameSize(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return RoundUp((POPCOUNT(X86_64CalleeSaveCoreSpills(type)) /* gprs */ + POPCOUNT(X86_64CalleeSaveFpSpills(type)) /* fprs */ + 1 /* Method* */) * static_cast<size_t>(kX86_64PointerSize), kStackAlignment); } constexpr QuickMethodFrameInfo X86_64CalleeSaveMethodFrameInfo(CalleeSaveType type) { + type = GetCanonicalCalleeSaveType(type); return QuickMethodFrameInfo(X86_64CalleeSaveFrameSize(type), X86_64CalleeSaveCoreSpills(type), X86_64CalleeSaveFpSpills(type)); diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index fad927804e..50e91447a9 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -285,6 +285,10 @@ inline const char* ArtMethod::GetName() { return "<runtime internal callee-save reference and argument registers method>"; } else if (this == runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverything)) { return "<runtime internal save-every-register method>"; + } else if (this == runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForClinit)) { + return "<runtime internal save-every-register method for clinit>"; + } else if (this == runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForSuspendCheck)) { + return "<runtime internal save-every-register method for suspend check>"; } else { return "<unknown runtime internal method>"; } diff --git a/runtime/base/callee_save_type.h b/runtime/base/callee_save_type.h index 501b296d4f..e9cd63c3a0 100644 --- a/runtime/base/callee_save_type.h +++ b/runtime/base/callee_save_type.h @@ -28,10 +28,20 @@ enum class CalleeSaveType : uint32_t { kSaveRefsOnly, // Only those callee-save registers that can hold references. kSaveRefsAndArgs, // References (see above) and arguments (usually caller-save registers). kSaveEverything, // All registers, including both callee-save and caller-save. + kSaveEverythingForClinit, // Special kSaveEverything for clinit. + kSaveEverythingForSuspendCheck, // Special kSaveEverything for suspend check. kLastCalleeSaveType // Value used for iteration. }; std::ostream& operator<<(std::ostream& os, const CalleeSaveType& rhs); +static inline constexpr CalleeSaveType GetCanonicalCalleeSaveType(CalleeSaveType type) { + if (type == CalleeSaveType::kSaveEverythingForClinit || + type == CalleeSaveType::kSaveEverythingForSuspendCheck) { + return CalleeSaveType::kSaveEverything; + } + return type; +} + } // namespace art #endif // ART_RUNTIME_BASE_CALLEE_SAVE_TYPE_H_ diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index 0c73ce7ef2..eb8ced00a8 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -438,4 +438,30 @@ bool FdFile::ResetOffset() { return true; } +int FdFile::Compare(FdFile* other) { + int64_t length = GetLength(); + int64_t length2 = other->GetLength(); + if (length != length2) { + return length < length2 ? -1 : 1; + } + static const size_t kBufferSize = 4096; + std::unique_ptr<uint8_t[]> buffer1(new uint8_t[kBufferSize]); + std::unique_ptr<uint8_t[]> buffer2(new uint8_t[kBufferSize]); + while (length > 0) { + size_t len = std::min(kBufferSize, static_cast<size_t>(length)); + if (!ReadFully(&buffer1[0], len)) { + return -1; + } + if (!other->ReadFully(&buffer2[0], len)) { + return 1; + } + int result = memcmp(&buffer1[0], &buffer2[0], len); + if (result != 0) { + return result; + } + length -= len; + } + return 0; +} + } // namespace unix_file diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h index e07c3fd800..91b08bcb4d 100644 --- a/runtime/base/unix_file/fd_file.h +++ b/runtime/base/unix_file/fd_file.h @@ -145,6 +145,11 @@ class FdFile : public RandomAccessFile { // WARNING: Only use this when you know what you're doing! void MarkUnchecked(); + // Compare against another file. Returns 0 if the files are equivalent, otherwise returns -1 or 1 + // depending on if the lenghts are different. If the lengths are the same, the function returns + // the difference of the first byte that differs. + int Compare(FdFile* other); + protected: // If the guard state indicates checking (!=kNoCheck), go to the target state "target". Print the // given warning if the current state is or exceeds warn_threshold. diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc index 6aef348433..8b1a115a26 100644 --- a/runtime/base/unix_file/fd_file_test.cc +++ b/runtime/base/unix_file/fd_file_test.cc @@ -220,4 +220,58 @@ TEST_F(FdFileTest, EraseWithPathUnlinks) { EXPECT_FALSE(art::OS::FileExists(filename.c_str())) << filename; } +TEST_F(FdFileTest, Compare) { + std::vector<uint8_t> buffer; + constexpr int64_t length = 17 * art::KB; + for (size_t i = 0; i < length; ++i) { + buffer.push_back(static_cast<uint8_t>(i)); + } + + auto reset_compare = [&](art::ScratchFile& a, art::ScratchFile& b) { + a.GetFile()->ResetOffset(); + b.GetFile()->ResetOffset(); + return a.GetFile()->Compare(b.GetFile()); + }; + + art::ScratchFile tmp; + EXPECT_TRUE(tmp.GetFile()->WriteFully(&buffer[0], length)); + EXPECT_EQ(tmp.GetFile()->GetLength(), length); + + art::ScratchFile tmp2; + EXPECT_TRUE(tmp2.GetFile()->WriteFully(&buffer[0], length)); + EXPECT_EQ(tmp2.GetFile()->GetLength(), length); + + // Basic equality check. + tmp.GetFile()->ResetOffset(); + tmp2.GetFile()->ResetOffset(); + // Files should be the same. + EXPECT_EQ(reset_compare(tmp, tmp2), 0); + + // Change a byte near the start. + ++buffer[2]; + art::ScratchFile tmp3; + EXPECT_TRUE(tmp3.GetFile()->WriteFully(&buffer[0], length)); + --buffer[2]; + EXPECT_NE(reset_compare(tmp, tmp3), 0); + + // Change a byte near the middle. + ++buffer[length / 2]; + art::ScratchFile tmp4; + EXPECT_TRUE(tmp4.GetFile()->WriteFully(&buffer[0], length)); + --buffer[length / 2]; + EXPECT_NE(reset_compare(tmp, tmp4), 0); + + // Change a byte near the end. + ++buffer[length - 5]; + art::ScratchFile tmp5; + EXPECT_TRUE(tmp5.GetFile()->WriteFully(&buffer[0], length)); + --buffer[length - 5]; + EXPECT_NE(reset_compare(tmp, tmp5), 0); + + // Reference check + art::ScratchFile tmp6; + EXPECT_TRUE(tmp6.GetFile()->WriteFully(&buffer[0], length)); + EXPECT_EQ(reset_compare(tmp, tmp6), 0); +} + } // namespace unix_file diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 3ac87c5137..fc14da1067 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7119,7 +7119,7 @@ void ClassLinker::LinkInterfaceMethodsHelper::ReallocMethods() { method_alignment_); const size_t old_methods_ptr_size = (old_methods != nullptr) ? old_size : 0; auto* methods = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>( - Runtime::Current()->GetLinearAlloc()->Realloc( + class_linker_->GetAllocatorForClassLoader(klass_->GetClassLoader())->Realloc( self_, old_methods, old_methods_ptr_size, new_size)); CHECK(methods != nullptr); // Native allocation failure aborts. diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index b50aec0b63..e7051b35d8 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -68,6 +68,10 @@ ClassLoaderContext::~ClassLoaderContext() { } } +std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Default() { + return Create(""); +} + std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Create(const std::string& spec) { std::unique_ptr<ClassLoaderContext> result(new ClassLoaderContext()); if (result->Parse(spec)) { diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h index 85299d41c0..9afa880da4 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -34,9 +34,6 @@ class OatFile; // Utility class which holds the class loader context used during compilation/verification. class ClassLoaderContext { public: - // Creates an empty context (with no class loaders). - ClassLoaderContext(); - ~ClassLoaderContext(); // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files. @@ -126,6 +123,10 @@ class ClassLoaderContext { static std::unique_ptr<ClassLoaderContext> CreateContextForClassLoader(jobject class_loader, jobjectArray dex_elements); + // Returns the default class loader context to be used when none is specified. + // This will return a context with a single and empty PathClassLoader. + static std::unique_ptr<ClassLoaderContext> Default(); + private: enum ClassLoaderType { kInvalidClassLoader = 0, @@ -151,6 +152,9 @@ class ClassLoaderContext { explicit ClassLoaderInfo(ClassLoaderType cl_type) : type(cl_type) {} }; + // Creates an empty context (with no class loaders). + ClassLoaderContext(); + // Constructs an empty context. // `owns_the_dex_files` specifies whether or not the context will own the opened dex files // present in the class loader chain. If `owns_the_dex_files` is true then OpenDexFiles cannot diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index a2a6e085c2..5355267b07 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -65,8 +65,8 @@ extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, // A class may be accessing another class' fields when it doesn't have access, as access has been // given by inheritance. ScopedQuickEntrypointChecks sqec(self); - auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, - CalleeSaveType::kSaveEverything); + auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod( + self, CalleeSaveType::kSaveEverythingForClinit); ArtMethod* caller = caller_and_outer.caller; mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, true, false); @@ -80,8 +80,8 @@ extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* s REQUIRES_SHARED(Locks::mutator_lock_) { // Called when method->dex_cache_resolved_types_[] misses. ScopedQuickEntrypointChecks sqec(self); - auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, - CalleeSaveType::kSaveEverything); + auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod( + self, CalleeSaveType::kSaveEverythingForClinit); ArtMethod* caller = caller_and_outer.caller; mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, false); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc index 7e08b7ace0..b692618740 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints_test.cc @@ -90,7 +90,18 @@ TEST_F(QuickTrampolineEntrypointsTest, FrameSize) { GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveRefsOnly)); \ CheckFrameSize(isa, \ CalleeSaveType::kSaveAllCalleeSaves, \ - GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveAllCalleeSaves)) + GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveAllCalleeSaves)); \ + CheckFrameSize(isa, \ + CalleeSaveType::kSaveEverything, \ + GetCalleeSaveFrameSize(isa, CalleeSaveType::kSaveEverything)); \ + CheckFrameSize(isa, \ + CalleeSaveType::kSaveEverythingForClinit, \ + GetCalleeSaveFrameSize(isa, \ + CalleeSaveType::kSaveEverythingForClinit)); \ + CheckFrameSize(isa, \ + CalleeSaveType::kSaveEverythingForSuspendCheck, \ + GetCalleeSaveFrameSize( \ + isa, CalleeSaveType::kSaveEverythingForSuspendCheck)) CHECK_FRAME_SIZE(kArm); CHECK_FRAME_SIZE(kArm64); @@ -123,6 +134,13 @@ TEST_F(QuickTrampolineEntrypointsTest, ReturnPC) { GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveRefsOnly)); CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveAllCalleeSaves, GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveAllCalleeSaves)); + CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveEverything, + GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveEverything)); + CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveEverythingForClinit, + GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveEverythingForClinit)); + CheckPCOffset(kRuntimeISA, CalleeSaveType::kSaveEverythingForSuspendCheck, + GetCalleeSaveReturnPcOffset(kRuntimeISA, + CalleeSaveType::kSaveEverythingForSuspendCheck)); } } // namespace art diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 9d672b1d34..9b24885f53 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -2431,24 +2431,31 @@ mirror::Object* ConcurrentCopying::IsMarked(mirror::Object* from_ref) { // Non-immune non-moving space. Use the mark bitmap. accounting::ContinuousSpaceBitmap* mark_bitmap = heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref); - accounting::LargeObjectBitmap* los_bitmap = - heap_mark_bitmap_->GetLargeObjectBitmap(from_ref); - CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range"; bool is_los = mark_bitmap == nullptr; if (!is_los && mark_bitmap->Test(from_ref)) { // Already marked. to_ref = from_ref; - } else if (is_los && los_bitmap->Test(from_ref)) { - // Already marked in LOS. - to_ref = from_ref; } else { - // Not marked. - if (IsOnAllocStack(from_ref)) { - // If on the allocation stack, it's considered marked. + accounting::LargeObjectBitmap* los_bitmap = + heap_mark_bitmap_->GetLargeObjectBitmap(from_ref); + // We may not have a large object space for dex2oat, don't assume it exists. + if (los_bitmap == nullptr) { + CHECK(heap_->GetLargeObjectsSpace() == nullptr) + << "LOS bitmap covers the entire address range " << from_ref + << " " << heap_->DumpSpaces(); + } + if (los_bitmap != nullptr && is_los && los_bitmap->Test(from_ref)) { + // Already marked in LOS. to_ref = from_ref; } else { // Not marked. - to_ref = nullptr; + if (IsOnAllocStack(from_ref)) { + // If on the allocation stack, it's considered marked. + to_ref = from_ref; + } else { + // Not marked. + to_ref = nullptr; + } } } } diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 148438296f..14e017abd9 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -725,6 +725,10 @@ class ImageSpaceLoader { image_header->GetImageMethod(ImageHeader::kSaveRefsAndArgsMethod)); CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverything), image_header->GetImageMethod(ImageHeader::kSaveEverythingMethod)); + CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForClinit), + image_header->GetImageMethod(ImageHeader::kSaveEverythingMethodForClinit)); + CHECK_EQ(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForSuspendCheck), + image_header->GetImageMethod(ImageHeader::kSaveEverythingMethodForSuspendCheck)); } else if (!runtime->HasResolutionMethod()) { runtime->SetInstructionSet(space->oat_file_non_owned_->GetOatHeader().GetInstructionSet()); runtime->SetResolutionMethod(image_header->GetImageMethod(ImageHeader::kResolutionMethod)); @@ -743,6 +747,12 @@ class ImageSpaceLoader { runtime->SetCalleeSaveMethod( image_header->GetImageMethod(ImageHeader::kSaveEverythingMethod), CalleeSaveType::kSaveEverything); + runtime->SetCalleeSaveMethod( + image_header->GetImageMethod(ImageHeader::kSaveEverythingMethodForClinit), + CalleeSaveType::kSaveEverythingForClinit); + runtime->SetCalleeSaveMethod( + image_header->GetImageMethod(ImageHeader::kSaveEverythingMethodForSuspendCheck), + CalleeSaveType::kSaveEverythingForSuspendCheck); } VLOG(image) << "ImageSpace::Init exiting " << *space.get(); diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h index 11b3abb6ed..314c45e117 100644 --- a/runtime/generated/asm_support_gen.h +++ b/runtime/generated/asm_support_gen.h @@ -34,6 +34,10 @@ DEFINE_CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_REFS_ONLY_METHOD_OFFSET), (stat DEFINE_CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_REFS_AND_ARGS_METHOD_OFFSET), (static_cast<size_t>(art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveRefsAndArgs)))) #define RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET 0x18 DEFINE_CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET), (static_cast<size_t>(art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveEverything)))) +#define RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET 0x20 +DEFINE_CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET), (static_cast<size_t>(art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveEverythingForClinit)))) +#define RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET 0x28 +DEFINE_CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET), (static_cast<size_t>(art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveEverythingForSuspendCheck)))) #define THREAD_FLAGS_OFFSET 0 DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_FLAGS_OFFSET), (static_cast<int32_t>(art::Thread:: ThreadFlagsOffset<art::kRuntimePointerSize>().Int32Value()))) #define THREAD_ID_OFFSET 12 diff --git a/runtime/image.h b/runtime/image.h index 6c76f4915e..7bb796c5a1 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -183,6 +183,8 @@ class PACKED(4) ImageHeader { kSaveRefsOnlyMethod, kSaveRefsAndArgsMethod, kSaveEverythingMethod, + kSaveEverythingMethodForClinit, + kSaveEverythingMethodForSuspendCheck, kImageMethodsCount, // Number of elements in enum. }; diff --git a/runtime/mirror/accessible_object.h b/runtime/mirror/accessible_object.h index a217193522..d489f14536 100644 --- a/runtime/mirror/accessible_object.h +++ b/runtime/mirror/accessible_object.h @@ -17,11 +17,8 @@ #ifndef ART_RUNTIME_MIRROR_ACCESSIBLE_OBJECT_H_ #define ART_RUNTIME_MIRROR_ACCESSIBLE_OBJECT_H_ -#include "class.h" -#include "gc_root.h" #include "object.h" #include "read_barrier_option.h" -#include "thread.h" namespace art { @@ -34,12 +31,6 @@ class MANAGED AccessibleObject : public Object { return OFFSET_OF_OBJECT_MEMBER(AccessibleObject, flag_); } - template<bool kTransactionActive> - void SetAccessible(bool value) REQUIRES_SHARED(Locks::mutator_lock_) { - UNUSED(padding_); - return SetFieldBoolean<kTransactionActive>(FlagOffset(), value ? 1u : 0u); - } - bool IsAccessible() REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldBoolean(FlagOffset()); } @@ -47,7 +38,7 @@ class MANAGED AccessibleObject : public Object { private: uint8_t flag_; // Padding required for correct alignment of subclasses like Executable, Field, etc. - uint8_t padding_[1]; + uint8_t padding_[1] ATTRIBUTE_UNUSED; DISALLOW_IMPLICIT_CONSTRUCTORS(AccessibleObject); }; diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index 63142d5c25..22812454d1 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -27,8 +27,7 @@ #include "class.h" #include "gc/heap-inl.h" #include "obj_ptr-inl.h" -#include "object-inl.h" -#include "thread.h" +#include "thread-current-inl.h" namespace art { namespace mirror { diff --git a/runtime/mirror/field-inl.h b/runtime/mirror/field-inl.h index d33df5c8e7..ad48202514 100644 --- a/runtime/mirror/field-inl.h +++ b/runtime/mirror/field-inl.h @@ -20,7 +20,8 @@ #include "field.h" #include "art_field-inl.h" -#include "mirror/dex_cache-inl.h" +#include "class-inl.h" +#include "dex_cache-inl.h" namespace art { @@ -87,6 +88,10 @@ inline void Field::SetType(ObjPtr<mirror::Class> type) { SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(Field, type_), type); } +inline Primitive::Type Field::GetTypeAsPrimitiveType() { + return GetType()->GetPrimitiveType(); +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/field.h b/runtime/mirror/field.h index 40186a689b..6845575d18 100644 --- a/runtime/mirror/field.h +++ b/runtime/mirror/field.h @@ -20,8 +20,10 @@ #include "accessible_object.h" #include "base/enums.h" #include "gc_root.h" +#include "modifiers.h" #include "obj_ptr.h" #include "object.h" +#include "primitive.h" #include "read_barrier_option.h" namespace art { @@ -69,10 +71,7 @@ class MANAGED Field : public AccessibleObject { return (GetAccessFlags() & kAccVolatile) != 0; } - ALWAYS_INLINE Primitive::Type GetTypeAsPrimitiveType() - REQUIRES_SHARED(Locks::mutator_lock_) { - return GetType()->GetPrimitiveType(); - } + ALWAYS_INLINE Primitive::Type GetTypeAsPrimitiveType() REQUIRES_SHARED(Locks::mutator_lock_); mirror::Class* GetType() REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldObject<mirror::Class>(OFFSET_OF_OBJECT_MEMBER(Field, type_)); diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 086925b9c7..4f8952d8c2 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -393,14 +393,6 @@ inline size_t Object::SizeOf() { } template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile> -inline uint8_t Object::GetFieldBoolean(MemberOffset field_offset) { - if (kVerifyFlags & kVerifyThis) { - VerifyObject(this); - } - return GetField<uint8_t, kIsVolatile>(field_offset); -} - -template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile> inline int8_t Object::GetFieldByte(MemberOffset field_offset) { if (kVerifyFlags & kVerifyThis) { VerifyObject(this); diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 886780f051..aedcd66082 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -380,7 +380,12 @@ class MANAGED LOCKABLE Object { template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false> ALWAYS_INLINE uint8_t GetFieldBoolean(MemberOffset field_offset) - REQUIRES_SHARED(Locks::mutator_lock_); + REQUIRES_SHARED(Locks::mutator_lock_) { + if (kVerifyFlags & kVerifyThis) { + VerifyObject(this); + } + return GetField<uint8_t, kIsVolatile>(field_offset); + } template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false> ALWAYS_INLINE int8_t GetFieldByte(MemberOffset field_offset) diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 9f59a1f751..2e4dd8a599 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -27,7 +27,7 @@ #include "dex_file_annotations.h" #include "jni_internal.h" #include "mirror/class-inl.h" -#include "mirror/field.h" +#include "mirror/field-inl.h" #include "native_util.h" #include "reflection-inl.h" #include "scoped_fast_native_object_access-inl.h" diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h index 609f0d658d..4584351a6a 100644 --- a/runtime/runtime-inl.h +++ b/runtime/runtime-inl.h @@ -49,7 +49,9 @@ inline QuickMethodFrameInfo Runtime::GetRuntimeMethodFrameInfo(ArtMethod* method } else if (method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveRefsOnly)) { return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsOnly); } else { - DCHECK_EQ(method, GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverything)); + DCHECK(method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverything) || + method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverythingForClinit) || + method == GetCalleeSaveMethodUnchecked(CalleeSaveType::kSaveEverythingForSuspendCheck)); return GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveEverything); } } diff --git a/runtime/runtime.h b/runtime/runtime.h index 0c1344e11a..4e84468744 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -714,7 +714,7 @@ class Runtime { static constexpr int kProfileForground = 0; static constexpr int kProfileBackground = 1; - static constexpr uint32_t kCalleeSaveSize = 4u; + static constexpr uint32_t kCalleeSaveSize = 6u; // 64 bit so that we can share the same asm offsets for both 32 and 64 bits. uint64_t callee_save_methods_[kCalleeSaveSize]; diff --git a/runtime/stack.cc b/runtime/stack.cc index 19df0d26a1..2e0653666f 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -634,7 +634,7 @@ static void AssertPcIsWithinQuickCode(ArtMethod* method, uintptr_t pc) void StackVisitor::SanityCheckFrame() const { if (kIsDebugBuild) { ArtMethod* method = GetMethod(); - auto* declaring_class = method->GetDeclaringClass(); + mirror::Class* declaring_class = method->GetDeclaringClass(); // Runtime methods have null declaring class. if (!method->IsRuntimeMethod()) { CHECK(declaring_class != nullptr); @@ -647,11 +647,14 @@ void StackVisitor::SanityCheckFrame() const { LinearAlloc* const linear_alloc = runtime->GetLinearAlloc(); if (!linear_alloc->Contains(method)) { // Check class linker linear allocs. - mirror::Class* klass = method->GetDeclaringClass(); + // We get the canonical method as copied methods may have their declaring + // class from another class loader. + ArtMethod* canonical = method->GetCanonicalMethod(); + mirror::Class* klass = canonical->GetDeclaringClass(); LinearAlloc* const class_linear_alloc = (klass != nullptr) ? runtime->GetClassLinker()->GetAllocatorForClassLoader(klass->GetClassLoader()) : linear_alloc; - if (!class_linear_alloc->Contains(method)) { + if (!class_linear_alloc->Contains(canonical)) { // Check image space. bool in_image = false; for (auto& space : runtime->GetHeap()->GetContinuousSpaces()) { @@ -660,14 +663,14 @@ void StackVisitor::SanityCheckFrame() const { const auto& header = image_space->GetImageHeader(); const ImageSection& methods = header.GetMethodsSection(); const ImageSection& runtime_methods = header.GetRuntimeMethodsSection(); - const size_t offset = reinterpret_cast<const uint8_t*>(method) - image_space->Begin(); + const size_t offset = reinterpret_cast<const uint8_t*>(canonical) - image_space->Begin(); if (methods.Contains(offset) || runtime_methods.Contains(offset)) { in_image = true; break; } } } - CHECK(in_image) << method->PrettyMethod() << " not in linear alloc or image"; + CHECK(in_image) << canonical->PrettyMethod() << " not in linear alloc or image"; } } if (cur_quick_frame_ != nullptr) { diff --git a/runtime/thread.h b/runtime/thread.h index 776096a7bb..7540fd2563 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -1705,8 +1705,13 @@ class Thread { class SCOPED_CAPABILITY ScopedAssertNoThreadSuspension { public: - ALWAYS_INLINE explicit ScopedAssertNoThreadSuspension(const char* cause) - ACQUIRE(Roles::uninterruptible_) { + ALWAYS_INLINE ScopedAssertNoThreadSuspension(const char* cause, + bool enabled = true) + ACQUIRE(Roles::uninterruptible_) + : enabled_(enabled) { + if (!enabled_) { + return; + } if (kIsDebugBuild) { self_ = Thread::Current(); old_cause_ = self_->StartAssertNoThreadSuspension(cause); @@ -1715,6 +1720,9 @@ class SCOPED_CAPABILITY ScopedAssertNoThreadSuspension { } } ALWAYS_INLINE ~ScopedAssertNoThreadSuspension() RELEASE(Roles::uninterruptible_) { + if (!enabled_) { + return; + } if (kIsDebugBuild) { self_->EndAssertNoThreadSuspension(old_cause_); } else { @@ -1724,6 +1732,7 @@ class SCOPED_CAPABILITY ScopedAssertNoThreadSuspension { private: Thread* self_; + const bool enabled_; const char* old_cause_; }; diff --git a/test/1338-gc-no-los/expected.txt b/test/1338-gc-no-los/expected.txt new file mode 100644 index 0000000000..36bec43b8b --- /dev/null +++ b/test/1338-gc-no-los/expected.txt @@ -0,0 +1 @@ +131072 200 true diff --git a/test/1338-gc-no-los/info.txt b/test/1338-gc-no-los/info.txt new file mode 100644 index 0000000000..2e27357b80 --- /dev/null +++ b/test/1338-gc-no-los/info.txt @@ -0,0 +1 @@ +Test that the GC works with no large object space. Regression test for b/64393515.
\ No newline at end of file diff --git a/test/1338-gc-no-los/run b/test/1338-gc-no-los/run new file mode 100755 index 0000000000..f3c43fbce6 --- /dev/null +++ b/test/1338-gc-no-los/run @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +./default-run "$@" --runtime-option -XX:LargeObjectSpace=disabled diff --git a/test/1338-gc-no-los/src-art/Main.java b/test/1338-gc-no-los/src-art/Main.java new file mode 100644 index 0000000000..662fa7e610 --- /dev/null +++ b/test/1338-gc-no-los/src-art/Main.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import dalvik.system.VMRuntime; +import java.lang.ref.WeakReference; +import java.lang.reflect.Method; + +public class Main { + public static void main(String[] args) { + try { + // Allocate a large object. + byte[] arr = new byte[128 * 1024]; + // Allocate a non movable object. + byte[] arr2 = (byte[])VMRuntime.getRuntime().newNonMovableArray(Byte.TYPE, 200); + // Put the array in a weak reference so that IsMarked is called by the GC. + WeakReference weakRef = new WeakReference(arr2); + // Do a GC. + Runtime.getRuntime().gc(); + arr[0] = 1; + arr2[0] = 1; + System.out.println(arr.length + " " + arr2.length + " " + (weakRef.get() != null)); + } catch (Exception e) { + System.out.println(e); + } + } +} diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java index 510354073a..f466eea97b 100644 --- a/test/449-checker-bce/src/Main.java +++ b/test/449-checker-bce/src/Main.java @@ -876,6 +876,91 @@ public class Main { return true; } + /// CHECK-START: void Main.modArrayIndex1(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: ArraySet + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: ArraySet + + /// CHECK-START: void Main.modArrayIndex1(int[]) BCE (after) + /// CHECK-NOT: Deoptimize + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: ArraySet + /// CHECK-NOT: BoundsCheck + /// CHECK-DAG: ArraySet + public static void modArrayIndex1(int[] array) { + for(int i = 0; i < 100; i++) { + // Cannot statically eliminate, for example, when array.length == 5. + // Currently dynamic BCE isn't applied for this case. + array[i % 10] = i; + // Can be eliminated by BCE. + array[i % array.length] = i; + } + } + + /// CHECK-START: void Main.modArrayIndex2(int[], int) BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: ArraySet + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: ArraySet + + /// CHECK-START: void Main.modArrayIndex2(int[], int) BCE (after) + /// CHECK-NOT: Deoptimize + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: ArraySet + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: ArraySet + public static void modArrayIndex2(int array[], int index) { + for(int i = 0; i < 100; i++) { + // Both bounds checks cannot be statically eliminated, because index can be < 0. + // Currently dynamic BCE isn't applied for this case. + array[(index+i) % 10] = i; + array[(index+i) % array.length] = i; + } + } + + static final int[] staticArray = new int[10]; + + /// CHECK-START: void Main.modArrayIndex3() BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: ArraySet + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: ArraySet + + /// CHECK-START: void Main.modArrayIndex3() BCE (after) + /// CHECK-NOT: Deoptimize + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: ArraySet + /// CHECK-NOT: BoundsCheck + /// CHECK-DAG: ArraySet + public static void modArrayIndex3() { + for(int i = 0; i < 100; i++) { + // Currently dynamic BCE isn't applied for this case. + staticArray[i % 10] = i; + // Can be eliminated by BCE. + staticArray[i % staticArray.length] = i; + } + } + + /// CHECK-START: void Main.modArrayIndex4() BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: ArraySet + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: ArraySet + + /// CHECK-START: void Main.modArrayIndex4() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-DAG: ArraySet + /// CHECK-NOT: BoundsCheck + /// CHECK-DAG: ArraySet + public static void modArrayIndex4() { + int[] array = new int[20]; + for(int i = 0; i < 100; i++) { + // The local array length is statically know. Both can be eliminated by BCE. + array[i % 10] = i; + array[i % array.length] = i; + } + } /// CHECK-START: void Main.bubbleSort(int[]) GVN (before) /// CHECK: BoundsCheck diff --git a/test/661-classloader-allocator/expected.txt b/test/661-classloader-allocator/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/661-classloader-allocator/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/661-classloader-allocator/info.txt b/test/661-classloader-allocator/info.txt new file mode 100644 index 0000000000..872b5e0a5a --- /dev/null +++ b/test/661-classloader-allocator/info.txt @@ -0,0 +1,3 @@ +Regression test for copied methods which used to not +be (re-)allocated with the right class loader allocator, +which crashed the JIT profiler. diff --git a/test/661-classloader-allocator/src-ex/OtherClass.java b/test/661-classloader-allocator/src-ex/OtherClass.java new file mode 100644 index 0000000000..e59cb953fc --- /dev/null +++ b/test/661-classloader-allocator/src-ex/OtherClass.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package p1; + +interface Itf { + public default int defaultMethod() { + return 42; + } +} + +public class OtherClass implements Itf { + public void foo() { + } +} diff --git a/test/661-classloader-allocator/src/Main.java b/test/661-classloader-allocator/src/Main.java new file mode 100644 index 0000000000..40f8f7a4bf --- /dev/null +++ b/test/661-classloader-allocator/src/Main.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Constructor; + +public class Main { + static final String DEX_FILE = + System.getenv("DEX_LOCATION") + "/661-classloader-allocator-ex.jar"; + static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path"); + + private static void doUnloading() { + // Stop the JIT to ensure its threads and work queue are not keeping classes + // artifically alive. + stopJit(); + // Do multiple GCs to prevent rare flakiness if some other thread is keeping the + // classloader live. + for (int i = 0; i < 5; ++i) { + Runtime.getRuntime().gc(); + } + startJit(); + } + + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + loadClass(); + doUnloading(); + // fetchProfiles iterate over the ProfilingInfo, we used to crash in the presence + // of unloaded copied methods. + fetchProfiles(); + } + + public static void loadClass() throws Exception { + Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); + if (pathClassLoader == null) { + throw new AssertionError("Couldn't find path class loader class"); + } + Constructor<?> constructor = + pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class); + ClassLoader loader = (ClassLoader) constructor.newInstance( + DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); + Class<?> otherClass = loader.loadClass("p1.OtherClass"); + ensureJitCompiled(otherClass, "foo"); + } + + public static native void ensureJitCompiled(Class<?> cls, String methodName); + public static native void fetchProfiles(); + public static native void stopJit(); + public static native void startJit(); +} diff --git a/test/662-regression-alias/expected.txt b/test/662-regression-alias/expected.txt new file mode 100644 index 0000000000..7250211df8 --- /dev/null +++ b/test/662-regression-alias/expected.txt @@ -0,0 +1 @@ +passed 127 4 diff --git a/test/662-regression-alias/info.txt b/test/662-regression-alias/info.txt new file mode 100644 index 0000000000..584d3ee3d3 --- /dev/null +++ b/test/662-regression-alias/info.txt @@ -0,0 +1 @@ +Regression test on missed array element aliasing. diff --git a/test/662-regression-alias/src/Main.java b/test/662-regression-alias/src/Main.java new file mode 100644 index 0000000000..aad61e254d --- /dev/null +++ b/test/662-regression-alias/src/Main.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Regression test on ARM-scheduling/array-aliasing bug (b/64018485). + */ +public class Main { + + // + // Mimic original bug. + // + + static void setFields(int[] fields) { + if (fields == null || fields.length < 6) + fields = new int[6]; // creates phi + fields[5] = 127; + } + + static void processFieldValues(int field0, int field1, int field2, + int field3, int field4, int field5) { + if (field5 != 127) { + throw new Error("field = " + field5); + } else if (field0 != 0) { + processFieldValues(0, 0, 0, 0, 0, 0); // disable inlining + } + } + + static int doit(int pass) { + int[] fields = new int[6]; + for (; ; pass++) { + setFields(fields); + processFieldValues(fields[0], fields[1], fields[2], + fields[3], fields[4], fields[5]); + if (pass == 0) + break; + } + return fields[5]; + } + + // + // Similar situation. + // + + private static int aliasing(boolean f) { + int[] array = new int[6]; + int[] array2 = null; + int s = 0; + for (int i = 0; i < 1; i++) { + if (f) { + array2 = array; + } + array2[1] = 4; + s = array[1]; + } + return s; + } + + // + // Main driver. + // + + static public void main(String[] args) { + int r = doit(0); + int s = aliasing(true); + System.out.println("passed " + r + " " + s); + } +} diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index 7c0ed691b6..60c7315b6f 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -254,4 +254,17 @@ extern "C" JNIEXPORT int JNICALL Java_Main_numberOfDeoptimizations(JNIEnv*, jcla return Runtime::Current()->GetNumberOfDeoptimizations(); } +extern "C" JNIEXPORT void JNICALL Java_Main_fetchProfiles(JNIEnv*, jclass) { + jit::Jit* jit = GetJitIfEnabled(); + if (jit == nullptr) { + return; + } + jit::JitCodeCache* code_cache = jit->GetCodeCache(); + std::vector<ProfileMethodInfo> unused_vector; + std::set<std::string> unused_locations; + unused_locations.insert("fake_location"); + ScopedObjectAccess soa(Thread::Current()); + code_cache->GetProfiledMethods(unused_locations, unused_vector); +} + } // namespace art diff --git a/test/knownfailures.json b/test/knownfailures.json index bd9591a8a8..a8191bbb23 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -713,8 +713,16 @@ "--no-dex2oat do not create"] }, { - "tests": "961-default-iface-resolution-gen", + "tests": ["961-default-iface-resolution-gen", + "964-default-iface-init-gen", + "968-default-partial-compile-gen"], "env_vars": {"SANITIZE_HOST": "address"}, "description": ["Test hits dex2oat watchdog timeout (60sec) on art-asan"] + }, + { + "tests": "662-regression-alias", + "variant": "target", + "description": ["disable until ARM scheduling/aliasing bug is fixed."], + "bug": "b/64018485" } ] diff --git a/test/ti-agent/trace_helper.cc b/test/ti-agent/trace_helper.cc index 7a9d1e0d92..1f8ceff91c 100644 --- a/test/ti-agent/trace_helper.cc +++ b/test/ti-agent/trace_helper.cc @@ -388,10 +388,12 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing( data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr; data->in_callback = false; - void* old_data = nullptr; - if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) { + TraceData* old_data = nullptr; + if (JvmtiErrorToException(env, jvmti_env, + jvmti_env->GetEnvironmentLocalStorage( + reinterpret_cast<void**>(&old_data)))) { return; - } else if (old_data != nullptr) { + } else if (old_data != nullptr && old_data->test_klass != nullptr) { ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException")); env->ThrowNew(rt_exception.get(), "Environment already has local storage set!"); return; @@ -455,6 +457,21 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing( extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing( JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) { + TraceData* data = nullptr; + if (JvmtiErrorToException( + env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) { + return; + } + // If data is null then we haven't ever enabled tracing so we don't need to do anything. + if (data == nullptr || data->test_klass == nullptr) { + return; + } + env->DeleteGlobalRef(data->test_klass); + if (env->ExceptionCheck()) { + return; + } + // Clear test_klass so we know this isn't being used + data->test_klass = nullptr; if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_FIELD_ACCESS, @@ -23,6 +23,7 @@ LAUNCH_WRAPPER= LIBART=libart.so JIT_PROFILE="no" VERBOSE="no" +CLEAN_OAT_FILES="yes" EXTRA_OPTIONS=() # Follow all sym links to get the program name. @@ -87,6 +88,7 @@ Supported OPTIONS include: report upon completion. --profile Run with profiling, then run using profile data. --verbose Run script verbosely. + --no-clean Don't cleanup oat directories. The ART_OPTIONS are passed directly to the Android Runtime. @@ -176,12 +178,14 @@ cleanup_oat_directory() { # Input: Command line arguments to the art script. # e.g. -cp foo/classes.dex:bar/classes.dex would delete (foo/oat bar/oat) directories. cleanup_oat_directory_for_classpath() { - # First try: Use $CLASSPATH environment variable. - parse_classpath "$CLASSPATH" - # Second try: Look for latest -cp or -classpath arg which will take precedence. - find_cp_in_args "$@" + if [ "$CLEAN_OAT_FILES" = "yes" ]; then + # First try: Use $CLASSPATH environment variable. + parse_classpath "$CLASSPATH" + # Second try: Look for latest -cp or -classpath arg which will take precedence. + find_cp_in_args "$@" - cleanup_oat_directory "${PARSE_CLASSPATH_RESULT[@]}" + cleanup_oat_directory "${PARSE_CLASSPATH_RESULT[@]}" + fi } # Attempt to find $ANDROID_ROOT/framework/<isa>/core.art' without knowing what <isa> is. @@ -291,6 +295,9 @@ while [[ "$1" = "-"* ]]; do --verbose) VERBOSE="yes" ;; + --no-clean) + CLEAN_OAT_FILES="no" + ;; --*) echo "unknown option: $1" 1>&2 usage diff --git a/tools/cpp-define-generator/offset_runtime.def b/tools/cpp-define-generator/offset_runtime.def index 41e7e40af5..1d5ce7dd49 100644 --- a/tools/cpp-define-generator/offset_runtime.def +++ b/tools/cpp-define-generator/offset_runtime.def @@ -40,6 +40,10 @@ DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(SAVE_REFS_ONLY, art::CalleeSaveType::kSaveRefs DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(SAVE_REFS_AND_ARGS, art::CalleeSaveType::kSaveRefsAndArgs) // Offset of field Runtime::callee_save_methods_[kSaveEverything] DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(SAVE_EVERYTHING, art::CalleeSaveType::kSaveEverything) +// Offset of field Runtime::callee_save_methods_[kSaveEverythingForClinit] +DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(SAVE_EVERYTHING_FOR_CLINIT, art::CalleeSaveType::kSaveEverythingForClinit) +// Offset of field Runtime::callee_save_methods_[kSaveEverythingForSuspendCheck] +DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(SAVE_EVERYTHING_FOR_SUSPEND_CHECK, art::CalleeSaveType::kSaveEverythingForSuspendCheck) #undef DEFINE_RUNTIME_CALLEE_SAVE_OFFSET #include "common_undef.def" // undef DEFINE_OFFSET_EXPR diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt index 66bc252055..d4a0423eac 100644 --- a/tools/libcore_gcstress_debug_failures.txt +++ b/tools/libcore_gcstress_debug_failures.txt @@ -11,6 +11,7 @@ names: ["libcore.icu.TransliteratorTest#testAll", "libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045", "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndTimeout", + "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndNoTimeout", "libcore.java.util.TimeZoneTest#testSetDefaultDeadlock", "org.apache.harmony.tests.java.util.TimerTest#testThrowingTaskKillsTimerThread"] } diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index d2322bb3a9..355646b713 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -122,6 +122,9 @@ while true; do fi done +# Make sure the debuggee doesn't clean up what the debugger has generated. +art_debugee="$art_debugee --no-clean" + # For the host: # # If, on the other hand, there is a variant set, use it to modify the art_debugee parameter to |