Merge "ART: Use libopenjdkd"
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index c9af1c6..ab70367 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -205,7 +205,7 @@
LOCAL_DEX_PREOPT_IMAGE_LOCATION := $(TARGET_CORE_IMG_OUT)
ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),)
LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex
- LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp
+ LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp -D jack.dex.output.multidex.legacy=true
endif
include $(BUILD_JAVA_LIBRARY)
$(5) := $$(LOCAL_INSTALLED_MODULE)
@@ -221,7 +221,7 @@
LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_LOCATION)
ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),)
LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex
- LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp
+ LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp -D jack.dex.output.multidex.legacy=true
endif
include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
$(6) := $$(LOCAL_INSTALLED_MODULE)
diff --git a/compiler/Android.mk b/compiler/Android.mk
index f0bf499..4589736 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -108,7 +108,8 @@
elf_writer_debug.cc \
elf_writer_quick.cc \
image_writer.cc \
- oat_writer.cc
+ oat_writer.cc \
+ profile_assistant.cc
LIBART_COMPILER_SRC_FILES_arm := \
dex/quick/arm/assemble_arm.cc \
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 278c490..b5fd1e0 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -208,8 +208,8 @@
false,
timer_.get(),
-1,
- /* profile_file */ "",
- /* dex_to_oat_map */ nullptr));
+ /* dex_to_oat_map */ nullptr,
+ /* profile_compilation_info */ nullptr));
// We typically don't generate an image in unit tests, disable this optimization by default.
compiler_driver_->SetSupportBootImageFixup(false);
}
diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc
index bcf20c7..12568a4 100644
--- a/compiler/dex/quick/quick_cfi_test.cc
+++ b/compiler/dex/quick/quick_cfi_test.cc
@@ -92,7 +92,7 @@
false,
0,
-1,
- "",
+ nullptr,
nullptr);
ClassLinker* linker = nullptr;
CompilationUnit cu(&pool, isa, &driver, linker);
diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
index 9deabc0..b39fe4d 100644
--- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc
+++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
@@ -73,7 +73,7 @@
false,
0,
-1,
- "",
+ nullptr,
nullptr));
cu_.reset(new CompilationUnit(pool_.get(), isa_, compiler_driver_.get(), nullptr));
DexFile::CodeItem* code_item = static_cast<DexFile::CodeItem*>(
diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc
index 84fb432..f18fa67 100644
--- a/compiler/driver/compiled_method_storage_test.cc
+++ b/compiler/driver/compiled_method_storage_test.cc
@@ -45,7 +45,7 @@
false,
nullptr,
-1,
- "",
+ nullptr,
nullptr);
CompiledMethodStorage* storage = driver.GetCompiledMethodStorage();
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index afb4b71..043bd93 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -347,8 +347,8 @@
size_t thread_count, bool dump_stats, bool dump_passes,
const std::string& dump_cfg_file_name, bool dump_cfg_append,
CumulativeLogger* timer, int swap_fd,
- const std::string& profile_file,
- const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map)
+ const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map,
+ const ProfileCompilationInfo* profile_compilation_info)
: compiler_options_(compiler_options),
verification_results_(verification_results),
method_inliner_map_(method_inliner_map),
@@ -377,7 +377,8 @@
support_boot_image_fixup_(instruction_set != kMips && instruction_set != kMips64),
dex_files_for_oat_file_(nullptr),
dex_file_oat_filename_map_(dex_to_oat_map),
- compiled_method_storage_(swap_fd) {
+ compiled_method_storage_(swap_fd),
+ profile_compilation_info_(profile_compilation_info) {
DCHECK(compiler_options_ != nullptr);
DCHECK(verification_results_ != nullptr);
DCHECK(method_inliner_map_ != nullptr);
@@ -385,12 +386,6 @@
compiler_->Init();
CHECK_EQ(boot_image_, image_classes_.get() != nullptr);
-
- // Read the profile file if one is provided.
- if (!profile_file.empty()) {
- profile_compilation_info_.reset(new ProfileCompilationInfo(profile_file));
- LOG(INFO) << "Using profile data from file " << profile_file;
- }
}
CompilerDriver::~CompilerDriver() {
@@ -2306,15 +2301,11 @@
void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings) {
- if (profile_compilation_info_ != nullptr) {
- if (!profile_compilation_info_->Load(dex_files)) {
- LOG(WARNING) << "Failed to load offline profile info from "
- << profile_compilation_info_->GetFilename()
- << ". No methods will be compiled";
- } else if (kDebugProfileGuidedCompilation) {
- LOG(INFO) << "[ProfileGuidedCompilation] "
- << profile_compilation_info_->DumpInfo();
- }
+ if (kDebugProfileGuidedCompilation) {
+ LOG(INFO) << "[ProfileGuidedCompilation] " <<
+ ((profile_compilation_info_ == nullptr)
+ ? "null"
+ : profile_compilation_info_->DumpInfo(&dex_files));
}
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index fa0cb9a..3847c81 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -97,8 +97,8 @@
size_t thread_count, bool dump_stats, bool dump_passes,
const std::string& dump_cfg_file_name, bool dump_cfg_append,
CumulativeLogger* timer, int swap_fd,
- const std::string& profile_file,
- const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map);
+ const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map,
+ const ProfileCompilationInfo* profile_compilation_info);
~CompilerDriver();
@@ -657,9 +657,6 @@
// This option may be restricted to the boot image, depending on a flag in the implementation.
std::unique_ptr<std::unordered_set<std::string>> methods_to_compile_;
- // Info for profile guided compilation.
- std::unique_ptr<ProfileCompilationInfo> profile_compilation_info_;
-
bool had_hard_verifier_failure_;
size_t thread_count_;
@@ -689,6 +686,9 @@
CompiledMethodStorage compiled_method_storage_;
+ // Info for profile guided compilation.
+ const ProfileCompilationInfo* const profile_compilation_info_;
+
friend class CompileClassVisitor;
DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
};
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 2985ca9..d0bb201 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -76,23 +76,35 @@
// Return true if an object is already in an image space.
bool ImageWriter::IsInBootImage(const void* obj) const {
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
if (!compile_app_image_) {
- DCHECK(boot_image_space_ == nullptr);
+ DCHECK(heap->GetBootImageSpaces().empty());
return false;
}
- const uint8_t* image_begin = boot_image_space_->Begin();
- // Real image end including ArtMethods and ArtField sections.
- const uint8_t* image_end = image_begin + boot_image_space_->GetImageHeader().GetImageSize();
- return image_begin <= obj && obj < image_end;
+ for (gc::space::ImageSpace* boot_image_space : heap->GetBootImageSpaces()) {
+ const uint8_t* image_begin = boot_image_space->Begin();
+ // Real image end including ArtMethods and ArtField sections.
+ const uint8_t* image_end = image_begin + boot_image_space->GetImageHeader().GetImageSize();
+ if (image_begin <= obj && obj < image_end) {
+ return true;
+ }
+ }
+ return false;
}
bool ImageWriter::IsInBootOatFile(const void* ptr) const {
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
if (!compile_app_image_) {
- DCHECK(boot_image_space_ == nullptr);
+ DCHECK(heap->GetBootImageSpaces().empty());
return false;
}
- const ImageHeader& image_header = boot_image_space_->GetImageHeader();
- return image_header.GetOatFileBegin() <= ptr && ptr < image_header.GetOatFileEnd();
+ for (gc::space::ImageSpace* boot_image_space : heap->GetBootImageSpaces()) {
+ const ImageHeader& image_header = boot_image_space->GetImageHeader();
+ if (image_header.GetOatFileBegin() <= ptr && ptr < image_header.GetOatFileEnd()) {
+ return true;
+ }
+ }
+ return false;
}
static void CheckNoDexObjectsCallback(Object* obj, void* arg ATTRIBUTE_UNUSED)
@@ -109,14 +121,6 @@
bool ImageWriter::PrepareImageAddressSpace() {
target_ptr_size_ = InstructionSetPointerSize(compiler_driver_.GetInstructionSet());
gc::Heap* const heap = Runtime::Current()->GetHeap();
- // Cache boot image space.
- for (gc::space::ContinuousSpace* space : heap->GetContinuousSpaces()) {
- if (space->IsImageSpace()) {
- CHECK(compile_app_image_);
- CHECK(boot_image_space_ == nullptr) << "Multiple image spaces";
- boot_image_space_ = space->AsImageSpace();
- }
- }
{
ScopedObjectAccess soa(Thread::Current());
PruneNonImageClasses(); // Remove junk
@@ -205,9 +209,6 @@
oat_header.GetQuickResolutionTrampolineOffset();
image_info.oat_address_offsets_[kOatAddressQuickToInterpreterBridge] =
oat_header.GetQuickToInterpreterBridgeOffset();
- } else {
- // Other oat files use the primary trampolines.
- // TODO: Dummy values to protect usage? b/26317072
}
@@ -1070,6 +1071,13 @@
};
const char* oat_file = GetOatFilenameForDexCache(dex_cache);
ImageInfo& image_info = GetImageInfo(oat_file);
+ {
+ // Note: This table is only accessed from the image writer, so the lock is technically
+ // unnecessary.
+ WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ // Insert in the class table for this iamge.
+ image_info.class_table_->Insert(as_klass);
+ }
for (LengthPrefixedArray<ArtField>* cur_fields : fields) {
// Total array length including header.
if (cur_fields != nullptr) {
@@ -1252,13 +1260,16 @@
// Calculate size of the dex cache arrays slot and prepare offsets.
PrepareDexCacheArraySlots();
- // Calculate the sizes of the intern tables.
+ // Calculate the sizes of the intern tables and class tables.
for (const char* oat_filename : oat_filenames_) {
ImageInfo& image_info = GetImageInfo(oat_filename);
// Calculate how big the intern table will be after being serialized.
InternTable* const intern_table = image_info.intern_table_.get();
CHECK_EQ(intern_table->WeakSize(), 0u) << " should have strong interned all the strings";
image_info.intern_table_bytes_ = intern_table->WriteToMemory(nullptr);
+ // Calculate the size of the class table.
+ ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
+ image_info.class_table_bytes_ += image_info.class_table_->WriteToMemory(nullptr);
}
// Calculate bin slot offsets.
@@ -1315,27 +1326,6 @@
relocation.offset += image_info.bin_slot_offsets_[bin_type];
}
- /* TODO: Reenable the class table. b/26317072
- // Write out the class table.
- ClassLinker* class_linker = runtime->GetClassLinker();
- if (boot_image_space_ == nullptr) {
- // Compiling the boot image, add null class loader.
- class_loaders_.insert(nullptr);
- }
- // class_loaders_ usually will not be empty, but may be empty if we attempt to create an image
- // with no classes.
- if (class_loaders_.size() == 1u) {
- // Only write the class table if we have exactly one class loader. There may be cases where
- // there are multiple class loaders if a class path is passed to dex2oat.
- ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- for (mirror::ClassLoader* loader : class_loaders_) {
- ClassTable* table = class_linker->ClassTableForClassLoader(loader);
- CHECK(table != nullptr);
- class_table_bytes_ += table->WriteToMemory(nullptr);
- }
- }
- */
-
// Note that image_info.image_end_ is left at end of used mirror object section.
}
@@ -1375,8 +1365,7 @@
cur_pos = RoundUp(cur_pos, sizeof(uint64_t));
// Calculate the size of the class table section.
auto* class_table_section = &out_sections[ImageHeader::kSectionClassTable];
- // TODO: class_table_bytes_
- *class_table_section = ImageSection(cur_pos, 0u);
+ *class_table_section = ImageSection(cur_pos, class_table_bytes_);
cur_pos = class_table_section->End();
// Image end goes right before the start of the image bitmap.
return cur_pos;
@@ -1555,35 +1544,29 @@
CHECK_EQ(temp_intern_table.Size(), intern_table->Size());
temp_intern_table.VisitRoots(&root_visitor, kVisitRootFlagAllRoots);
}
- /* TODO: Reenable the class table writing.
// Write the class table(s) into the image. class_table_bytes_ may be 0 if there are multiple
// class loaders. Writing multiple class tables into the image is currently unsupported.
- if (class_table_bytes_ > 0u) {
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ if (image_info.class_table_bytes_ > 0u) {
const ImageSection& class_table_section = image_header->GetImageSection(
ImageHeader::kSectionClassTable);
uint8_t* const class_table_memory_ptr =
image_info.image_->Begin() + class_table_section.Offset();
ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- size_t class_table_bytes = 0;
- for (mirror::ClassLoader* loader : class_loaders_) {
- ClassTable* table = class_linker->ClassTableForClassLoader(loader);
- CHECK(table != nullptr);
- uint8_t* memory_ptr = class_table_memory_ptr + class_table_bytes;
- class_table_bytes += table->WriteToMemory(memory_ptr);
- // Fixup the pointers in the newly written class table to contain image addresses. See
- // above comment for intern tables.
- ClassTable temp_class_table;
- temp_class_table.ReadFromMemory(memory_ptr);
- CHECK_EQ(temp_class_table.NumZygoteClasses(), table->NumNonZygoteClasses() +
- table->NumZygoteClasses());
- BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(&root_visitor,
- RootInfo(kRootUnknown));
- temp_class_table.VisitRoots(buffered_visitor);
- }
- CHECK_EQ(class_table_bytes, class_table_bytes_);
+
+ ClassTable* table = image_info.class_table_.get();
+ CHECK(table != nullptr);
+ const size_t class_table_bytes = table->WriteToMemory(class_table_memory_ptr);
+ CHECK_EQ(class_table_bytes, image_info.class_table_bytes_);
+ // Fixup the pointers in the newly written class table to contain image addresses. See
+ // above comment for intern tables.
+ ClassTable temp_class_table;
+ temp_class_table.ReadFromMemory(class_table_memory_ptr);
+ CHECK_EQ(temp_class_table.NumZygoteClasses(), table->NumNonZygoteClasses() +
+ table->NumZygoteClasses());
+ BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(&root_visitor,
+ RootInfo(kRootUnknown));
+ temp_class_table.VisitRoots(buffered_visitor);
}
- */
}
void ImageWriter::CopyAndFixupObjects() {
@@ -1991,7 +1974,7 @@
copy->SetDeclaringClass(GetImageAddress(orig->GetDeclaringClassUnchecked()));
const char* oat_filename;
- if (orig->IsRuntimeMethod()) {
+ if (orig->IsRuntimeMethod() || compile_app_image_) {
oat_filename = default_oat_filename_;
} else {
auto it = dex_file_oat_filename_map_.find(orig->GetDexFile());
@@ -2210,12 +2193,10 @@
oat_file_(nullptr),
compile_pic_(compile_pic),
compile_app_image_(compile_app_image),
- boot_image_space_(nullptr),
target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())),
image_method_array_(ImageHeader::kImageMethodsCount),
dirty_methods_(0u),
clean_methods_(0u),
- class_table_bytes_(0u),
image_storage_mode_(image_storage_mode),
dex_file_oat_filename_map_(dex_file_oat_filename_map),
oat_filenames_(oat_filenames),
@@ -2227,6 +2208,8 @@
std::fill_n(image_methods_, arraysize(image_methods_), nullptr);
}
-ImageWriter::ImageInfo::ImageInfo() : intern_table_(new InternTable) {}
+ImageWriter::ImageInfo::ImageInfo()
+ : intern_table_(new InternTable),
+ class_table_(new ClassTable) {}
} // namespace art
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index d423799..ad69038 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -47,6 +47,8 @@
} // namespace space
} // namespace gc
+class ClassTable;
+
static constexpr int kInvalidImageFd = -1;
// Write a Space built during compilation for use during execution.
@@ -263,8 +265,14 @@
// Cached size of the intern table for when we allocate memory.
size_t intern_table_bytes_ = 0;
- // Intern table associated with this for serialization.
+ // Number of image class table bytes.
+ size_t class_table_bytes_ = 0;
+
+ // Intern table associated with this image for serialization.
std::unique_ptr<InternTable> intern_table_;
+
+ // Class table associated with this image for serialization.
+ std::unique_ptr<ClassTable> class_table_;
};
// We use the lock word to store the offset of the object in the image.
@@ -462,9 +470,6 @@
const bool compile_pic_;
const bool compile_app_image_;
- // Cache the boot image space in this class for faster lookups.
- gc::space::ImageSpace* boot_image_space_;
-
// Size of pointers on the target architecture.
size_t target_ptr_size_;
@@ -504,9 +509,6 @@
// null is a valid entry.
std::unordered_set<mirror::ClassLoader*> class_loaders_;
- // Number of image class table bytes.
- size_t class_table_bytes_;
-
// Which mode the image is stored as, see image.h
const ImageHeader::StorageMode image_storage_mode_;
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index b323d24..85216b7 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -155,8 +155,8 @@
/* dump_cfg_append */ false,
cumulative_logger_.get(),
/* swap_fd */ -1,
- /* profile_file */ "",
- /* dex to oat map */ nullptr));
+ /* dex to oat map */ nullptr,
+ /* profile_compilation_info */ nullptr));
// Disable dedupe so we can remove compiled methods.
compiler_driver_->SetDedupeEnabled(false);
compiler_driver_->SetSupportBootImageFixup(false);
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
index 877a674..b10cc35 100644
--- a/compiler/linker/relative_patcher_test.h
+++ b/compiler/linker/relative_patcher_test.h
@@ -47,7 +47,7 @@
driver_(&compiler_options_, &verification_results_, &inliner_map_,
Compiler::kQuick, instruction_set, nullptr,
false, nullptr, nullptr, nullptr, 1u,
- false, false, "", false, nullptr, -1, "", nullptr),
+ false, false, "", false, nullptr, -1, nullptr, nullptr),
error_msg_(),
instruction_set_(instruction_set),
features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)),
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 58f46d6..9f7ffa5 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -121,7 +121,7 @@
false,
timer_.get(),
-1,
- "",
+ nullptr,
nullptr));
}
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 53d3615..57c5058 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -997,6 +997,12 @@
stack_map_stream_.EndStackMapEntry();
}
+bool CodeGenerator::HasStackMapAtCurrentPc() {
+ uint32_t pc = GetAssembler()->CodeSize();
+ size_t count = stack_map_stream_.GetNumberOfStackMaps();
+ return count > 0 && stack_map_stream_.GetStackMap(count - 1).native_pc_offset == pc;
+}
+
void CodeGenerator::RecordCatchBlockInfo() {
ArenaAllocator* arena = graph_->GetArena();
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index eade05d..950043e 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -269,6 +269,8 @@
// Record native to dex mapping for a suspend point. Required by runtime.
void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path = nullptr);
+ // Check whether we have already recorded mapping at this PC.
+ bool HasStackMapAtCurrentPc();
bool CanMoveNullCheckToUser(HNullCheck* null_check);
void MaybeRecordImplicitNullCheck(HInstruction* instruction);
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 58feb67..24f06a3 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -417,6 +417,56 @@
DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM);
};
+// Slow path marking an object during a read barrier.
+class ReadBarrierMarkSlowPathARM : public SlowPathCode {
+ public:
+ ReadBarrierMarkSlowPathARM(HInstruction* instruction, Location out, Location obj)
+ : instruction_(instruction), out_(out), obj_(obj) {
+ DCHECK(kEmitCompilerReadBarrier);
+ }
+
+ const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathARM"; }
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ LocationSummary* locations = instruction_->GetLocations();
+ Register reg_out = out_.AsRegister<Register>();
+ DCHECK(locations->CanCall());
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
+ DCHECK(instruction_->IsInstanceFieldGet() ||
+ instruction_->IsStaticFieldGet() ||
+ instruction_->IsArrayGet() ||
+ instruction_->IsLoadClass() ||
+ instruction_->IsLoadString() ||
+ instruction_->IsInstanceOf() ||
+ instruction_->IsCheckCast())
+ << "Unexpected instruction in read barrier marking slow path: "
+ << instruction_->DebugName();
+
+ __ Bind(GetEntryLabel());
+ SaveLiveRegisters(codegen, locations);
+
+ InvokeRuntimeCallingConvention calling_convention;
+ CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
+ arm_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), obj_);
+ arm_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pReadBarrierMark),
+ instruction_,
+ instruction_->GetDexPc(),
+ this);
+ CheckEntrypointTypes<kQuickReadBarrierMark, mirror::Object*, mirror::Object*>();
+ arm_codegen->Move32(out_, Location::RegisterLocation(R0));
+
+ RestoreLiveRegisters(codegen, locations);
+ __ b(GetExitLabel());
+ }
+
+ private:
+ HInstruction* const instruction_;
+ const Location out_;
+ const Location obj_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARM);
+};
+
// Slow path generating a read barrier for a heap reference.
class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode {
public:
@@ -438,7 +488,7 @@
// to be instrumented, e.g.:
//
// __ LoadFromOffset(kLoadWord, out, out, offset);
- // codegen_->GenerateReadBarrier(instruction, out_loc, out_loc, out_loc, offset);
+ // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
//
// In that case, we have lost the information about the original
// object, and the emitted read barrier cannot work properly.
@@ -454,7 +504,9 @@
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
DCHECK(!instruction_->IsInvoke() ||
(instruction_->IsInvokeStaticOrDirect() &&
- instruction_->GetLocations()->Intrinsified()));
+ instruction_->GetLocations()->Intrinsified()))
+ << "Unexpected instruction in read barrier for heap reference slow path: "
+ << instruction_->DebugName();
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
@@ -596,14 +648,18 @@
class ReadBarrierForRootSlowPathARM : public SlowPathCode {
public:
ReadBarrierForRootSlowPathARM(HInstruction* instruction, Location out, Location root)
- : instruction_(instruction), out_(out), root_(root) {}
+ : instruction_(instruction), out_(out), root_(root) {
+ DCHECK(kEmitCompilerReadBarrier);
+ }
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
Register reg_out = out_.AsRegister<Register>();
DCHECK(locations->CanCall());
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
- DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString());
+ DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
+ << "Unexpected instruction in read barrier for GC root slow path: "
+ << instruction_->DebugName();
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
@@ -1623,6 +1679,10 @@
}
void InstructionCodeGeneratorARM::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ if (codegen_->HasStackMapAtCurrentPc()) {
+ // Ensure that we do not collide with the stack map of the previous instruction.
+ __ nop();
+ }
codegen_->RecordPcInfo(info, info->GetDexPc());
}
@@ -1891,7 +1951,7 @@
}
void InstructionCodeGeneratorARM::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
- GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
+ codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
}
void LocationsBuilderARM::VisitReturnVoid(HReturnVoid* ret) {
@@ -2846,8 +2906,7 @@
Register dividend = locations->InAt(0).AsRegister<Register>();
Register temp = locations->GetTemp(0).AsRegister<Register>();
int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
- uint32_t abs_imm = static_cast<uint32_t>(std::abs(imm));
- DCHECK(IsPowerOfTwo(abs_imm));
+ uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
int ctz_imm = CTZ(abs_imm);
if (ctz_imm == 1) {
@@ -2923,7 +2982,7 @@
// Do not generate anything. DivZeroCheck would prevent any code to be executed.
} else if (imm == 1 || imm == -1) {
DivRemOneOrMinusOne(instruction);
- } else if (IsPowerOfTwo(std::abs(imm))) {
+ } else if (IsPowerOfTwo(AbsOrMin(imm))) {
DivRemByPowerOfTwo(instruction);
} else {
DCHECK(imm <= -2 || imm >= 2);
@@ -2952,12 +3011,12 @@
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
- int32_t abs_imm = std::abs(div->InputAt(1)->AsIntConstant()->GetValue());
- if (abs_imm <= 1) {
+ int32_t value = div->InputAt(1)->AsIntConstant()->GetValue();
+ if (value == 1 || value == 0 || value == -1) {
// No temp register required.
} else {
locations->AddTemp(Location::RequiresRegister());
- if (!IsPowerOfTwo(abs_imm)) {
+ if (!IsPowerOfTwo(AbsOrMin(value))) {
locations->AddTemp(Location::RequiresRegister());
}
}
@@ -3078,12 +3137,12 @@
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
- int32_t abs_imm = std::abs(rem->InputAt(1)->AsIntConstant()->GetValue());
- if (abs_imm <= 1) {
+ int32_t value = rem->InputAt(1)->AsIntConstant()->GetValue();
+ if (value == 1 || value == 0 || value == -1) {
// No temp register required.
} else {
locations->AddTemp(Location::RequiresRegister());
- if (!IsPowerOfTwo(abs_imm)) {
+ if (!IsPowerOfTwo(AbsOrMin(value))) {
locations->AddTemp(Location::RequiresRegister());
}
}
@@ -3437,7 +3496,7 @@
Register first_reg = first.AsRegister<Register>();
if (second.IsRegister()) {
Register second_reg = second.AsRegister<Register>();
- // Arm doesn't mask the shift count so we need to do it ourselves.
+ // ARM doesn't mask the shift count so we need to do it ourselves.
__ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftValue));
if (op->IsShl()) {
__ Lsl(out_reg, first_reg, out_reg);
@@ -3449,7 +3508,7 @@
} else {
int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
uint32_t shift_value = static_cast<uint32_t>(cst & kMaxIntShiftValue);
- if (shift_value == 0) { // arm does not support shifting with 0 immediate.
+ if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
__ Mov(out_reg, first_reg);
} else if (op->IsShl()) {
__ Lsl(out_reg, first_reg, shift_value);
@@ -3796,9 +3855,9 @@
LOG(FATAL) << "Unreachable";
}
-void InstructionCodeGeneratorARM::GenerateMemoryBarrier(MemBarrierKind kind) {
- // TODO (ported from quick): revisit Arm barrier kinds
- DmbOptions flavor = DmbOptions::ISH; // quiet c++ warnings
+void CodeGeneratorARM::GenerateMemoryBarrier(MemBarrierKind kind) {
+ // TODO (ported from quick): revisit ARM barrier kinds.
+ DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings.
switch (kind) {
case MemBarrierKind::kAnyStore:
case MemBarrierKind::kLoadAny:
@@ -3879,11 +3938,11 @@
locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
locations->AddTemp(Location::RequiresRegister());
} else if (generate_volatile) {
- // Arm encoding have some additional constraints for ldrexd/strexd:
+ // ARM encoding have some additional constraints for ldrexd/strexd:
// - registers need to be consecutive
// - the first register should be even but not R14.
- // We don't test for Arm yet, and the assertion makes sure that we revisit this if we ever
- // enable Arm encoding.
+ // We don't test for ARM yet, and the assertion makes sure that we
+ // revisit this if we ever enable ARM encoding.
DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
locations->AddTemp(Location::RequiresRegister());
@@ -3913,7 +3972,7 @@
CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
if (is_volatile) {
- GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
+ codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
}
switch (field_type) {
@@ -4005,7 +4064,7 @@
}
if (is_volatile) {
- GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+ codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
}
}
@@ -4039,14 +4098,18 @@
(overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
}
if (volatile_for_double) {
- // Arm encoding have some additional constraints for ldrexd/strexd:
+ // ARM encoding have some additional constraints for ldrexd/strexd:
// - registers need to be consecutive
// - the first register should be even but not R14.
- // We don't test for Arm yet, and the assertion makes sure that we revisit this if we ever
- // enable Arm encoding.
+ // We don't test for ARM yet, and the assertion makes sure that we
+ // revisit this if we ever enable ARM encoding.
DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
+ } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
+ // We need a temporary register for the read barrier marking slow
+ // path in CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier.
+ locations->AddTemp(Location::RequiresRegister());
}
}
@@ -4105,33 +4168,52 @@
uint32_t offset = field_info.GetFieldOffset().Uint32Value();
switch (field_type) {
- case Primitive::kPrimBoolean: {
+ case Primitive::kPrimBoolean:
__ LoadFromOffset(kLoadUnsignedByte, out.AsRegister<Register>(), base, offset);
break;
- }
- case Primitive::kPrimByte: {
+ case Primitive::kPrimByte:
__ LoadFromOffset(kLoadSignedByte, out.AsRegister<Register>(), base, offset);
break;
- }
- case Primitive::kPrimShort: {
+ case Primitive::kPrimShort:
__ LoadFromOffset(kLoadSignedHalfword, out.AsRegister<Register>(), base, offset);
break;
- }
- case Primitive::kPrimChar: {
+ case Primitive::kPrimChar:
__ LoadFromOffset(kLoadUnsignedHalfword, out.AsRegister<Register>(), base, offset);
break;
- }
case Primitive::kPrimInt:
- case Primitive::kPrimNot: {
__ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset);
break;
+
+ case Primitive::kPrimNot: {
+ // /* HeapReference<Object> */ out = *(base + offset)
+ if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ Location temp_loc = locations->GetTemp(0);
+ // Note that a potential implicit null check is handled in this
+ // CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier call.
+ codegen_->GenerateFieldLoadWithBakerReadBarrier(
+ instruction, out, base, offset, temp_loc, /* needs_null_check */ true);
+ if (is_volatile) {
+ codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
+ }
+ } else {
+ __ LoadFromOffset(kLoadWord, out.AsRegister<Register>(), base, offset);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ if (is_volatile) {
+ codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
+ }
+ // If read barriers are enabled, emit read barriers other than
+ // Baker's using a slow path (and also unpoison the loaded
+ // reference, if heap poisoning is enabled).
+ codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, base_loc, offset);
+ }
+ break;
}
- case Primitive::kPrimLong: {
+ case Primitive::kPrimLong:
if (is_volatile && !atomic_ldrd_strd) {
GenerateWideAtomicLoad(base, offset,
out.AsRegisterPairLow<Register>(),
@@ -4140,12 +4222,10 @@
__ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), base, offset);
}
break;
- }
- case Primitive::kPrimFloat: {
+ case Primitive::kPrimFloat:
__ LoadSFromOffset(out.AsFpuRegister<SRegister>(), base, offset);
break;
- }
case Primitive::kPrimDouble: {
DRegister out_reg = FromLowSToD(out.AsFpuRegisterPairLow<SRegister>());
@@ -4167,17 +4247,20 @@
UNREACHABLE();
}
- // Doubles are handled in the switch.
- if (field_type != Primitive::kPrimDouble) {
+ if (field_type == Primitive::kPrimNot || field_type == Primitive::kPrimDouble) {
+ // Potential implicit null checks, in the case of reference or
+ // double fields, are handled in the previous switch statement.
+ } else {
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
if (is_volatile) {
- GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
- }
-
- if (field_type == Primitive::kPrimNot) {
- codegen_->MaybeGenerateReadBarrier(instruction, out, out, base_loc, offset);
+ if (field_type == Primitive::kPrimNot) {
+ // Memory barriers, in the case of references, are also handled
+ // in the previous switch statement.
+ } else {
+ codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
+ }
}
}
@@ -4340,6 +4423,11 @@
Location::RequiresRegister(),
object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
}
+ // We need a temporary register for the read barrier marking slow
+ // path in CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
+ if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
}
void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
@@ -4347,12 +4435,13 @@
Location obj_loc = locations->InAt(0);
Register obj = obj_loc.AsRegister<Register>();
Location index = locations->InAt(1);
- Primitive::Type type = instruction->GetType();
+ Location out_loc = locations->Out();
+ Primitive::Type type = instruction->GetType();
switch (type) {
case Primitive::kPrimBoolean: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
- Register out = locations->Out().AsRegister<Register>();
+ Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
@@ -4366,7 +4455,7 @@
case Primitive::kPrimByte: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
- Register out = locations->Out().AsRegister<Register>();
+ Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset;
@@ -4380,7 +4469,7 @@
case Primitive::kPrimShort: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
- Register out = locations->Out().AsRegister<Register>();
+ Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
@@ -4394,7 +4483,7 @@
case Primitive::kPrimChar: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
- Register out = locations->Out().AsRegister<Register>();
+ Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset;
@@ -4406,13 +4495,9 @@
break;
}
- case Primitive::kPrimInt:
- case Primitive::kPrimNot: {
- static_assert(
- sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
- "art::mirror::HeapReference<mirror::Object> and int32_t have different sizes.");
+ case Primitive::kPrimInt: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
- Register out = locations->Out().AsRegister<Register>();
+ Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
@@ -4424,44 +4509,79 @@
break;
}
+ case Primitive::kPrimNot: {
+ static_assert(
+ sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
+ "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
+ uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
+ // /* HeapReference<Object> */ out =
+ // *(obj + data_offset + index * sizeof(HeapReference<Object>))
+ if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ Location temp = locations->GetTemp(0);
+ // Note that a potential implicit null check is handled in this
+ // CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier call.
+ codegen_->GenerateArrayLoadWithBakerReadBarrier(
+ instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ true);
+ } else {
+ Register out = out_loc.AsRegister<Register>();
+ if (index.IsConstant()) {
+ size_t offset =
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ __ LoadFromOffset(kLoadWord, out, obj, offset);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ // If read barriers are enabled, emit read barriers other than
+ // Baker's using a slow path (and also unpoison the loaded
+ // reference, if heap poisoning is enabled).
+ codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
+ __ LoadFromOffset(kLoadWord, out, IP, data_offset);
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ // If read barriers are enabled, emit read barriers other than
+ // Baker's using a slow path (and also unpoison the loaded
+ // reference, if heap poisoning is enabled).
+ codegen_->MaybeGenerateReadBarrierSlow(
+ instruction, out_loc, out_loc, obj_loc, data_offset, index);
+ }
+ }
+ break;
+ }
+
case Primitive::kPrimLong: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
- Location out = locations->Out();
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
- __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), obj, offset);
+ __ LoadFromOffset(kLoadWordPair, out_loc.AsRegisterPairLow<Register>(), obj, offset);
} else {
__ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
- __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), IP, data_offset);
+ __ LoadFromOffset(kLoadWordPair, out_loc.AsRegisterPairLow<Register>(), IP, data_offset);
}
break;
}
case Primitive::kPrimFloat: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
- Location out = locations->Out();
- DCHECK(out.IsFpuRegister());
+ SRegister out = out_loc.AsFpuRegister<SRegister>();
if (index.IsConstant()) {
size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), obj, offset);
+ __ LoadSFromOffset(out, obj, offset);
} else {
__ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
- __ LoadSFromOffset(out.AsFpuRegister<SRegister>(), IP, data_offset);
+ __ LoadSFromOffset(out, IP, data_offset);
}
break;
}
case Primitive::kPrimDouble: {
uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
- Location out = locations->Out();
- DCHECK(out.IsFpuRegisterPair());
+ SRegister out = out_loc.AsFpuRegisterPairLow<SRegister>();
if (index.IsConstant()) {
size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
- __ LoadDFromOffset(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), obj, offset);
+ __ LoadDFromOffset(FromLowSToD(out), obj, offset);
} else {
__ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_8));
- __ LoadDFromOffset(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), IP, data_offset);
+ __ LoadDFromOffset(FromLowSToD(out), IP, data_offset);
}
break;
}
@@ -4470,20 +4590,12 @@
LOG(FATAL) << "Unreachable type " << type;
UNREACHABLE();
}
- codegen_->MaybeRecordImplicitNullCheck(instruction);
if (type == Primitive::kPrimNot) {
- static_assert(
- sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
- "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
- Location out = locations->Out();
- if (index.IsConstant()) {
- uint32_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
- codegen_->MaybeGenerateReadBarrier(instruction, out, out, obj_loc, offset);
- } else {
- codegen_->MaybeGenerateReadBarrier(instruction, out, out, obj_loc, data_offset, index);
- }
+ // Potential implicit null checks, in the case of reference
+ // arrays, are handled in the previous switch statement.
+ } else {
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
}
}
@@ -4574,6 +4686,7 @@
__ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
__ StoreToOffset(kStoreWord, source, IP, data_offset);
}
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
DCHECK(!needs_write_barrier);
DCHECK(!may_need_runtime_call_for_type_check);
break;
@@ -4615,12 +4728,12 @@
// __ Mov(temp2, temp1);
// // /* HeapReference<Class> */ temp1 = temp1->component_type_
// __ LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
- // codegen_->GenerateReadBarrier(
+ // codegen_->GenerateReadBarrierSlow(
// instruction, temp1_loc, temp1_loc, temp2_loc, component_offset);
//
// // /* HeapReference<Class> */ temp2 = value->klass_
// __ LoadFromOffset(kLoadWord, temp2, value, class_offset);
- // codegen_->GenerateReadBarrier(
+ // codegen_->GenerateReadBarrierSlow(
// instruction, temp2_loc, temp2_loc, value_loc, class_offset, temp1_loc);
//
// __ cmp(temp1, ShifterOperand(temp2));
@@ -4717,8 +4830,6 @@
__ add(IP, array, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
__ StoreToOffset(kStoreWord, value, IP, data_offset);
}
-
- codegen_->MaybeRecordImplicitNullCheck(instruction);
break;
}
@@ -4770,8 +4881,8 @@
UNREACHABLE();
}
- // Ints and objects are handled in the switch.
- if (value_type != Primitive::kPrimInt && value_type != Primitive::kPrimNot) {
+ // Objects are handled in the switch.
+ if (value_type != Primitive::kPrimNot) {
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
}
@@ -5140,16 +5251,9 @@
if (cls->IsReferrersClass()) {
DCHECK(!cls->CanCallRuntime());
DCHECK(!cls->MustGenerateClinitCheck());
- uint32_t declaring_class_offset = ArtMethod::DeclaringClassOffset().Int32Value();
- if (kEmitCompilerReadBarrier) {
- // /* GcRoot<mirror::Class>* */ out = &(current_method->declaring_class_)
- __ AddConstant(out, current_method, declaring_class_offset);
- // /* mirror::Class* */ out = out->Read()
- codegen_->GenerateReadBarrierForRoot(cls, out_loc, out_loc);
- } else {
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- __ LoadFromOffset(kLoadWord, out, current_method, declaring_class_offset);
- }
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ GenerateGcRootFieldLoad(
+ cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
} else {
// /* GcRoot<mirror::Class>[] */ out =
// current_method.ptr_sized_fields_->dex_cache_resolved_types_
@@ -5157,17 +5261,8 @@
out,
current_method,
ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value());
-
- size_t cache_offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex());
- if (kEmitCompilerReadBarrier) {
- // /* GcRoot<mirror::Class>* */ out = &out[type_index]
- __ AddConstant(out, out, cache_offset);
- // /* mirror::Class* */ out = out->Read()
- codegen_->GenerateReadBarrierForRoot(cls, out_loc, out_loc);
- } else {
- // /* GcRoot<mirror::Class> */ out = out[type_index]
- __ LoadFromOffset(kLoadWord, out, out, cache_offset);
- }
+ // /* GcRoot<mirror::Class> */ out = out[type_index]
+ GenerateGcRootFieldLoad(cls, out_loc, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
DCHECK(cls->CanCallRuntime());
@@ -5230,30 +5325,14 @@
Register out = out_loc.AsRegister<Register>();
Register current_method = locations->InAt(0).AsRegister<Register>();
- uint32_t declaring_class_offset = ArtMethod::DeclaringClassOffset().Int32Value();
- if (kEmitCompilerReadBarrier) {
- // /* GcRoot<mirror::Class>* */ out = &(current_method->declaring_class_)
- __ AddConstant(out, current_method, declaring_class_offset);
- // /* mirror::Class* */ out = out->Read()
- codegen_->GenerateReadBarrierForRoot(load, out_loc, out_loc);
- } else {
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- __ LoadFromOffset(kLoadWord, out, current_method, declaring_class_offset);
- }
-
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ GenerateGcRootFieldLoad(
+ load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
// /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_
__ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
-
- size_t cache_offset = CodeGenerator::GetCacheOffset(load->GetStringIndex());
- if (kEmitCompilerReadBarrier) {
- // /* GcRoot<mirror::String>* */ out = &out[string_index]
- __ AddConstant(out, out, cache_offset);
- // /* mirror::String* */ out = out->Read()
- codegen_->GenerateReadBarrierForRoot(load, out_loc, out_loc);
- } else {
- // /* GcRoot<mirror::String> */ out = out[string_index]
- __ LoadFromOffset(kLoadWord, out, out, cache_offset);
- }
+ // /* GcRoot<mirror::String> */ out = out[string_index]
+ GenerateGcRootFieldLoad(
+ load, out_loc, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
if (!load->IsInDexCache()) {
SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load);
@@ -5300,6 +5379,14 @@
CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
}
+static bool TypeCheckNeedsATemporary(TypeCheckKind type_check_kind) {
+ return kEmitCompilerReadBarrier &&
+ (kUseBakerReadBarrier ||
+ type_check_kind == TypeCheckKind::kAbstractClassCheck ||
+ type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
+ type_check_kind == TypeCheckKind::kArrayObjectCheck);
+}
+
void LocationsBuilderARM::VisitInstanceOf(HInstanceOf* instruction) {
LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
@@ -5326,21 +5413,22 @@
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
// When read barriers are enabled, we need a temporary register for
// some cases.
- if (kEmitCompilerReadBarrier &&
- (type_check_kind == TypeCheckKind::kAbstractClassCheck ||
- type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
- type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
+ if (TypeCheckNeedsATemporary(type_check_kind)) {
locations->AddTemp(Location::RequiresRegister());
}
}
void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) {
+ TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
Register obj = obj_loc.AsRegister<Register>();
Register cls = locations->InAt(1).AsRegister<Register>();
Location out_loc = locations->Out();
Register out = out_loc.AsRegister<Register>();
+ Location temp_loc = TypeCheckNeedsATemporary(type_check_kind) ?
+ locations->GetTemp(0) :
+ Location::NoLocation();
uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
@@ -5355,10 +5443,9 @@
}
// /* HeapReference<Class> */ out = obj->klass_
- __ LoadFromOffset(kLoadWord, out, obj, class_offset);
- codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, obj_loc, class_offset);
+ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, temp_loc);
- switch (instruction->GetTypeCheckKind()) {
+ switch (type_check_kind) {
case TypeCheckKind::kExactCheck: {
__ cmp(out, ShifterOperand(cls));
// Classes must be equal for the instanceof to succeed.
@@ -5373,17 +5460,8 @@
// object to avoid doing a comparison we know will fail.
Label loop;
__ Bind(&loop);
- Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation();
- if (kEmitCompilerReadBarrier) {
- // Save the value of `out` into `temp` before overwriting it
- // in the following move operation, as we will need it for the
- // read barrier below.
- Register temp = temp_loc.AsRegister<Register>();
- __ Mov(temp, out);
- }
// /* HeapReference<Class> */ out = out->super_class_
- __ LoadFromOffset(kLoadWord, out, out, super_offset);
- codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, super_offset);
+ GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, temp_loc);
// If `out` is null, we use it for the result, and jump to `done`.
__ CompareAndBranchIfZero(out, &done);
__ cmp(out, ShifterOperand(cls));
@@ -5401,17 +5479,8 @@
__ Bind(&loop);
__ cmp(out, ShifterOperand(cls));
__ b(&success, EQ);
- Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation();
- if (kEmitCompilerReadBarrier) {
- // Save the value of `out` into `temp` before overwriting it
- // in the following move operation, as we will need it for the
- // read barrier below.
- Register temp = temp_loc.AsRegister<Register>();
- __ Mov(temp, out);
- }
// /* HeapReference<Class> */ out = out->super_class_
- __ LoadFromOffset(kLoadWord, out, out, super_offset);
- codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, super_offset);
+ GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, temp_loc);
__ CompareAndBranchIfNonZero(out, &loop);
// If `out` is null, we use it for the result, and jump to `done`.
__ b(&done);
@@ -5429,17 +5498,8 @@
__ cmp(out, ShifterOperand(cls));
__ b(&exact_check, EQ);
// Otherwise, we need to check that the object's class is a non-primitive array.
- Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation();
- if (kEmitCompilerReadBarrier) {
- // Save the value of `out` into `temp` before overwriting it
- // in the following move operation, as we will need it for the
- // read barrier below.
- Register temp = temp_loc.AsRegister<Register>();
- __ Mov(temp, out);
- }
// /* HeapReference<Class> */ out = out->component_type_
- __ LoadFromOffset(kLoadWord, out, out, component_offset);
- codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, component_offset);
+ GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, temp_loc);
// If `out` is null, we use it for the result, and jump to `done`.
__ CompareAndBranchIfZero(out, &done);
__ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
@@ -5478,6 +5538,13 @@
// HInstanceOf instruction (following the runtime calling
// convention), which might be cluttered by the potential first
// read barrier emission at the beginning of this method.
+ //
+ // TODO: Introduce a new runtime entry point taking the object
+ // to test (instead of its class) as argument, and let it deal
+ // with the read barrier issues. This will let us refactor this
+ // case of the `switch` code as it was previously (with a direct
+ // call to the runtime not using a type checking slow path).
+ // This should also be beneficial for the other cases above.
DCHECK(locations->OnlyCallsOnSlowPath());
slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM(instruction,
/* is_fatal */ false);
@@ -5532,27 +5599,27 @@
locations->AddTemp(Location::RequiresRegister());
// When read barriers are enabled, we need an additional temporary
// register for some cases.
- if (kEmitCompilerReadBarrier &&
- (type_check_kind == TypeCheckKind::kAbstractClassCheck ||
- type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
- type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
+ if (TypeCheckNeedsATemporary(type_check_kind)) {
locations->AddTemp(Location::RequiresRegister());
}
}
void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) {
+ TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
LocationSummary* locations = instruction->GetLocations();
Location obj_loc = locations->InAt(0);
Register obj = obj_loc.AsRegister<Register>();
Register cls = locations->InAt(1).AsRegister<Register>();
Location temp_loc = locations->GetTemp(0);
Register temp = temp_loc.AsRegister<Register>();
+ Location temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ?
+ locations->GetTemp(1) :
+ Location::NoLocation();
uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
- TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
bool is_type_check_slow_path_fatal =
(type_check_kind == TypeCheckKind::kExactCheck ||
type_check_kind == TypeCheckKind::kAbstractClassCheck ||
@@ -5571,8 +5638,7 @@
}
// /* HeapReference<Class> */ temp = obj->klass_
- __ LoadFromOffset(kLoadWord, temp, obj, class_offset);
- codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset);
+ GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc);
switch (type_check_kind) {
case TypeCheckKind::kExactCheck:
@@ -5589,18 +5655,8 @@
// object to avoid doing a comparison we know will fail.
Label loop, compare_classes;
__ Bind(&loop);
- Location temp2_loc =
- kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation();
- if (kEmitCompilerReadBarrier) {
- // Save the value of `temp` into `temp2` before overwriting it
- // in the following move operation, as we will need it for the
- // read barrier below.
- Register temp2 = temp2_loc.AsRegister<Register>();
- __ Mov(temp2, temp);
- }
// /* HeapReference<Class> */ temp = temp->super_class_
- __ LoadFromOffset(kLoadWord, temp, temp, super_offset);
- codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, temp2_loc, super_offset);
+ GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, temp2_loc);
// If the class reference currently in `temp` is not null, jump
// to the `compare_classes` label to compare it with the checked
@@ -5612,8 +5668,7 @@
// going into the slow path, as it has been overwritten in the
// meantime.
// /* HeapReference<Class> */ temp = obj->klass_
- __ LoadFromOffset(kLoadWord, temp, obj, class_offset);
- codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset);
+ GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc);
__ b(type_check_slow_path->GetEntryLabel());
__ Bind(&compare_classes);
@@ -5629,18 +5684,8 @@
__ cmp(temp, ShifterOperand(cls));
__ b(&done, EQ);
- Location temp2_loc =
- kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation();
- if (kEmitCompilerReadBarrier) {
- // Save the value of `temp` into `temp2` before overwriting it
- // in the following move operation, as we will need it for the
- // read barrier below.
- Register temp2 = temp2_loc.AsRegister<Register>();
- __ Mov(temp2, temp);
- }
// /* HeapReference<Class> */ temp = temp->super_class_
- __ LoadFromOffset(kLoadWord, temp, temp, super_offset);
- codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, temp2_loc, super_offset);
+ GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, temp2_loc);
// If the class reference currently in `temp` is not null, jump
// back at the beginning of the loop.
@@ -5651,8 +5696,7 @@
// going into the slow path, as it has been overwritten in the
// meantime.
// /* HeapReference<Class> */ temp = obj->klass_
- __ LoadFromOffset(kLoadWord, temp, obj, class_offset);
- codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset);
+ GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc);
__ b(type_check_slow_path->GetEntryLabel());
break;
}
@@ -5664,19 +5708,8 @@
__ b(&done, EQ);
// Otherwise, we need to check that the object's class is a non-primitive array.
- Location temp2_loc =
- kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation();
- if (kEmitCompilerReadBarrier) {
- // Save the value of `temp` into `temp2` before overwriting it
- // in the following move operation, as we will need it for the
- // read barrier below.
- Register temp2 = temp2_loc.AsRegister<Register>();
- __ Mov(temp2, temp);
- }
// /* HeapReference<Class> */ temp = temp->component_type_
- __ LoadFromOffset(kLoadWord, temp, temp, component_offset);
- codegen_->MaybeGenerateReadBarrier(
- instruction, temp_loc, temp_loc, temp2_loc, component_offset);
+ GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, temp2_loc);
// If the component type is not null (i.e. the object is indeed
// an array), jump to label `check_non_primitive_component_type`
@@ -5689,8 +5722,7 @@
// going into the slow path, as it has been overwritten in the
// meantime.
// /* HeapReference<Class> */ temp = obj->klass_
- __ LoadFromOffset(kLoadWord, temp, obj, class_offset);
- codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset);
+ GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc);
__ b(type_check_slow_path->GetEntryLabel());
__ Bind(&check_non_primitive_component_type);
@@ -5699,8 +5731,7 @@
__ CompareAndBranchIfZero(temp, &done);
// Same comment as above regarding `temp` and the slow path.
// /* HeapReference<Class> */ temp = obj->klass_
- __ LoadFromOffset(kLoadWord, temp, obj, class_offset);
- codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset);
+ GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc);
__ b(type_check_slow_path->GetEntryLabel());
break;
}
@@ -5717,6 +5748,13 @@
// instruction (following the runtime calling convention), which
// might be cluttered by the potential first read barrier
// emission at the beginning of this method.
+ //
+ // TODO: Introduce a new runtime entry point taking the object
+ // to test (instead of its class) as argument, and let it deal
+ // with the read barrier issues. This will let us refactor this
+ // case of the `switch` code as it was previously (with a direct
+ // call to the runtime not using a type checking slow path).
+ // This should also be beneficial for the other cases above.
__ b(type_check_slow_path->GetEntryLabel());
break;
}
@@ -5901,14 +5939,249 @@
}
}
-void CodeGeneratorARM::GenerateReadBarrier(HInstruction* instruction,
- Location out,
- Location ref,
- Location obj,
- uint32_t offset,
- Location index) {
+void InstructionCodeGeneratorARM::GenerateReferenceLoadOneRegister(HInstruction* instruction,
+ Location out,
+ uint32_t offset,
+ Location temp) {
+ Register out_reg = out.AsRegister<Register>();
+ if (kEmitCompilerReadBarrier) {
+ if (kUseBakerReadBarrier) {
+ // Load with fast path based Baker's read barrier.
+ // /* HeapReference<Object> */ out = *(out + offset)
+ codegen_->GenerateFieldLoadWithBakerReadBarrier(
+ instruction, out, out_reg, offset, temp, /* needs_null_check */ false);
+ } else {
+ // Load with slow path based read barrier.
+ // Save the value of `out` into `temp` before overwriting it
+ // in the following move operation, as we will need it for the
+ // read barrier below.
+ __ Mov(temp.AsRegister<Register>(), out_reg);
+ // /* HeapReference<Object> */ out = *(out + offset)
+ __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
+ codegen_->GenerateReadBarrierSlow(instruction, out, out, temp, offset);
+ }
+ } else {
+ // Plain load with no read barrier.
+ // /* HeapReference<Object> */ out = *(out + offset)
+ __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
+ __ MaybeUnpoisonHeapReference(out_reg);
+ }
+}
+
+void InstructionCodeGeneratorARM::GenerateReferenceLoadTwoRegisters(HInstruction* instruction,
+ Location out,
+ Location obj,
+ uint32_t offset,
+ Location temp) {
+ Register out_reg = out.AsRegister<Register>();
+ Register obj_reg = obj.AsRegister<Register>();
+ if (kEmitCompilerReadBarrier) {
+ if (kUseBakerReadBarrier) {
+ // Load with fast path based Baker's read barrier.
+ // /* HeapReference<Object> */ out = *(obj + offset)
+ codegen_->GenerateFieldLoadWithBakerReadBarrier(
+ instruction, out, obj_reg, offset, temp, /* needs_null_check */ false);
+ } else {
+ // Load with slow path based read barrier.
+ // /* HeapReference<Object> */ out = *(obj + offset)
+ __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
+ codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
+ }
+ } else {
+ // Plain load with no read barrier.
+ // /* HeapReference<Object> */ out = *(obj + offset)
+ __ LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
+ __ MaybeUnpoisonHeapReference(out_reg);
+ }
+}
+
+void InstructionCodeGeneratorARM::GenerateGcRootFieldLoad(HInstruction* instruction,
+ Location root,
+ Register obj,
+ uint32_t offset) {
+ Register root_reg = root.AsRegister<Register>();
+ if (kEmitCompilerReadBarrier) {
+ if (kUseBakerReadBarrier) {
+ // Fast path implementation of art::ReadBarrier::BarrierForRoot when
+ // Baker's read barrier are used:
+ //
+ // root = obj.field;
+ // if (Thread::Current()->GetIsGcMarking()) {
+ // root = ReadBarrier::Mark(root)
+ // }
+
+ // /* GcRoot<mirror::Object> */ root = *(obj + offset)
+ __ LoadFromOffset(kLoadWord, root_reg, obj, offset);
+ static_assert(
+ sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
+ "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
+ "have different sizes.");
+ static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
+ "art::mirror::CompressedReference<mirror::Object> and int32_t "
+ "have different sizes.");
+
+ // Slow path used to mark the GC root `root`.
+ SlowPathCode* slow_path =
+ new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(instruction, root, root);
+ codegen_->AddSlowPath(slow_path);
+
+ __ LoadFromOffset(
+ kLoadWord, IP, TR, Thread::IsGcMarkingOffset<kArmWordSize>().Int32Value());
+ __ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+ } else {
+ // GC root loaded through a slow path for read barriers other
+ // than Baker's.
+ // /* GcRoot<mirror::Object>* */ root = obj + offset
+ __ AddConstant(root_reg, obj, offset);
+ // /* mirror::Object* */ root = root->Read()
+ codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
+ }
+ } else {
+ // Plain GC root load with no read barrier.
+ // /* GcRoot<mirror::Object> */ root = *(obj + offset)
+ __ LoadFromOffset(kLoadWord, root_reg, obj, offset);
+ // Note that GC roots are not affected by heap poisoning, thus we
+ // do not have to unpoison `root_reg` here.
+ }
+}
+
+void CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ Register obj,
+ uint32_t offset,
+ Location temp,
+ bool needs_null_check) {
+ DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(kUseBakerReadBarrier);
+
+ // /* HeapReference<Object> */ ref = *(obj + offset)
+ Location no_index = Location::NoLocation();
+ GenerateReferenceLoadWithBakerReadBarrier(
+ instruction, ref, obj, offset, no_index, temp, needs_null_check);
+}
+
+void CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ Register obj,
+ uint32_t data_offset,
+ Location index,
+ Location temp,
+ bool needs_null_check) {
+ DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(kUseBakerReadBarrier);
+
+ // /* HeapReference<Object> */ ref =
+ // *(obj + data_offset + index * sizeof(HeapReference<Object>))
+ GenerateReferenceLoadWithBakerReadBarrier(
+ instruction, ref, obj, data_offset, index, temp, needs_null_check);
+}
+
+void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ Register obj,
+ uint32_t offset,
+ Location index,
+ Location temp,
+ bool needs_null_check) {
+ DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(kUseBakerReadBarrier);
+
+ // In slow path based read barriers, the read barrier call is
+ // inserted after the original load. However, in fast path based
+ // Baker's read barriers, we need to perform the load of
+ // mirror::Object::monitor_ *before* the original reference load.
+ // This load-load ordering is required by the read barrier.
+ // The fast path/slow path (for Baker's algorithm) should look like:
+ //
+ // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
+ // lfence; // Load fence or artificial data dependency to prevent load-load reordering
+ // HeapReference<Object> ref = *src; // Original reference load.
+ // bool is_gray = (rb_state == ReadBarrier::gray_ptr_);
+ // if (is_gray) {
+ // ref = ReadBarrier::Mark(ref); // Performed by runtime entrypoint slow path.
+ // }
+ //
+ // Note: the original implementation in ReadBarrier::Barrier is
+ // slightly more complex as:
+ // - it implements the load-load fence using a data dependency on
+ // the high-bits of rb_state, which are expected to be all zeroes;
+ // - it performs additional checks that we do not do here for
+ // performance reasons.
+
+ Register ref_reg = ref.AsRegister<Register>();
+ Register temp_reg = temp.AsRegister<Register>();
+ uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
+
+ // /* int32_t */ monitor = obj->monitor_
+ __ LoadFromOffset(kLoadWord, temp_reg, obj, monitor_offset);
+ if (needs_null_check) {
+ MaybeRecordImplicitNullCheck(instruction);
+ }
+ // /* LockWord */ lock_word = LockWord(monitor)
+ static_assert(sizeof(LockWord) == sizeof(int32_t),
+ "art::LockWord and int32_t have different sizes.");
+ // /* uint32_t */ rb_state = lock_word.ReadBarrierState()
+ __ Lsr(temp_reg, temp_reg, LockWord::kReadBarrierStateShift);
+ __ and_(temp_reg, temp_reg, ShifterOperand(LockWord::kReadBarrierStateMask));
+ static_assert(
+ LockWord::kReadBarrierStateMask == ReadBarrier::rb_ptr_mask_,
+ "art::LockWord::kReadBarrierStateMask is not equal to art::ReadBarrier::rb_ptr_mask_.");
+
+ // Introduce a dependency on the high bits of rb_state, which shall
+ // be all zeroes, to prevent load-load reordering, and without using
+ // a memory barrier (which would be more expensive).
+ // IP = rb_state & ~LockWord::kReadBarrierStateMask = 0
+ __ bic(IP, temp_reg, ShifterOperand(LockWord::kReadBarrierStateMask));
+ // obj is unchanged by this operation, but its value now depends on
+ // IP, which depends on temp_reg.
+ __ add(obj, obj, ShifterOperand(IP));
+
+ // The actual reference load.
+ if (index.IsValid()) {
+ static_assert(
+ sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
+ "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
+ // /* HeapReference<Object> */ ref =
+ // *(obj + offset + index * sizeof(HeapReference<Object>))
+ if (index.IsConstant()) {
+ size_t computed_offset =
+ (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset;
+ __ LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset);
+ } else {
+ __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
+ __ LoadFromOffset(kLoadWord, ref_reg, IP, offset);
+ }
+ } else {
+ // /* HeapReference<Object> */ ref = *(obj + offset)
+ __ LoadFromOffset(kLoadWord, ref_reg, obj, offset);
+ }
+
+ // Object* ref = ref_addr->AsMirrorPtr()
+ __ MaybeUnpoisonHeapReference(ref_reg);
+
+ // Slow path used to mark the object `ref` when it is gray.
+ SlowPathCode* slow_path =
+ new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(instruction, ref, ref);
+ AddSlowPath(slow_path);
+
+ // if (rb_state == ReadBarrier::gray_ptr_)
+ // ref = ReadBarrier::Mark(ref);
+ __ cmp(temp_reg, ShifterOperand(ReadBarrier::gray_ptr_));
+ __ b(slow_path->GetEntryLabel(), EQ);
+ __ Bind(slow_path->GetExitLabel());
+}
+
+void CodeGeneratorARM::GenerateReadBarrierSlow(HInstruction* instruction,
+ Location out,
+ Location ref,
+ Location obj,
+ uint32_t offset,
+ Location index) {
DCHECK(kEmitCompilerReadBarrier);
+ // Insert a slow path based read barrier *after* the reference load.
+ //
// If heap poisoning is enabled, the unpoisoning of the loaded
// reference will be carried out by the runtime within the slow
// path.
@@ -5922,57 +6195,41 @@
ReadBarrierForHeapReferenceSlowPathARM(instruction, out, ref, obj, offset, index);
AddSlowPath(slow_path);
- // TODO: When read barrier has a fast path, add it here.
- /* Currently the read barrier call is inserted after the original load.
- * However, if we have a fast path, we need to perform the load of obj.LockWord *before* the
- * original load. This load-load ordering is required by the read barrier.
- * The fast path/slow path (for Baker's algorithm) should look like:
- *
- * bool isGray = obj.LockWord & kReadBarrierMask;
- * lfence; // load fence or artificial data dependence to prevent load-load reordering
- * ref = obj.field; // this is the original load
- * if (isGray) {
- * ref = Mark(ref); // ideally the slow path just does Mark(ref)
- * }
- */
-
__ b(slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
}
-void CodeGeneratorARM::MaybeGenerateReadBarrier(HInstruction* instruction,
- Location out,
- Location ref,
- Location obj,
- uint32_t offset,
- Location index) {
+void CodeGeneratorARM::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
+ Location out,
+ Location ref,
+ Location obj,
+ uint32_t offset,
+ Location index) {
if (kEmitCompilerReadBarrier) {
+ // Baker's read barriers shall be handled by the fast path
+ // (CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier).
+ DCHECK(!kUseBakerReadBarrier);
// If heap poisoning is enabled, unpoisoning will be taken care of
// by the runtime within the slow path.
- GenerateReadBarrier(instruction, out, ref, obj, offset, index);
+ GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
} else if (kPoisonHeapReferences) {
__ UnpoisonHeapReference(out.AsRegister<Register>());
}
}
-void CodeGeneratorARM::GenerateReadBarrierForRoot(HInstruction* instruction,
- Location out,
- Location root) {
+void CodeGeneratorARM::GenerateReadBarrierForRootSlow(HInstruction* instruction,
+ Location out,
+ Location root) {
DCHECK(kEmitCompilerReadBarrier);
+ // Insert a slow path based read barrier *after* the GC root load.
+ //
// Note that GC roots are not affected by heap poisoning, so we do
// not need to do anything special for this here.
SlowPathCode* slow_path =
new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathARM(instruction, out, root);
AddSlowPath(slow_path);
- // TODO: Implement a fast path for ReadBarrierForRoot, performing
- // the following operation (for Baker's algorithm):
- //
- // if (thread.tls32_.is_gc_marking) {
- // root = Mark(root);
- // }
-
__ b(slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
}
@@ -6356,7 +6613,7 @@
void CodeGeneratorARM::MoveFromReturnRegister(Location trg, Primitive::Type type) {
if (!trg.IsValid()) {
- DCHECK(type == Primitive::kPrimVoid);
+ DCHECK_EQ(type, Primitive::kPrimVoid);
return;
}
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index b7c58e1..f9c49a5 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -222,17 +222,51 @@
void HandleLongRotate(LocationSummary* locations);
void HandleRotate(HRor* ror);
void HandleShift(HBinaryOperation* operation);
- void GenerateMemoryBarrier(MemBarrierKind kind);
+
void GenerateWideAtomicStore(Register addr, uint32_t offset,
Register value_lo, Register value_hi,
Register temp1, Register temp2,
HInstruction* instruction);
void GenerateWideAtomicLoad(Register addr, uint32_t offset,
Register out_lo, Register out_hi);
+
void HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
bool value_can_be_null);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+
+ // Generate a heap reference load using one register `out`:
+ //
+ // out <- *(out + offset)
+ //
+ // while honoring heap poisoning and/or read barriers (if any).
+ // Register `temp` is used when generating a read barrier.
+ void GenerateReferenceLoadOneRegister(HInstruction* instruction,
+ Location out,
+ uint32_t offset,
+ Location temp);
+ // Generate a heap reference load using two different registers
+ // `out` and `obj`:
+ //
+ // out <- *(obj + offset)
+ //
+ // while honoring heap poisoning and/or read barriers (if any).
+ // Register `temp` is used when generating a Baker's read barrier.
+ void GenerateReferenceLoadTwoRegisters(HInstruction* instruction,
+ Location out,
+ Location obj,
+ uint32_t offset,
+ Location temp);
+ // Generate a GC root reference load:
+ //
+ // root <- *(obj + offset)
+ //
+ // while honoring read barriers (if any).
+ void GenerateGcRootFieldLoad(HInstruction* instruction,
+ Location root,
+ Register obj,
+ uint32_t offset);
+
void GenerateImplicitNullCheck(HNullCheck* instruction);
void GenerateExplicitNullCheck(HNullCheck* instruction);
void GenerateTestAndBranch(HInstruction* instruction,
@@ -346,6 +380,8 @@
// Emit a write barrier.
void MarkGCCard(Register temp, Register card, Register object, Register value, bool can_be_null);
+ void GenerateMemoryBarrier(MemBarrierKind kind);
+
Label* GetLabelOf(HBasicBlock* block) const {
return CommonGetLabelOf<Label>(block_labels_, block);
}
@@ -406,7 +442,26 @@
return &it->second;
}
- // Generate a read barrier for a heap reference within `instruction`.
+ // Fast path implementation of ReadBarrier::Barrier for a heap
+ // reference field load when Baker's read barriers are used.
+ void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location out,
+ Register obj,
+ uint32_t offset,
+ Location temp,
+ bool needs_null_check);
+ // Fast path implementation of ReadBarrier::Barrier for a heap
+ // reference array load when Baker's read barriers are used.
+ void GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location out,
+ Register obj,
+ uint32_t data_offset,
+ Location index,
+ Location temp,
+ bool needs_null_check);
+
+ // Generate a read barrier for a heap reference within `instruction`
+ // using a slow path.
//
// A read barrier for an object reference read from the heap is
// implemented as a call to the artReadBarrierSlow runtime entry
@@ -423,23 +478,25 @@
// When `index` is provided (i.e. for array accesses), the offset
// value passed to artReadBarrierSlow is adjusted to take `index`
// into account.
- void GenerateReadBarrier(HInstruction* instruction,
- Location out,
- Location ref,
- Location obj,
- uint32_t offset,
- Location index = Location::NoLocation());
+ void GenerateReadBarrierSlow(HInstruction* instruction,
+ Location out,
+ Location ref,
+ Location obj,
+ uint32_t offset,
+ Location index = Location::NoLocation());
- // If read barriers are enabled, generate a read barrier for a heap reference.
- // If heap poisoning is enabled, also unpoison the reference in `out`.
- void MaybeGenerateReadBarrier(HInstruction* instruction,
- Location out,
- Location ref,
- Location obj,
- uint32_t offset,
- Location index = Location::NoLocation());
+ // If read barriers are enabled, generate a read barrier for a heap
+ // reference using a slow path. If heap poisoning is enabled, also
+ // unpoison the reference in `out`.
+ void MaybeGenerateReadBarrierSlow(HInstruction* instruction,
+ Location out,
+ Location ref,
+ Location obj,
+ uint32_t offset,
+ Location index = Location::NoLocation());
- // Generate a read barrier for a GC root within `instruction`.
+ // Generate a read barrier for a GC root within `instruction` using
+ // a slow path.
//
// A read barrier for an object reference GC root is implemented as
// a call to the artReadBarrierForRootSlow runtime entry point,
@@ -449,9 +506,19 @@
//
// The `out` location contains the value returned by
// artReadBarrierForRootSlow.
- void GenerateReadBarrierForRoot(HInstruction* instruction, Location out, Location root);
+ void GenerateReadBarrierForRootSlow(HInstruction* instruction, Location out, Location root);
private:
+ // Factored implementation of GenerateFieldLoadWithBakerReadBarrier
+ // and GenerateArrayLoadWithBakerReadBarrier.
+ void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ Register obj,
+ uint32_t offset,
+ Location index,
+ Location temp,
+ bool needs_null_check);
+
Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp);
using MethodToLiteralMap = ArenaSafeMap<MethodReference, Literal*, MethodReferenceComparator>;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index b49f42b..1ad487d 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2534,8 +2534,7 @@
Register out = OutputRegister(instruction);
Register dividend = InputRegisterAt(instruction, 0);
int64_t imm = Int64FromConstant(second.GetConstant());
- uint64_t abs_imm = static_cast<uint64_t>(std::abs(imm));
- DCHECK(IsPowerOfTwo(abs_imm));
+ uint64_t abs_imm = static_cast<uint64_t>(AbsOrMin(imm));
int ctz_imm = CTZ(abs_imm);
UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -2627,7 +2626,7 @@
// Do not generate anything. DivZeroCheck would prevent any code to be executed.
} else if (imm == 1 || imm == -1) {
DivRemOneOrMinusOne(instruction);
- } else if (IsPowerOfTwo(std::abs(imm))) {
+ } else if (IsPowerOfTwo(AbsOrMin(imm))) {
DivRemByPowerOfTwo(instruction);
} else {
DCHECK(imm <= -2 || imm >= 2);
@@ -2954,6 +2953,10 @@
}
void InstructionCodeGeneratorARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ if (codegen_->HasStackMapAtCurrentPc()) {
+ // Ensure that we do not collide with the stack map of the previous instruction.
+ __ Nop();
+ }
codegen_->RecordPcInfo(info, info->GetDexPc());
}
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 4648606..7bc0635 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -2314,8 +2314,7 @@
Register out = locations->Out().AsRegister<Register>();
Register dividend = locations->InAt(0).AsRegister<Register>();
int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
- uint32_t abs_imm = static_cast<uint32_t>(std::abs(imm));
- DCHECK(IsPowerOfTwo(abs_imm));
+ uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
int ctz_imm = CTZ(abs_imm);
if (instruction->IsDiv()) {
@@ -2418,7 +2417,7 @@
// Do not generate anything. DivZeroCheck would prevent any code to be executed.
} else if (imm == 1 || imm == -1) {
DivRemOneOrMinusOne(instruction);
- } else if (IsPowerOfTwo(std::abs(imm))) {
+ } else if (IsPowerOfTwo(AbsOrMin(imm))) {
DivRemByPowerOfTwo(instruction);
} else {
DCHECK(imm <= -2 || imm >= 2);
@@ -3371,6 +3370,10 @@
}
void InstructionCodeGeneratorMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ if (codegen_->HasStackMapAtCurrentPc()) {
+ // Ensure that we do not collide with the stack map of the previous instruction.
+ __ Nop();
+ }
codegen_->RecordPcInfo(info, info->GetDexPc());
}
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 05834ff..7682ca7 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1955,8 +1955,7 @@
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
GpuRegister dividend = locations->InAt(0).AsRegister<GpuRegister>();
int64_t imm = Int64FromConstant(second.GetConstant());
- uint64_t abs_imm = static_cast<uint64_t>(std::abs(imm));
- DCHECK(IsPowerOfTwo(abs_imm));
+ uint64_t abs_imm = static_cast<uint64_t>(AbsOrMin(imm));
int ctz_imm = CTZ(abs_imm);
if (instruction->IsDiv()) {
@@ -2138,7 +2137,7 @@
// Do not generate anything. DivZeroCheck would prevent any code to be executed.
} else if (imm == 1 || imm == -1) {
DivRemOneOrMinusOne(instruction);
- } else if (IsPowerOfTwo(std::abs(imm))) {
+ } else if (IsPowerOfTwo(AbsOrMin(imm))) {
DivRemByPowerOfTwo(instruction);
} else {
DCHECK(imm <= -2 || imm >= 2);
@@ -2750,6 +2749,10 @@
}
void InstructionCodeGeneratorMIPS64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ if (codegen_->HasStackMapAtCurrentPc()) {
+ // Ensure that we do not collide with the stack map of the previous instruction.
+ __ Nop();
+ }
codegen_->RecordPcInfo(info, info->GetDexPc());
}
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index a808c27..4a0c2f4 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1625,6 +1625,10 @@
}
void InstructionCodeGeneratorX86::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ if (codegen_->HasStackMapAtCurrentPc()) {
+ // Ensure that we do not collide with the stack map of the previous instruction.
+ __ nop();
+ }
codegen_->RecordPcInfo(info, info->GetDexPc());
}
@@ -3223,11 +3227,12 @@
Register out_register = locations->Out().AsRegister<Register>();
Register input_register = locations->InAt(0).AsRegister<Register>();
int32_t imm = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+ DCHECK(IsPowerOfTwo(AbsOrMin(imm)));
+ uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
- DCHECK(IsPowerOfTwo(std::abs(imm)));
Register num = locations->GetTemp(0).AsRegister<Register>();
- __ leal(num, Address(input_register, std::abs(imm) - 1));
+ __ leal(num, Address(input_register, abs_imm - 1));
__ testl(input_register, input_register);
__ cmovl(kGreaterEqual, num, input_register);
int shift = CTZ(imm);
@@ -3340,7 +3345,7 @@
// Do not generate anything for 0. DivZeroCheck would forbid any generated code.
} else if (imm == 1 || imm == -1) {
DivRemOneOrMinusOne(instruction);
- } else if (is_div && IsPowerOfTwo(std::abs(imm))) {
+ } else if (is_div && IsPowerOfTwo(AbsOrMin(imm))) {
DivByPowerOfTwo(instruction->AsDiv());
} else {
DCHECK(imm <= -2 || imm >= 2);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 76a4ce2..ec62d84 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1608,6 +1608,10 @@
}
void InstructionCodeGeneratorX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ if (codegen_->HasStackMapAtCurrentPc()) {
+ // Ensure that we do not collide with the stack map of the previous instruction.
+ __ nop();
+ }
codegen_->RecordPcInfo(info, info->GetDexPc());
}
@@ -3350,13 +3354,13 @@
CpuRegister numerator = locations->InAt(0).AsRegister<CpuRegister>();
int64_t imm = Int64FromConstant(second.GetConstant());
-
- DCHECK(IsPowerOfTwo(std::abs(imm)));
+ DCHECK(IsPowerOfTwo(AbsOrMin(imm)));
+ uint64_t abs_imm = AbsOrMin(imm);
CpuRegister tmp = locations->GetTemp(0).AsRegister<CpuRegister>();
if (instruction->GetResultType() == Primitive::kPrimInt) {
- __ leal(tmp, Address(numerator, std::abs(imm) - 1));
+ __ leal(tmp, Address(numerator, abs_imm - 1));
__ testl(numerator, numerator);
__ cmov(kGreaterEqual, tmp, numerator);
int shift = CTZ(imm);
@@ -3371,7 +3375,7 @@
DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);
CpuRegister rdx = locations->GetTemp(0).AsRegister<CpuRegister>();
- codegen_->Load64BitValue(rdx, std::abs(imm) - 1);
+ codegen_->Load64BitValue(rdx, abs_imm - 1);
__ addq(rdx, numerator);
__ testq(numerator, numerator);
__ cmov(kGreaterEqual, rdx, numerator);
@@ -3529,7 +3533,7 @@
// Do not generate anything. DivZeroCheck would prevent any code to be executed.
} else if (imm == 1 || imm == -1) {
DivRemOneOrMinusOne(instruction);
- } else if (instruction->IsDiv() && IsPowerOfTwo(std::abs(imm))) {
+ } else if (instruction->IsDiv() && IsPowerOfTwo(AbsOrMin(imm))) {
DivByPowerOfTwo(instruction->AsDiv());
} else {
DCHECK(imm <= -2 || imm >= 2);
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 4683aee..1e6b3a1 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -502,9 +502,6 @@
bool is_volatile,
CodeGeneratorARM* codegen) {
LocationSummary* locations = invoke->GetLocations();
- DCHECK((type == Primitive::kPrimInt) ||
- (type == Primitive::kPrimLong) ||
- (type == Primitive::kPrimNot));
ArmAssembler* assembler = codegen->GetAssembler();
Location base_loc = locations->InAt(1);
Register base = base_loc.AsRegister<Register>(); // Object pointer.
@@ -512,30 +509,67 @@
Register offset = offset_loc.AsRegisterPairLow<Register>(); // Long offset, lo part only.
Location trg_loc = locations->Out();
- if (type == Primitive::kPrimLong) {
- Register trg_lo = trg_loc.AsRegisterPairLow<Register>();
- __ add(IP, base, ShifterOperand(offset));
- if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
- Register trg_hi = trg_loc.AsRegisterPairHigh<Register>();
- __ ldrexd(trg_lo, trg_hi, IP);
- } else {
- __ ldrd(trg_lo, Address(IP));
+ switch (type) {
+ case Primitive::kPrimInt: {
+ Register trg = trg_loc.AsRegister<Register>();
+ __ ldr(trg, Address(base, offset));
+ if (is_volatile) {
+ __ dmb(ISH);
+ }
+ break;
}
- } else {
- Register trg = trg_loc.AsRegister<Register>();
- __ ldr(trg, Address(base, offset));
- }
- if (is_volatile) {
- __ dmb(ISH);
- }
+ case Primitive::kPrimNot: {
+ Register trg = trg_loc.AsRegister<Register>();
+ if (kEmitCompilerReadBarrier) {
+ if (kUseBakerReadBarrier) {
+ Location temp = locations->GetTemp(0);
+ codegen->GenerateArrayLoadWithBakerReadBarrier(
+ invoke, trg_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false);
+ if (is_volatile) {
+ __ dmb(ISH);
+ }
+ } else {
+ __ ldr(trg, Address(base, offset));
+ if (is_volatile) {
+ __ dmb(ISH);
+ }
+ codegen->GenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
+ }
+ } else {
+ __ ldr(trg, Address(base, offset));
+ if (is_volatile) {
+ __ dmb(ISH);
+ }
+ __ MaybeUnpoisonHeapReference(trg);
+ }
+ break;
+ }
- if (type == Primitive::kPrimNot) {
- codegen->MaybeGenerateReadBarrier(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc);
+ case Primitive::kPrimLong: {
+ Register trg_lo = trg_loc.AsRegisterPairLow<Register>();
+ __ add(IP, base, ShifterOperand(offset));
+ if (is_volatile && !codegen->GetInstructionSetFeatures().HasAtomicLdrdAndStrd()) {
+ Register trg_hi = trg_loc.AsRegisterPairHigh<Register>();
+ __ ldrexd(trg_lo, trg_hi, IP);
+ } else {
+ __ ldrd(trg_lo, Address(IP));
+ }
+ if (is_volatile) {
+ __ dmb(ISH);
+ }
+ break;
+ }
+
+ default:
+ LOG(FATAL) << "Unexpected type " << type;
+ UNREACHABLE();
}
}
-static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+static void CreateIntIntIntToIntLocations(ArenaAllocator* arena,
+ HInvoke* invoke,
+ Primitive::Type type) {
bool can_call = kEmitCompilerReadBarrier &&
(invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
@@ -548,25 +582,30 @@
locations->SetInAt(1, Location::RequiresRegister());
locations->SetInAt(2, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ // We need a temporary register for the read barrier marking slow
+ // path in InstructionCodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
+ locations->AddTemp(Location::RequiresRegister());
+ }
}
void IntrinsicLocationsBuilderARM::VisitUnsafeGet(HInvoke* invoke) {
- CreateIntIntIntToIntLocations(arena_, invoke);
+ CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
}
void IntrinsicLocationsBuilderARM::VisitUnsafeGetVolatile(HInvoke* invoke) {
- CreateIntIntIntToIntLocations(arena_, invoke);
+ CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
}
void IntrinsicLocationsBuilderARM::VisitUnsafeGetLong(HInvoke* invoke) {
- CreateIntIntIntToIntLocations(arena_, invoke);
+ CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
}
void IntrinsicLocationsBuilderARM::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
- CreateIntIntIntToIntLocations(arena_, invoke);
+ CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
}
void IntrinsicLocationsBuilderARM::VisitUnsafeGetObject(HInvoke* invoke) {
- CreateIntIntIntToIntLocations(arena_, invoke);
+ CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
}
void IntrinsicLocationsBuilderARM::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
- CreateIntIntIntToIntLocations(arena_, invoke);
+ CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
}
void IntrinsicCodeGeneratorARM::VisitUnsafeGet(HInvoke* invoke) {
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index c85e573..6d4275d 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -2288,4 +2288,19 @@
}
}
+std::ostream& operator<<(std::ostream& os, const MoveOperands& rhs) {
+ os << "["
+ << " source=" << rhs.GetSource()
+ << " destination=" << rhs.GetDestination()
+ << " type=" << rhs.GetType()
+ << " instruction=";
+ if (rhs.GetInstruction() != nullptr) {
+ os << rhs.GetInstruction()->DebugName() << ' ' << rhs.GetInstruction()->GetId();
+ } else {
+ os << "null";
+ }
+ os << " ]";
+ return os;
+}
+
} // namespace art
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 1a7cbde..c06d164 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -5613,8 +5613,8 @@
}
bool IsPending() const {
- DCHECK(!source_.IsInvalid() || destination_.IsInvalid());
- return destination_.IsInvalid() && !source_.IsInvalid();
+ DCHECK(source_.IsValid() || destination_.IsInvalid());
+ return destination_.IsInvalid() && source_.IsValid();
}
// True if this blocks a move from the given location.
@@ -5658,6 +5658,8 @@
HInstruction* instruction_;
};
+std::ostream& operator<<(std::ostream& os, const MoveOperands& rhs);
+
static constexpr size_t kDefaultNumberOfMoves = 4;
class HParallelMove : public HTemplateInstruction<0> {
diff --git a/compiler/optimizing/parallel_move_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc
index 176c50c..9d136f3 100644
--- a/compiler/optimizing/parallel_move_resolver.cc
+++ b/compiler/optimizing/parallel_move_resolver.cc
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include <iostream>
#include "parallel_move_resolver.h"
@@ -172,7 +171,7 @@
i = -1;
} else if (required_swap != nullptr) {
// A move is required to swap. We walk back the cycle to find the
- // move by just returning from this `PerforrmMove`.
+ // move by just returning from this `PerformMove`.
moves_[index]->ClearPending(destination);
return required_swap;
}
@@ -201,7 +200,7 @@
} else {
for (MoveOperands* other_move : moves_) {
if (other_move->Blocks(destination)) {
- DCHECK(other_move->IsPending());
+ DCHECK(other_move->IsPending()) << "move=" << *move << " other_move=" << *other_move;
if (!move->Is64BitMove() && other_move->Is64BitMove()) {
// We swap 64bits moves before swapping 32bits moves. Go back from the
// cycle by returning the move that must be swapped.
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index d1770b7..63ef600 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -96,7 +96,7 @@
if (can_merge_with_load_class && !load_class->HasUses()) {
load_class->GetBlock()->RemoveInstruction(load_class);
}
- } else if (can_merge_with_load_class) {
+ } else if (can_merge_with_load_class && !load_class->NeedsAccessCheck()) {
// Pass the initialization duty to the `HLoadClass` instruction,
// and remove the instruction from the graph.
load_class->SetMustGenerateClinitCheck(true);
diff --git a/compiler/profile_assistant.cc b/compiler/profile_assistant.cc
new file mode 100644
index 0000000..81f2a56
--- /dev/null
+++ b/compiler/profile_assistant.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "profile_assistant.h"
+
+namespace art {
+
+// Minimum number of new methods that profiles must contain to enable recompilation.
+static constexpr const uint32_t kMinNewMethodsForCompilation = 10;
+
+bool ProfileAssistant::ProcessProfiles(
+ const std::vector<std::string>& profile_files,
+ const std::vector<std::string>& reference_profile_files,
+ /*out*/ ProfileCompilationInfo** profile_compilation_info) {
+ DCHECK(!profile_files.empty());
+ DCHECK(reference_profile_files.empty() ||
+ (profile_files.size() == reference_profile_files.size()));
+
+ std::vector<ProfileCompilationInfo> new_info(profile_files.size());
+ bool should_compile = false;
+ // Read the main profile files.
+ for (size_t i = 0; i < profile_files.size(); i++) {
+ if (!new_info[i].Load(profile_files[i])) {
+ LOG(WARNING) << "Could not load profile file: " << profile_files[i];
+ return false;
+ }
+ // Do we have enough new profiled methods that will make the compilation worthwhile?
+ should_compile |= (new_info[i].GetNumberOfMethods() > kMinNewMethodsForCompilation);
+ }
+ if (!should_compile) {
+ *profile_compilation_info = nullptr;
+ return true;
+ }
+
+ std::unique_ptr<ProfileCompilationInfo> result(new ProfileCompilationInfo());
+ for (size_t i = 0; i < new_info.size(); i++) {
+ // Merge all data into a single object.
+ result->Load(new_info[i]);
+ // If we have any reference profile information merge their information with
+ // the current profiles and save them back to disk.
+ if (!reference_profile_files.empty()) {
+ if (!new_info[i].Load(reference_profile_files[i])) {
+ LOG(WARNING) << "Could not load reference profile file: " << reference_profile_files[i];
+ return false;
+ }
+ if (!new_info[i].Save(reference_profile_files[i])) {
+ LOG(WARNING) << "Could not save reference profile file: " << reference_profile_files[i];
+ return false;
+ }
+ }
+ }
+ *profile_compilation_info = result.release();
+ return true;
+}
+
+} // namespace art
diff --git a/compiler/profile_assistant.h b/compiler/profile_assistant.h
new file mode 100644
index 0000000..088c8bd
--- /dev/null
+++ b/compiler/profile_assistant.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_PROFILE_ASSISTANT_H_
+#define ART_COMPILER_PROFILE_ASSISTANT_H_
+
+#include <string>
+#include <vector>
+
+#include "jit/offline_profiling_info.cc"
+
+namespace art {
+
+class ProfileAssistant {
+ public:
+ // Process the profile information present in the given files. Returns true
+ // if the analysis ended up successfully (i.e. no errors during reading,
+ // merging or writing of profile files).
+ //
+ // If the returned value is true and there is a significant difference between
+ // profile_files and reference_profile_files:
+ // - profile_compilation_info is set to a not null object that
+ // can be used to drive compilation. It will be the merge of all the data
+ // found in profile_files and reference_profile_files.
+ // - the data from profile_files[i] is merged into
+ // reference_profile_files[i] and the corresponding backing file is
+ // updated.
+ //
+ // If the returned value is false or the difference is insignificant,
+ // profile_compilation_info will be set to null.
+ //
+ // Additional notes:
+ // - as mentioned above, this function may update the content of the files
+ // passed with the reference_profile_files.
+ // - if reference_profile_files is not empty it must be the same size as
+ // profile_files.
+ static bool ProcessProfiles(
+ const std::vector<std::string>& profile_files,
+ const std::vector<std::string>& reference_profile_files,
+ /*out*/ ProfileCompilationInfo** profile_compilation_info);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProfileAssistant);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_PROFILE_ASSISTANT_H_
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 50480d9..32a237a 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -63,6 +63,7 @@
#include "gc/space/space-inl.h"
#include "image_writer.h"
#include "interpreter/unstarted_runtime.h"
+#include "jit/offline_profiling_info.h"
#include "leb128.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
@@ -70,6 +71,7 @@
#include "mirror/object_array-inl.h"
#include "oat_writer.h"
#include "os.h"
+#include "profile_assistant.h"
#include "runtime.h"
#include "runtime_options.h"
#include "ScopedLocalRef.h"
@@ -328,6 +330,16 @@
UsageError(" Example: --runtime-arg -Xms256m");
UsageError("");
UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation.");
+ UsageError(" Can be specified multiple time, in which case the data from the different");
+ UsageError(" profiles will be aggregated.");
+ UsageError("");
+ UsageError(" --reference-profile-file=<filename>: specify a reference profile file to use when");
+ UsageError(" compiling. The data in this file will be compared with the data in the");
+ UsageError(" associated --profile-file and the compilation will proceed only if there is");
+ UsageError(" a significant difference (--reference-profile-file is paired with");
+ UsageError(" --profile-file in the natural order). If the compilation was attempted then");
+ UsageError(" --profile-file will be merged into --reference-profile-file. Valid only when");
+ UsageError(" specified together with --profile-file.");
UsageError("");
UsageError(" --print-pass-names: print a list of pass names");
UsageError("");
@@ -767,6 +779,13 @@
}
}
+ if (!profile_files_.empty()) {
+ if (!reference_profile_files_.empty() &&
+ (reference_profile_files_.size() != profile_files_.size())) {
+ Usage("If specified, --reference-profile-file should match the number of --profile-file.");
+ }
+ }
+
if (!parser_options->oat_symbols.empty()) {
oat_unstripped_ = std::move(parser_options->oat_symbols);
}
@@ -1057,8 +1076,10 @@
} else if (option.starts_with("--compiler-backend=")) {
ParseCompilerBackend(option, parser_options.get());
} else if (option.starts_with("--profile-file=")) {
- profile_file_ = option.substr(strlen("--profile-file=")).data();
- VLOG(compiler) << "dex2oat: profile file is " << profile_file_;
+ profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString());
+ } else if (option.starts_with("--reference-profile-file=")) {
+ reference_profile_files_.push_back(
+ option.substr(strlen("--reference-profile-file=")).ToString());
} else if (option == "--no-profile-file") {
// No profile
} else if (option == "--host") {
@@ -1479,9 +1500,8 @@
dump_cfg_append_,
compiler_phases_timings_.get(),
swap_fd_,
- profile_file_,
- &dex_file_oat_filename_map_));
-
+ &dex_file_oat_filename_map_,
+ profile_compilation_info_.get()));
driver_->SetDexFilesForOatFile(dex_files_);
driver_->CompileAll(class_loader, dex_files_, timings_);
}
@@ -1569,7 +1589,6 @@
std::vector<gc::space::ImageSpace*> image_spaces =
Runtime::Current()->GetHeap()->GetBootImageSpaces();
for (gc::space::ImageSpace* image_space : image_spaces) {
- // TODO: IS THIS IN ORDER? JUST TAKE THE LAST ONE?
image_base_ = std::max(image_base_, RoundUp(
reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatFileEnd()),
kPageSize));
@@ -1790,6 +1809,26 @@
return is_host_;
}
+ bool UseProfileGuidedCompilation() const {
+ return !profile_files_.empty();
+ }
+
+ bool ProcessProfiles() {
+ DCHECK(UseProfileGuidedCompilation());
+ ProfileCompilationInfo* info = nullptr;
+ if (ProfileAssistant::ProcessProfiles(profile_files_, reference_profile_files_, &info)) {
+ profile_compilation_info_.reset(info);
+ return true;
+ }
+ return false;
+ }
+
+ bool ShouldCompileBasedOnProfiles() const {
+ DCHECK(UseProfileGuidedCompilation());
+ // If we are given profiles, compile only if we have new information.
+ return profile_compilation_info_ != nullptr;
+ }
+
private:
template <typename T>
static std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) {
@@ -2263,7 +2302,9 @@
int swap_fd_;
std::string app_image_file_name_;
int app_image_fd_;
- std::string profile_file_; // Profile file to use
+ std::vector<std::string> profile_files_;
+ std::vector<std::string> reference_profile_files_;
+ std::unique_ptr<ProfileCompilationInfo> profile_compilation_info_;
TimingLogger* timings_;
std::unique_ptr<CumulativeLogger> compiler_phases_timings_;
std::vector<std::vector<const DexFile*>> dex_files_per_oat_file_;
@@ -2380,6 +2421,20 @@
// Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
dex2oat.ParseArgs(argc, argv);
+ // Process profile information and assess if we need to do a profile guided compilation.
+ // This operation involves I/O.
+ if (dex2oat.UseProfileGuidedCompilation()) {
+ if (dex2oat.ProcessProfiles()) {
+ if (!dex2oat.ShouldCompileBasedOnProfiles()) {
+ LOG(INFO) << "Skipped compilation because of insignificant profile delta";
+ return EXIT_SUCCESS;
+ }
+ } else {
+ LOG(WARNING) << "Failed to process profile files";
+ return EXIT_FAILURE;
+ }
+ }
+
// Check early that the result of compilation can be written
if (!dex2oat.OpenFile()) {
return EXIT_FAILURE;
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 70bd398..82a5f96 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -855,6 +855,18 @@
PLOG(FATAL) << "futex wait failed for " << name_;
}
}
+ if (self != nullptr) {
+ JNIEnvExt* const env = self->GetJniEnv();
+ if (UNLIKELY(env != nullptr && env->runtime_deleted)) {
+ CHECK(self->IsDaemon());
+ // If the runtime has been deleted, then we cannot proceed. Just sleep forever. This may
+ // occur for user daemon threads that get a spurious wakeup. This occurs for test 132 with
+ // --host and --gdb.
+ // After we wake up, the runtime may have been shutdown, which means that this condition may
+ // have been deleted. It is not safe to retry the wait.
+ SleepForever();
+ }
+ }
guard_.ExclusiveLock(self);
CHECK_GE(num_waiters_, 0);
num_waiters_--;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d995f28..41842e8 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -736,7 +736,7 @@
static void SanityCheckArtMethod(ArtMethod* m,
mirror::Class* expected_class,
- std::vector<gc::space::ImageSpace*>& spaces)
+ const std::vector<gc::space::ImageSpace*>& spaces)
SHARED_REQUIRES(Locks::mutator_lock_) {
if (m->IsRuntimeMethod()) {
CHECK(m->GetDeclaringClass() == nullptr) << PrettyMethod(m);
@@ -760,7 +760,7 @@
static void SanityCheckArtMethodPointerArray(mirror::PointerArray* arr,
mirror::Class* expected_class,
size_t pointer_size,
- std::vector<gc::space::ImageSpace*>& spaces)
+ const std::vector<gc::space::ImageSpace*>& spaces)
SHARED_REQUIRES(Locks::mutator_lock_) {
CHECK(arr != nullptr);
for (int32_t j = 0; j < arr->GetLength(); ++j) {
@@ -778,7 +778,7 @@
static void SanityCheckArtMethodPointerArray(ArtMethod** arr,
size_t size,
size_t pointer_size,
- std::vector<gc::space::ImageSpace*>& spaces)
+ const std::vector<gc::space::ImageSpace*>& spaces)
SHARED_REQUIRES(Locks::mutator_lock_) {
CHECK_EQ(arr != nullptr, size != 0u);
if (arr != nullptr) {
@@ -1113,6 +1113,7 @@
mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
+ size_t class_tables_added = 0;
for (gc::space::ImageSpace* space : spaces) {
const ImageHeader& header = space->GetImageHeader();
const ImageSection& section = header.GetImageSection(ImageHeader::kSectionClassTable);
@@ -1120,9 +1121,17 @@
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
ClassTable* const class_table = InsertClassTableForClassLoader(nullptr);
class_table->ReadFromMemory(space->Begin() + section.Offset());
- dex_cache_boot_image_class_lookup_required_ = false;
+ ++class_tables_added;
}
}
+ if (class_tables_added != 0) {
+ // Either all of the image spaces have an empty class section or none do. In the case where
+ // an image space has no classes, it will still have a non-empty class section that contains
+ // metadata.
+ CHECK_EQ(spaces.size(), class_tables_added)
+ << "Expected non-empty class section for each image space.";
+ dex_cache_boot_image_class_lookup_required_ = false;
+ }
FinishInit(self);
diff --git a/runtime/class_table.h b/runtime/class_table.h
index c911365..911f3c2 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -106,8 +106,7 @@
// Combines all of the tables into one class set.
size_t WriteToMemory(uint8_t* ptr) const
- REQUIRES(Locks::classlinker_classes_lock_)
- SHARED_REQUIRES(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
size_t ReadFromMemory(uint8_t* ptr)
REQUIRES(Locks::classlinker_classes_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index 5dc0e45..a132701 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -30,38 +30,40 @@
namespace art {
-void OfflineProfilingInfo::SaveProfilingInfo(const std::string& filename,
- const std::vector<ArtMethod*>& methods) {
+bool ProfileCompilationInfo::SaveProfilingInfo(const std::string& filename,
+ const std::vector<ArtMethod*>& methods) {
if (methods.empty()) {
VLOG(profiler) << "No info to save to " << filename;
- return;
+ return true;
}
- DexFileToMethodsMap info;
+ ProfileCompilationInfo info;
+ if (!info.Load(filename)) {
+ LOG(WARNING) << "Could not load previous profile data from file " << filename;
+ return false;
+ }
{
ScopedObjectAccess soa(Thread::Current());
for (auto it = methods.begin(); it != methods.end(); it++) {
- AddMethodInfo(*it, &info);
+ const DexFile* dex_file = (*it)->GetDexFile();
+ if (!info.AddData(dex_file->GetLocation(),
+ dex_file->GetLocationChecksum(),
+ (*it)->GetDexMethodIndex())) {
+ return false;
+ }
}
}
// This doesn't need locking because we are trying to lock the file for exclusive
// access and fail immediately if we can't.
- if (Serialize(filename, info)) {
+ bool result = info.Save(filename);
+ if (result) {
VLOG(profiler) << "Successfully saved profile info to " << filename
<< " Size: " << GetFileSizeBytes(filename);
+ } else {
+ VLOG(profiler) << "Failed to save profile info to " << filename;
}
-}
-
-void OfflineProfilingInfo::AddMethodInfo(ArtMethod* method, DexFileToMethodsMap* info) {
- DCHECK(method != nullptr);
- const DexFile* dex_file = method->GetDexFile();
-
- auto info_it = info->find(dex_file);
- if (info_it == info->end()) {
- info_it = info->Put(dex_file, std::set<uint32_t>());
- }
- info_it->second.insert(method->GetDexMethodIndex());
+ return result;
}
enum OpenMode {
@@ -77,9 +79,7 @@
break;
case READ_WRITE:
// TODO(calin) allow the shared uid of the app to access the file.
- fd = open(filename.c_str(),
- O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
- S_IRUSR | S_IWUSR);
+ fd = open(filename.c_str(), O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC);
break;
}
@@ -137,8 +137,7 @@
* /system/priv-app/app/app.apk,131232145,11,23,454,54
* /system/priv-app/app/app.apk:classes5.dex,218490184,39,13,49,1
**/
-bool OfflineProfilingInfo::Serialize(const std::string& filename,
- const DexFileToMethodsMap& info) const {
+bool ProfileCompilationInfo::Save(const std::string& filename) {
int fd = OpenFile(filename, READ_WRITE);
if (fd == -1) {
return false;
@@ -148,14 +147,12 @@
// TODO(calin): Profile this and see how much memory it takes. If too much,
// write to file directly.
std::ostringstream os;
- for (auto it : info) {
- const DexFile* dex_file = it.first;
- const std::set<uint32_t>& method_dex_ids = it.second;
+ for (const auto& it : info_) {
+ const std::string& dex_location = it.first;
+ const DexFileData& dex_data = it.second;
- os << dex_file->GetLocation()
- << kFieldSeparator
- << dex_file->GetLocationChecksum();
- for (auto method_it : method_dex_ids) {
+ os << dex_location << kFieldSeparator << dex_data.checksum;
+ for (auto method_it : dex_data.method_set) {
os << kFieldSeparator << method_it;
}
os << kLineSeparator;
@@ -190,8 +187,22 @@
}
}
-bool ProfileCompilationInfo::ProcessLine(const std::string& line,
- const std::vector<const DexFile*>& dex_files) {
+bool ProfileCompilationInfo::AddData(const std::string& dex_location,
+ uint32_t checksum,
+ uint16_t method_idx) {
+ auto info_it = info_.find(dex_location);
+ if (info_it == info_.end()) {
+ info_it = info_.Put(dex_location, DexFileData(checksum));
+ }
+ if (info_it->second.checksum != checksum) {
+ LOG(WARNING) << "Checksum mismatch for dex " << dex_location;
+ return false;
+ }
+ info_it->second.method_set.insert(method_idx);
+ return true;
+}
+
+bool ProfileCompilationInfo::ProcessLine(const std::string& line) {
std::vector<std::string> parts;
SplitString(line, kFieldSeparator, &parts);
if (parts.size() < 3) {
@@ -205,39 +216,13 @@
return false;
}
- const DexFile* current_dex_file = nullptr;
- for (auto dex_file : dex_files) {
- if (dex_file->GetLocation() == dex_location) {
- if (checksum != dex_file->GetLocationChecksum()) {
- LOG(WARNING) << "Checksum mismatch for "
- << dex_file->GetLocation() << " when parsing " << filename_;
- return false;
- }
- current_dex_file = dex_file;
- break;
- }
- }
- if (current_dex_file == nullptr) {
- return true;
- }
-
for (size_t i = 2; i < parts.size(); i++) {
uint32_t method_idx;
if (!ParseInt(parts[i].c_str(), &method_idx)) {
LOG(WARNING) << "Cannot parse method_idx " << parts[i];
return false;
}
- uint16_t class_idx = current_dex_file->GetMethodId(method_idx).class_idx_;
- auto info_it = info_.find(current_dex_file);
- if (info_it == info_.end()) {
- info_it = info_.Put(current_dex_file, ClassToMethodsMap());
- }
- ClassToMethodsMap& class_map = info_it->second;
- auto class_it = class_map.find(class_idx);
- if (class_it == class_map.end()) {
- class_it = class_map.Put(class_idx, std::set<uint32_t>());
- }
- class_it->second.insert(method_idx);
+ AddData(dex_location, checksum, method_idx);
}
return true;
}
@@ -264,25 +249,8 @@
return new_line_pos == -1 ? new_line_pos : new_line_pos + 1;
}
-bool ProfileCompilationInfo::Load(const std::vector<const DexFile*>& dex_files) {
- if (dex_files.empty()) {
- return true;
- }
- if (kIsDebugBuild) {
- // In debug builds verify that the locations are unique.
- std::set<std::string> locations;
- for (auto dex_file : dex_files) {
- const std::string& location = dex_file->GetLocation();
- DCHECK(locations.find(location) == locations.end())
- << "DexFiles appear to belong to different apks."
- << " There are multiple dex files with the same location: "
- << location;
- locations.insert(location);
- }
- }
- info_.clear();
-
- int fd = OpenFile(filename_, READ);
+bool ProfileCompilationInfo::Load(const std::string& filename) {
+ int fd = OpenFile(filename, READ);
if (fd == -1) {
return false;
}
@@ -295,7 +263,7 @@
while (success) {
int n = read(fd, buffer, kBufferSize);
if (n < 0) {
- PLOG(WARNING) << "Error when reading profile file " << filename_;
+ PLOG(WARNING) << "Error when reading profile file " << filename;
success = false;
break;
} else if (n == 0) {
@@ -309,7 +277,7 @@
if (current_start_pos == -1) {
break;
}
- if (!ProcessLine(current_line, dex_files)) {
+ if (!ProcessLine(current_line)) {
success = false;
break;
}
@@ -320,25 +288,50 @@
if (!success) {
info_.clear();
}
- return CloseDescriptorForFile(fd, filename_) && success;
+ return CloseDescriptorForFile(fd, filename) && success;
+}
+
+bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) {
+ for (const auto& other_it : other.info_) {
+ const std::string& other_dex_location = other_it.first;
+ const DexFileData& other_dex_data = other_it.second;
+
+ auto info_it = info_.find(other_dex_location);
+ if (info_it == info_.end()) {
+ info_it = info_.Put(other_dex_location, DexFileData(other_dex_data.checksum));
+ }
+ if (info_it->second.checksum != other_dex_data.checksum) {
+ LOG(WARNING) << "Checksum mismatch for dex " << other_dex_location;
+ return false;
+ }
+ info_it->second.method_set.insert(other_dex_data.method_set.begin(),
+ other_dex_data.method_set.end());
+ }
+ return true;
}
bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
- auto info_it = info_.find(method_ref.dex_file);
+ auto info_it = info_.find(method_ref.dex_file->GetLocation());
if (info_it != info_.end()) {
- uint16_t class_idx = method_ref.dex_file->GetMethodId(method_ref.dex_method_index).class_idx_;
- const ClassToMethodsMap& class_map = info_it->second;
- auto class_it = class_map.find(class_idx);
- if (class_it != class_map.end()) {
- const std::set<uint32_t>& methods = class_it->second;
- return methods.find(method_ref.dex_method_index) != methods.end();
+ if (method_ref.dex_file->GetLocationChecksum() != info_it->second.checksum) {
+ return false;
}
- return false;
+ const std::set<uint16_t>& methods = info_it->second.method_set;
+ return methods.find(method_ref.dex_method_index) != methods.end();
}
return false;
}
-std::string ProfileCompilationInfo::DumpInfo(bool print_full_dex_location) const {
+uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
+ uint32_t total = 0;
+ for (const auto& it : info_) {
+ total += it.second.method_set.size();
+ }
+ return total;
+}
+
+std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
+ bool print_full_dex_location) const {
std::ostringstream os;
if (info_.empty()) {
return "ProfileInfo: empty";
@@ -346,17 +339,11 @@
os << "ProfileInfo:";
- // Use an additional map to achieve a predefined order based on the dex locations.
- SafeMap<const std::string, const DexFile*> dex_locations_map;
- for (auto info_it : info_) {
- dex_locations_map.Put(info_it.first->GetLocation(), info_it.first);
- }
-
const std::string kFirstDexFileKeySubstitute = ":classes.dex";
- for (auto dex_file_it : dex_locations_map) {
+ for (const auto& it : info_) {
os << "\n";
- const std::string& location = dex_file_it.first;
- const DexFile* dex_file = dex_file_it.second;
+ const std::string& location = it.first;
+ const DexFileData& dex_data = it.second;
if (print_full_dex_location) {
os << location;
} else {
@@ -364,10 +351,19 @@
std::string multidex_suffix = DexFile::GetMultiDexSuffix(location);
os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
}
- for (auto class_it : info_.find(dex_file)->second) {
- for (auto method_it : class_it.second) {
- os << "\n " << PrettyMethod(method_it, *dex_file, true);
+ for (const auto method_it : dex_data.method_set) {
+ if (dex_files != nullptr) {
+ const DexFile* dex_file = nullptr;
+ for (size_t i = 0; i < dex_files->size(); i++) {
+ if (location == (*dex_files)[i]->GetLocation()) {
+ dex_file = (*dex_files)[i];
+ }
+ }
+ if (dex_file != nullptr) {
+ os << "\n " << PrettyMethod(method_it, *dex_file, true);
+ }
}
+ os << "\n " << method_it;
}
}
return os.str();
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index 32d4c5b..26e1ac3 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -29,60 +29,50 @@
class ArtMethod;
+// TODO: rename file.
/**
- * Profiling information in a format that can be serialized to disk.
- * It is a serialize-friendly format based on information collected
- * by the interpreter (ProfileInfo).
+ * Profile information in a format suitable to be queried by the compiler and
+ * performing profile guided compilation.
+ * It is a serialize-friendly format based on information collected by the
+ * interpreter (ProfileInfo).
* Currently it stores only the hot compiled methods.
*/
-class OfflineProfilingInfo {
- public:
- void SaveProfilingInfo(const std::string& filename, const std::vector<ArtMethod*>& methods);
-
- private:
- // Map identifying the location of the profiled methods.
- // dex_file_ -> [dex_method_index]+
- using DexFileToMethodsMap = SafeMap<const DexFile*, std::set<uint32_t>>;
-
- void AddMethodInfo(ArtMethod* method, DexFileToMethodsMap* info)
- SHARED_REQUIRES(Locks::mutator_lock_);
- bool Serialize(const std::string& filename, const DexFileToMethodsMap& info) const;
-};
-
-/**
- * Profile information in a format suitable to be queried by the compiler and performing
- * profile guided compilation.
- */
class ProfileCompilationInfo {
public:
- // Constructs a ProfileCompilationInfo backed by the provided file.
- explicit ProfileCompilationInfo(const std::string& filename) : filename_(filename) {}
+ static bool SaveProfilingInfo(const std::string& filename,
+ const std::vector<ArtMethod*>& methods);
- // Loads profile information corresponding to the provided dex files.
- // The dex files' multidex suffixes must be unique.
- // This resets the state of the profiling information
- // (i.e. all previously loaded info are cleared).
- bool Load(const std::vector<const DexFile*>& dex_files);
+ // Loads profile information from the given file.
+ bool Load(const std::string& profile_filename);
+ // Loads the data from another ProfileCompilationInfo object.
+ bool Load(const ProfileCompilationInfo& info);
+ // Saves the profile data to the given file.
+ bool Save(const std::string& profile_filename);
+ // Returns the number of methods that were profiled.
+ uint32_t GetNumberOfMethods() const;
// Returns true if the method reference is present in the profiling info.
bool ContainsMethod(const MethodReference& method_ref) const;
- const std::string& GetFilename() const { return filename_; }
-
// Dumps all the loaded profile info into a string and returns it.
+ // If dex_files is not null then the method indices will be resolved to their
+ // names.
// This is intended for testing and debugging.
- std::string DumpInfo(bool print_full_dex_location = true) const;
+ std::string DumpInfo(const std::vector<const DexFile*>* dex_files,
+ bool print_full_dex_location = true) const;
private:
- bool ProcessLine(const std::string& line,
- const std::vector<const DexFile*>& dex_files);
+ bool AddData(const std::string& dex_location, uint32_t checksum, uint16_t method_idx);
+ bool ProcessLine(const std::string& line);
- using ClassToMethodsMap = SafeMap<uint32_t, std::set<uint32_t>>;
- // Map identifying the location of the profiled methods.
- // dex_file -> class_index -> [dex_method_index]+
- using DexFileToProfileInfoMap = SafeMap<const DexFile*, ClassToMethodsMap>;
+ struct DexFileData {
+ explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {}
+ uint32_t checksum;
+ std::set<uint16_t> method_set;
+ };
- const std::string filename_;
+ using DexFileToProfileInfoMap = SafeMap<const std::string, DexFileData>;
+
DexFileToProfileInfoMap info_;
};
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 0278138..ec289ea 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -106,10 +106,9 @@
VLOG(profiler) << "Not enough information to save. Nr of methods: " << methods.size();
return false;
}
- offline_profiling_info_.SaveProfilingInfo(output_filename_, methods);
- VLOG(profiler) << "Saved profile time: " << PrettyDuration(NanoTime() - start);
-
+ ProfileCompilationInfo::SaveProfilingInfo(output_filename_, methods);
+ VLOG(profiler) << "Profile process time: " << PrettyDuration(NanoTime() - start);
return true;
}
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 88efd41..d60142b 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -66,7 +66,6 @@
const std::string output_filename_;
jit::JitCodeCache* jit_code_cache_;
const std::set<const std::string> tracked_dex_base_locations_;
- OfflineProfilingInfo offline_profiling_info_;
uint64_t code_cache_last_update_time_ns_;
bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index aa25f67..1ee1611 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -59,6 +59,7 @@
local_ref_cookie(IRT_FIRST_SEGMENT),
locals(kLocalsInitial, kLocalsMax, kLocal, false),
check_jni(false),
+ runtime_deleted(false),
critical(0),
monitors("monitors", kMonitorsInitial, kMonitorsMax) {
functions = unchecked_functions = GetJniNativeInterface();
@@ -67,6 +68,11 @@
}
}
+void JNIEnvExt::SetFunctionsToRuntimeShutdownFunctions() {
+ functions = GetRuntimeShutdownNativeInterface();
+ runtime_deleted = true;
+}
+
JNIEnvExt::~JNIEnvExt() {
}
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index 2f8decf..d4accc3 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -74,6 +74,9 @@
// Frequently-accessed fields cached from JavaVM.
bool check_jni;
+ // If we are a JNI env for a daemon thread with a deleted runtime.
+ bool runtime_deleted;
+
// How many nested "critical" JNI calls are we in?
int critical;
@@ -95,6 +98,9 @@
// Check that no monitors are held that have been acquired in this JNI "segment."
void CheckNoHeldMonitors() SHARED_REQUIRES(Locks::mutator_lock_);
+ // Set the functions to the runtime shutdown functions.
+ void SetFunctionsToRuntimeShutdownFunctions();
+
private:
// The constructor should not be called directly. It may leave the object in an erronuous state,
// and the result needs to be checked.
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index cb67ee3..c893a0f 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -2734,6 +2734,246 @@
return &gJniNativeInterface;
}
+void (*gJniSleepForeverStub[])() = {
+ nullptr, // reserved0.
+ nullptr, // reserved1.
+ nullptr, // reserved2.
+ nullptr, // reserved3.
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+ SleepForever,
+};
+
+const JNINativeInterface* GetRuntimeShutdownNativeInterface() {
+ return reinterpret_cast<JNINativeInterface*>(&gJniSleepForeverStub);
+}
+
void RegisterNativeMethods(JNIEnv* env, const char* jni_class_name, const JNINativeMethod* methods,
jint method_count) {
ScopedLocalRef<jclass> c(env, env->FindClass(jni_class_name));
diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h
index 48b10f5..3429962 100644
--- a/runtime/jni_internal.h
+++ b/runtime/jni_internal.h
@@ -30,6 +30,7 @@
namespace art {
const JNINativeInterface* GetJniNativeInterface();
+const JNINativeInterface* GetRuntimeShutdownNativeInterface();
// Similar to RegisterNatives except its passed a descriptor for a class name and failures are
// fatal.
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 4b24f82..da4a891 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -565,8 +565,8 @@
*/
static void VMRuntime_registerAppInfo(JNIEnv* env,
jclass clazz ATTRIBUTE_UNUSED,
- jstring pkg_name,
- jstring app_dir,
+ jstring profile_file,
+ jstring app_dir ATTRIBUTE_UNUSED, // TODO: remove argument
jobjectArray code_paths) {
std::vector<std::string> code_paths_vec;
int code_paths_length = env->GetArrayLength(code_paths);
@@ -577,13 +577,11 @@
env->ReleaseStringUTFChars(code_path, raw_code_path);
}
- const char* raw_app_dir = env->GetStringUTFChars(app_dir, nullptr);
- const char* raw_pkg_name = env->GetStringUTFChars(pkg_name, nullptr);
- std::string profile_file = StringPrintf("%s/code_cache/%s.prof", raw_app_dir, raw_pkg_name);
- env->ReleaseStringUTFChars(pkg_name, raw_pkg_name);
- env->ReleaseStringUTFChars(app_dir, raw_app_dir);
+ const char* raw_profile_file = env->GetStringUTFChars(profile_file, nullptr);
+ std::string profile_file_str(raw_profile_file);
+ env->ReleaseStringUTFChars(profile_file, raw_profile_file);
- Runtime::Current()->RegisterAppInfo(code_paths_vec, profile_file);
+ Runtime::Current()->RegisterAppInfo(code_paths_vec, profile_file_str);
}
static jboolean VMRuntime_isBootClassPathOnDisk(JNIEnv* env, jclass, jstring java_instruction_set) {
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 1977481..e89c74d 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -288,13 +288,6 @@
GetPublicFieldRecursive(soa.Self(), DecodeClass(soa, javaThis), name_string));
}
-static jobject Class_getDeclaredFieldInternal(JNIEnv* env, jobject javaThis, jstring name) {
- ScopedFastNativeObjectAccess soa(env);
- auto* name_string = soa.Decode<mirror::String*>(name);
- return soa.AddLocalReference<jobject>(
- GetDeclaredField(soa.Self(), DecodeClass(soa, javaThis), name_string));
-}
-
static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring name) {
ScopedFastNativeObjectAccess soa(env);
auto* name_string = soa.Decode<mirror::String*>(name);
@@ -306,6 +299,12 @@
mirror::Field* result = GetDeclaredField(soa.Self(), klass, name_string);
if (result == nullptr) {
std::string name_str = name_string->ToModifiedUtf8();
+ if (name_str == "value" && klass->IsStringClass()) {
+ // We log the error for this specific case, as the user might just swallow the exception.
+ // This helps diagnose crashes when applications rely on the String#value field being
+ // there.
+ LOG(ERROR) << "The String#value field is not present on Android versions >= 6.0";
+ }
// We may have a pending exception if we failed to resolve.
if (!soa.Self()->IsExceptionPending()) {
ThrowNoSuchFieldException(DecodeClass(soa, javaThis), name_str.c_str());
@@ -723,7 +722,6 @@
NATIVE_METHOD(Class, getDeclaredConstructorsInternal, "!(Z)[Ljava/lang/reflect/Constructor;"),
NATIVE_METHOD(Class, getDeclaredField, "!(Ljava/lang/String;)Ljava/lang/reflect/Field;"),
NATIVE_METHOD(Class, getPublicFieldRecursive, "!(Ljava/lang/String;)Ljava/lang/reflect/Field;"),
- NATIVE_METHOD(Class, getDeclaredFieldInternal, "!(Ljava/lang/String;)Ljava/lang/reflect/Field;"),
NATIVE_METHOD(Class, getDeclaredFields, "!()[Ljava/lang/reflect/Field;"),
NATIVE_METHOD(Class, getDeclaredFieldsUnchecked, "!(Z)[Ljava/lang/reflect/Field;"),
NATIVE_METHOD(Class, getDeclaredMethodInternal,
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 8543ff4..d6b0868 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -952,7 +952,6 @@
Runtime* runtime = Runtime::Current();
std::vector<gc::space::ImageSpace*> image_spaces = runtime->GetHeap()->GetBootImageSpaces();
if (!image_spaces.empty()) {
- // TODO: Better support multi-images? b/26317072
cached_image_info_.location = image_spaces[0]->GetImageLocation();
if (isa_ == kRuntimeISA) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index cb40e9e..87681cf 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1699,13 +1699,29 @@
void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths,
const std::string& profile_output_filename) {
- VLOG(profiler) << "Register app with " << profile_output_filename_
- << " " << Join(code_paths, ':');
- DCHECK(!profile_output_filename.empty());
- profile_output_filename_ = profile_output_filename;
- if (jit_.get() != nullptr && !profile_output_filename.empty() && !code_paths.empty()) {
- jit_->StartProfileSaver(profile_output_filename, code_paths);
+ if (jit_.get() == nullptr) {
+ // We are not JITing. Nothing to do.
+ return;
}
+
+ VLOG(profiler) << "Register app with " << profile_output_filename
+ << " " << Join(code_paths, ':');
+
+ if (profile_output_filename.empty()) {
+ LOG(WARNING) << "JIT profile information will not be recorded: profile filename is empty.";
+ return;
+ }
+ if (!FileExists(profile_output_filename)) {
+ LOG(WARNING) << "JIT profile information will not be recorded: profile file does not exits.";
+ return;
+ }
+ if (code_paths.empty()) {
+ LOG(WARNING) << "JIT profile information will not be recorded: code paths is empty.";
+ return;
+ }
+
+ profile_output_filename_ = profile_output_filename;
+ jit_->StartProfileSaver(profile_output_filename, code_paths);
}
// Transaction support.
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index f9eb935..ae18819 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -97,8 +97,8 @@
ATRACE_END();
// TODO: there's an unaddressed race here where a thread may attach during shutdown, see
// Thread::Init.
- ATRACE_BEGIN("SuspendAllDaemonThreads");
- SuspendAllDaemonThreads();
+ ATRACE_BEGIN("SuspendAllDaemonThreadsForShutdown");
+ SuspendAllDaemonThreadsForShutdown();
ATRACE_END();
ATRACE_END();
}
@@ -1142,7 +1142,7 @@
}
}
-void ThreadList::SuspendAllDaemonThreads() {
+void ThreadList::SuspendAllDaemonThreadsForShutdown() {
Thread* self = Thread::Current();
MutexLock mu(self, *Locks::thread_list_lock_);
{ // Tell all the daemons it's time to suspend.
@@ -1154,11 +1154,14 @@
if (thread != self) {
thread->ModifySuspendCount(self, +1, nullptr, false);
}
+ // We are shutting down the runtime, set the JNI functions of all the JNIEnvs to be
+ // the sleep forever one.
+ thread->GetJniEnv()->SetFunctionsToRuntimeShutdownFunctions();
}
}
// Give the threads a chance to suspend, complaining if they're slow.
bool have_complained = false;
- static constexpr size_t kTimeoutMicroseconds = 200 * 1000;
+ static constexpr size_t kTimeoutMicroseconds = 2000 * 1000;
static constexpr size_t kSleepMicroseconds = 1000;
for (size_t i = 0; i < kTimeoutMicroseconds / kSleepMicroseconds; ++i) {
bool all_suspended = true;
@@ -1176,7 +1179,7 @@
}
usleep(kSleepMicroseconds);
}
- LOG(ERROR) << "timed out suspending all daemon threads";
+ LOG(WARNING) << "timed out suspending all daemon threads";
}
void ThreadList::Register(Thread* self) {
DCHECK_EQ(self, Thread::Current());
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 07ea10d..2e73f6a 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -164,7 +164,7 @@
void DumpUnattachedThreads(std::ostream& os)
REQUIRES(!Locks::thread_list_lock_);
- void SuspendAllDaemonThreads()
+ void SuspendAllDaemonThreadsForShutdown()
REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
void WaitForOtherNonDaemonThreadsToExit()
REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
diff --git a/runtime/utils.cc b/runtime/utils.cc
index ff6b4c0..8e9f12b 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1446,6 +1446,11 @@
return true;
}
+bool FileExists(const std::string& filename) {
+ struct stat buffer;
+ return stat(filename.c_str(), &buffer) == 0;
+}
+
std::string PrettyDescriptor(Primitive::Type type) {
return PrettyDescriptor(Primitive::Descriptor(type));
}
@@ -1866,4 +1871,10 @@
return rc == 0 ? stat_buf.st_size : -1;
}
+void SleepForever() {
+ while (true) {
+ usleep(1000000);
+ }
+}
+
} // namespace art
diff --git a/runtime/utils.h b/runtime/utils.h
index a07e74c..153749e 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -99,6 +99,18 @@
return (ch < ' ' || ch > '~');
}
+template <typename T> T SafeAbs(T value) {
+ // std::abs has undefined behavior on min limits.
+ DCHECK_NE(value, std::numeric_limits<T>::min());
+ return std::abs(value);
+}
+
+template <typename T> T AbsOrMin(T value) {
+ return (value == std::numeric_limits<T>::min())
+ ? value
+ : std::abs(value);
+}
+
std::string PrintableChar(uint16_t ch);
// Returns an ASCII string corresponding to the given UTF-8 string.
@@ -276,6 +288,9 @@
// Wrapper on fork/execv to run a command in a subprocess.
bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg);
+// Returns true if the file exists.
+bool FileExists(const std::string& filename);
+
class VoidFunctor {
public:
template <typename A>
@@ -370,6 +385,9 @@
// Return the file size in bytes or -1 if the file does not exists.
int64_t GetFileSizeBytes(const std::string& filename);
+// Sleep forever and never come back.
+NO_RETURN void SleepForever();
+
} // namespace art
#endif // ART_RUNTIME_UTILS_H_
diff --git a/test/143-string-value/check b/test/143-string-value/check
new file mode 100755
index 0000000..cdf7b78
--- /dev/null
+++ b/test/143-string-value/check
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Strip run-specific numbers (pid and line number)
+sed -e 's/^art E[ ]\+[0-9]\+[ ]\+[0-9]\+ art\/runtime\/native\/java_lang_Class.cc:[0-9]\+\] //' "$2" > "$2.tmp"
+
+diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
diff --git a/test/143-string-value/expected.txt b/test/143-string-value/expected.txt
new file mode 100644
index 0000000..06cdb89
--- /dev/null
+++ b/test/143-string-value/expected.txt
@@ -0,0 +1 @@
+The String#value field is not present on Android versions >= 6.0
diff --git a/test/143-string-value/info.txt b/test/143-string-value/info.txt
new file mode 100644
index 0000000..61ec816
--- /dev/null
+++ b/test/143-string-value/info.txt
@@ -0,0 +1,2 @@
+Test to ensure we emit an error message when being asked
+for String#value.
diff --git a/test/143-string-value/src/Main.java b/test/143-string-value/src/Main.java
new file mode 100644
index 0000000..e970692
--- /dev/null
+++ b/test/143-string-value/src/Main.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ try {
+ String.class.getDeclaredField("value");
+ throw new Error("Expected to fail");
+ } catch (ReflectiveOperationException e) {
+ // Ignore...
+ }
+ }
+}
diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java
index 43bc9d0..0e07f47 100644
--- a/test/442-checker-constant-folding/src/Main.java
+++ b/test/442-checker-constant-folding/src/Main.java
@@ -120,9 +120,10 @@
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
/// CHECK-DAG: <<Const6:i\d+>> IntConstant 6
+ /// CHECK-DAG: <<Const11:i\d+>> IntConstant 11
/// CHECK-DAG: <<Add1:i\d+>> Add [<<Const1>>,<<Const2>>]
- /// CHECK-DAG: <<Add2:i\d+>> Add [<<Const5>>,<<Const6>>]
- /// CHECK-DAG: <<Add3:i\d+>> Add [<<Add1>>,<<Add2>>]
+ /// CHECK-DAG: Add [<<Const5>>,<<Const6>>]
+ /// CHECK-DAG: <<Add3:i\d+>> Add [<<Add1>>,<<Const11>>]
/// CHECK-DAG: Return [<<Add3>>]
/// CHECK-START: int Main.IntAddition2() constant_folding (after)
@@ -522,7 +523,7 @@
/// CHECK-DAG: <<Const10L:j\d+>> LongConstant 10
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK-DAG: <<TypeConv:j\d+>> TypeConversion [<<Const3>>]
- /// CHECK-DAG: <<And:j\d+>> And [<<Const10L>>,<<TypeConv>>]
+ /// CHECK-DAG: <<And:j\d+>> And [<<TypeConv>>,<<Const10L>>]
/// CHECK-DAG: Return [<<And>>]
/// CHECK-START: long Main.AndLongInt() constant_folding (after)
@@ -567,7 +568,7 @@
/// CHECK-DAG: <<Const10L:j\d+>> LongConstant 10
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK-DAG: <<TypeConv:j\d+>> TypeConversion [<<Const3>>]
- /// CHECK-DAG: <<Or:j\d+>> Or [<<Const10L>>,<<TypeConv>>]
+ /// CHECK-DAG: <<Or:j\d+>> Or [<<TypeConv>>,<<Const10L>>]
/// CHECK-DAG: Return [<<Or>>]
/// CHECK-START: long Main.OrLongInt() constant_folding (after)
@@ -612,7 +613,7 @@
/// CHECK-DAG: <<Const10L:j\d+>> LongConstant 10
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK-DAG: <<TypeConv:j\d+>> TypeConversion [<<Const3>>]
- /// CHECK-DAG: <<Xor:j\d+>> Xor [<<Const10L>>,<<TypeConv>>]
+ /// CHECK-DAG: <<Xor:j\d+>> Xor [<<TypeConv>>,<<Const10L>>]
/// CHECK-DAG: Return [<<Xor>>]
/// CHECK-START: long Main.XorLongInt() constant_folding (after)
@@ -749,7 +750,7 @@
/// CHECK-START: long Main.Mul0(long) constant_folding (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const0:j\d+>> LongConstant 0
- /// CHECK-DAG: <<Mul:j\d+>> Mul [<<Arg>>,<<Const0>>]
+ /// CHECK-DAG: <<Mul:j\d+>> Mul [<<Const0>>,<<Arg>>]
/// CHECK-DAG: Return [<<Mul>>]
/// CHECK-START: long Main.Mul0(long) constant_folding (after)
diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java
index 6151fc1..0fd7801 100644
--- a/test/458-checker-instruction-simplification/src/Main.java
+++ b/test/458-checker-instruction-simplification/src/Main.java
@@ -288,7 +288,7 @@
/// CHECK-START: long Main.Mul1(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const1:j\d+>> LongConstant 1
- /// CHECK-DAG: <<Mul:j\d+>> Mul [<<Arg>>,<<Const1>>]
+ /// CHECK-DAG: <<Mul:j\d+>> Mul [<<Const1>>,<<Arg>>]
/// CHECK-DAG: Return [<<Mul>>]
/// CHECK-START: long Main.Mul1(long) instruction_simplifier (after)
@@ -323,7 +323,7 @@
/// CHECK-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const128:j\d+>> LongConstant 128
- /// CHECK-DAG: <<Mul:j\d+>> Mul [<<Arg>>,<<Const128>>]
+ /// CHECK-DAG: <<Mul:j\d+>> Mul [<<Const128>>,<<Arg>>]
/// CHECK-DAG: Return [<<Mul>>]
/// CHECK-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (after)
@@ -705,7 +705,7 @@
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg>>]
/// CHECK-DAG: <<Neg2:i\d+>> Neg [<<Neg1>>]
- /// CHECK-DAG: <<Add:i\d+>> Add [<<Neg1>>,<<Neg2>>]
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Neg2>>,<<Neg1>>]
/// CHECK-DAG: Return [<<Add>>]
/// CHECK-START: int Main.NegNeg2(int) instruction_simplifier (after)
@@ -841,13 +841,13 @@
/// CHECK-DAG: <<ConstF1:i\d+>> IntConstant -1
/// CHECK-DAG: <<Xor1:i\d+>> Xor [<<Arg>>,<<ConstF1>>]
/// CHECK-DAG: <<Xor2:i\d+>> Xor [<<Xor1>>,<<ConstF1>>]
- /// CHECK-DAG: <<Add:i\d+>> Add [<<Xor1>>,<<Xor2>>]
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Xor2>>,<<Xor1>>]
/// CHECK-DAG: Return [<<Add>>]
/// CHECK-START: int Main.NotNot2(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Not:i\d+>> Not [<<Arg>>]
- /// CHECK-DAG: <<Add:i\d+>> Add [<<Not>>,<<Arg>>]
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Arg>>,<<Not>>]
/// CHECK-DAG: Return [<<Add>>]
/// CHECK-START: int Main.NotNot2(int) instruction_simplifier (after)
@@ -1005,7 +1005,7 @@
/// CHECK-START: int Main.EqualFalseLhs(boolean) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
- /// CHECK-DAG: <<Cond:z\d+>> Equal [<<Const0>>,<<Arg>>]
+ /// CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Const0>>]
/// CHECK-DAG: If [<<Cond>>]
/// CHECK-START: int Main.EqualFalseLhs(boolean) instruction_simplifier (after)
@@ -1064,7 +1064,7 @@
/// CHECK-START: int Main.NotEqualFalseLhs(boolean) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
- /// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Const0>>,<<Arg>>]
+ /// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Arg>>,<<Const0>>]
/// CHECK-DAG: If [<<Cond>>]
/// CHECK-START: int Main.NotEqualFalseLhs(boolean) instruction_simplifier (after)
@@ -1234,7 +1234,7 @@
/// CHECK-START: long Main.mulPow2Minus1(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const31:j\d+>> LongConstant 31
- /// CHECK: Mul [<<Arg>>,<<Const31>>]
+ /// CHECK: Mul [<<Const31>>,<<Arg>>]
/// CHECK-START: long Main.mulPow2Minus1(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
diff --git a/test/482-checker-loop-back-edge-use/src/Main.java b/test/482-checker-loop-back-edge-use/src/Main.java
index 6b4da9d..d0b33b9 100644
--- a/test/482-checker-loop-back-edge-use/src/Main.java
+++ b/test/482-checker-loop-back-edge-use/src/Main.java
@@ -163,8 +163,8 @@
/// CHECK: <<Arg:z\d+>> StaticFieldGet liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopUse:\d+>>)} uses:[<<ArgUse:\d+>>,<<ArgLoopUse>>]
/// CHECK: If [<<Arg>>] liveness:<<IfLiv:\d+>>
/// CHECK: Goto liveness:<<GotoLiv1:\d+>>
- /// CHECK: Goto liveness:<<GotoLiv2:\d+>>
/// CHECK: Exit
+ /// CHECK: Goto liveness:<<GotoLiv2:\d+>>
/// CHECK-EVAL: <<IfLiv>> + 1 == <<ArgUse>>
/// CHECK-EVAL: <<GotoLiv1>> < <<GotoLiv2>>
/// CHECK-EVAL: <<GotoLiv1>> + 2 == <<ArgLoopUse>>
diff --git a/test/529-checker-unresolved/expected.txt b/test/529-checker-unresolved/expected.txt
index 1e7dbfe..1590a2a 100644
--- a/test/529-checker-unresolved/expected.txt
+++ b/test/529-checker-unresolved/expected.txt
@@ -5,3 +5,6 @@
UnresolvedClass.superMethod()
instanceof ok
checkcast ok
+UnresolvedClass.directCall()
+UnresolvedClass.directCall()
+UnresolvedClass.directCall()
diff --git a/test/529-checker-unresolved/src/Main.java b/test/529-checker-unresolved/src/Main.java
index 5219c04..872fa6d 100644
--- a/test/529-checker-unresolved/src/Main.java
+++ b/test/529-checker-unresolved/src/Main.java
@@ -138,6 +138,27 @@
callUnresolvedInstanceFieldAccess(c);
testInstanceOf(m);
testCheckCast(m);
+ testLicm(2);
+ }
+
+ /// CHECK-START: void Main.testLicm(int) licm (before)
+ /// CHECK: <<Class:l\d+>> LoadClass loop:B2
+ /// CHECK-NEXT: <<Clinit:l\d+>> ClinitCheck [<<Class>>] loop:B2
+ /// CHECK-NEXT: <<New:l\d+>> NewInstance [<<Clinit>>,<<Method:[i|j]\d+>>] loop:B2
+ /// CHECK-NEXT: InvokeUnresolved [<<New>>] loop:B2
+
+ /// CHECK-START: void Main.testLicm(int) licm (after)
+ /// CHECK: <<Class:l\d+>> LoadClass loop:none
+ /// CHECK-NEXT: <<Clinit:l\d+>> ClinitCheck [<<Class>>] loop:none
+ /// CHECK: <<New:l\d+>> NewInstance [<<Clinit>>,<<Method:[i|j]\d+>>] loop:B2
+ /// CHECK-NEXT: InvokeUnresolved [<<New>>] loop:B2
+ static public void testLicm(int count) {
+ // Test to make sure we keep the initialization check after loading an unresolved class.
+ UnresolvedClass c;
+ int i = 0;
+ do {
+ c = new UnresolvedClass();
+ } while (i++ != count);
}
public static void expectEquals(byte expected, byte result) {
diff --git a/test/561-divrem/expected.txt b/test/561-divrem/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/561-divrem/expected.txt
diff --git a/test/561-divrem/info.txt b/test/561-divrem/info.txt
new file mode 100644
index 0000000..71c9601
--- /dev/null
+++ b/test/561-divrem/info.txt
@@ -0,0 +1,2 @@
+Regression test for div/rem taking Integer.MIN_VALUE and
+Long.MIN_VALUE.
diff --git a/test/561-divrem/src/Main.java b/test/561-divrem/src/Main.java
new file mode 100644
index 0000000..082783d
--- /dev/null
+++ b/test/561-divrem/src/Main.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void assertEquals(long expected, long actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void main(String[] args) {
+ assertEquals(0, $noinline$divInt(1));
+ assertEquals(1, $noinline$remInt(1));
+
+ assertEquals(0, $noinline$divInt(-1));
+ assertEquals(-1, $noinline$remInt(-1));
+
+ assertEquals(0, $noinline$divInt(0));
+ assertEquals(0, $noinline$remInt(0));
+
+ assertEquals(1, $noinline$divInt(Integer.MIN_VALUE));
+ assertEquals(0, $noinline$remInt(Integer.MIN_VALUE));
+
+ assertEquals(0, $noinline$divInt(Integer.MAX_VALUE));
+ assertEquals(Integer.MAX_VALUE, $noinline$remInt(Integer.MAX_VALUE));
+
+ assertEquals(0, $noinline$divInt(Integer.MAX_VALUE - 1));
+ assertEquals(Integer.MAX_VALUE - 1, $noinline$remInt(Integer.MAX_VALUE - 1));
+
+ assertEquals(0, $noinline$divInt(Integer.MIN_VALUE + 1));
+ assertEquals(Integer.MIN_VALUE + 1, $noinline$remInt(Integer.MIN_VALUE + 1));
+
+ assertEquals(0L, $noinline$divLong(1L));
+ assertEquals(1L, $noinline$remLong(1L));
+
+ assertEquals(0L, $noinline$divLong(-1L));
+ assertEquals(-1L, $noinline$remLong(-1L));
+
+ assertEquals(0L, $noinline$divLong(0L));
+ assertEquals(0L, $noinline$remLong(0L));
+
+ assertEquals(1L, $noinline$divLong(Long.MIN_VALUE));
+ assertEquals(0L, $noinline$remLong(Long.MIN_VALUE));
+
+ assertEquals(0L, $noinline$divLong(Long.MAX_VALUE));
+ assertEquals(Long.MAX_VALUE, $noinline$remLong(Long.MAX_VALUE));
+
+ assertEquals(0L, $noinline$divLong(Long.MAX_VALUE - 1));
+ assertEquals(Long.MAX_VALUE - 1, $noinline$remLong(Long.MAX_VALUE - 1));
+
+ assertEquals(0L, $noinline$divLong(Integer.MIN_VALUE + 1));
+ assertEquals(Long.MIN_VALUE + 1, $noinline$remLong(Long.MIN_VALUE + 1));
+ }
+
+ public static int $noinline$divInt(int value) {
+ if (doThrow) {
+ throw new Error("");
+ }
+ return value / Integer.MIN_VALUE;
+ }
+
+ public static int $noinline$remInt(int value) {
+ if (doThrow) {
+ throw new Error("");
+ }
+ return value % Integer.MIN_VALUE;
+ }
+
+ public static long $noinline$divLong(long value) {
+ if (doThrow) {
+ throw new Error("");
+ }
+ return value / Long.MIN_VALUE;
+ }
+
+ public static long $noinline$remLong(long value) {
+ if (doThrow) {
+ throw new Error("");
+ }
+ return value % Long.MIN_VALUE;
+ }
+
+ static boolean doThrow = false;
+}
diff --git a/test/701-easy-div-rem/genMain.py b/test/701-easy-div-rem/genMain.py
index 75eee17..b6c769f 100644
--- a/test/701-easy-div-rem/genMain.py
+++ b/test/701-easy-div-rem/genMain.py
@@ -13,25 +13,27 @@
# limitations under the License.
upper_bound_int_pow2 = 31
+upper_bound_int_pow2_neg = 32
upper_bound_long_pow2 = 63
+upper_bound_long_pow2_neg = 64
upper_bound_constant = 100
all_tests = [
({'@INT@': 'int', '@SUFFIX@':''},
[('CheckDiv', 'idiv_by_pow2_', [2**i for i in range(upper_bound_int_pow2)]),
- ('CheckDiv', 'idiv_by_pow2_neg_', [-2**i for i in range(upper_bound_int_pow2)]),
+ ('CheckDiv', 'idiv_by_pow2_neg_', [-2**i for i in range(upper_bound_int_pow2_neg)]),
('CheckDiv', 'idiv_by_constant_', [i for i in range(1, upper_bound_constant)]),
('CheckDiv', 'idiv_by_constant_neg_', [-i for i in range(1, upper_bound_constant)]),
('CheckRem', 'irem_by_pow2_', [2**i for i in range(upper_bound_int_pow2)]),
- ('CheckRem', 'irem_by_pow2_neg_', [-2**i for i in range(upper_bound_int_pow2)]),
+ ('CheckRem', 'irem_by_pow2_neg_', [-2**i for i in range(upper_bound_int_pow2_neg)]),
('CheckRem', 'irem_by_constant_', [i for i in range(1, upper_bound_constant)]),
('CheckRem', 'irem_by_constant_neg_', [-i for i in range(1, upper_bound_constant)])]),
({'@INT@': 'long', '@SUFFIX@': 'l'},
[('CheckDiv', 'ldiv_by_pow2_', [2**i for i in range(upper_bound_long_pow2)]),
- ('CheckDiv', 'ldiv_by_pow2_neg_', [-2**i for i in range(upper_bound_long_pow2)]),
+ ('CheckDiv', 'ldiv_by_pow2_neg_', [-2**i for i in range(upper_bound_long_pow2_neg)]),
('CheckDiv', 'ldiv_by_constant_', [i for i in range(1, upper_bound_constant)]),
('CheckDiv', 'ldiv_by_constant_neg_', [-i for i in range(1, upper_bound_constant)]),
('CheckRem', 'lrem_by_pow2_', [2**i for i in range(upper_bound_long_pow2)]),
- ('CheckRem', 'lrem_by_pow2_neg_', [-2**i for i in range(upper_bound_long_pow2)]),
+ ('CheckRem', 'lrem_by_pow2_neg_', [-2**i for i in range(upper_bound_long_pow2_neg)]),
('CheckRem', 'lrem_by_constant_', [i for i in range(1, upper_bound_constant)]),
('CheckRem', 'lrem_by_constant_neg_', [-i for i in range(1, upper_bound_constant)])])
]
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 3ce9d09..53d0f10 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -267,6 +267,26 @@
TEST_ART_BROKEN_PREBUILD_RUN_TESTS :=
+# b/26483935
+TEST_ART_BROKEN_HOST_RUN_TESTS := \
+ 132-daemon-locks-shutdown \
+
+ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,host,$(RUN_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_HOST_RUN_TESTS), $(ALL_ADDRESS_SIZES))
+
+TEST_ART_BROKEN_HOST_RUN_TESTS :=
+
+# 143-string-value tests for a LOG(E) tag, which is only supported on host.
+TEST_ART_BROKEN_TARGET_RUN_TESTS := \
+ 143-string-value \
+
+ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_TARGET_RUN_TESTS), $(ALL_ADDRESS_SIZES))
+
+TEST_ART_BROKEN_TARGET_RUN_TESTS :=
+
# 554-jit-profile-file is disabled because it needs a primary oat file to know what it should save.
TEST_ART_BROKEN_NO_PREBUILD_TESTS := \
117-nopatchoat \
@@ -588,8 +608,7 @@
TEST_ART_BROKEN_DEFAULT_HEAP_POISONING_RUN_TESTS :=
TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS :=
-
-# Tests broken by multi-image. b/26317072
+# Tests broken by multi-image.
TEST_ART_BROKEN_MULTI_IMAGE_RUN_TESTS := \
476-checker-ctor-memory-barrier \
530-checker-lse
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index dacb7b9..e004b6c 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -361,9 +361,7 @@
dex2oat_cmdline="true"
mkdir_cmdline="mkdir -p ${DEX_LOCATION}/dalvik-cache/$ISA"
-# TODO: allow app-image to work with multi-image. b/26317072
-app_image=""
-# app_image="--app-image-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.art | cut -d/ -f 2- | sed "s:/:@:g")"
+app_image="--app-image-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.art | cut -d/ -f 2- | sed "s:/:@:g")"
if [ "$PREBUILD" = "y" ]; then
dex2oat_cmdline="$INVOKE_WITH $ANDROID_ROOT/bin/dex2oatd \
diff --git a/test/run-test b/test/run-test
index ec34e09..4f111d2 100755
--- a/test/run-test
+++ b/test/run-test
@@ -683,11 +683,6 @@
# Tests named '<number>-checker-*' will also have their CFGs verified with
# Checker when compiled with Optimizing on host.
if [[ "$TEST_NAME" =~ ^[0-9]+-checker- ]]; then
- # Build Checker DEX files without dx's optimizations so the input to dex2oat
- # better resembles the Java source. We always build the DEX the same way, even
- # if Checker is not invoked and the test only runs the program.
- build_args="${build_args} --dx-option --no-optimize"
-
# Jack does not necessarily generate the same DEX output than dx. Because these tests depend
# on a particular DEX output, keep building them with dx for now (b/19467889).
USE_JACK="false"
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 02787fb..9e02ce2 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -48,7 +48,7 @@
if [[ $mode == "host" ]]; then
make_command="make $j_arg $showcommands build-art-host-tests $common_targets ${out_dir}/host/linux-x86/lib/libjavacoretests.so ${out_dir}/host/linux-x86/lib64/libjavacoretests.so"
elif [[ $mode == "target" ]]; then
- make_command="make $j_arg $showcommands build-art-target-tests $common_targets libjavacrypto libjavacoretests linker toybox toolbox sh ${out_dir}/host/linux-x86/bin/adb"
+ make_command="make $j_arg $showcommands build-art-target-tests $common_targets libjavacrypto libjavacoretests linker toybox toolbox sh ${out_dir}/host/linux-x86/bin/adb libstdc++"
fi
echo "Executing $make_command"
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index d11e015..6e1ec49 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -173,7 +173,8 @@
{
description: "Assertion failing on the concurrent collector configuration.",
result: EXEC_FAILED,
- names: ["jsr166.LinkedTransferQueueTest#testTransfer2"],
+ names: ["jsr166.LinkedTransferQueueTest#testTransfer2",
+ "jsr166.LinkedTransferQueueTest#testWaitingConsumer"],
bug: 25883050
},
{