Merge "Fix deadlock with the JIT code cache."
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index fb116bb..d055b37 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -697,6 +697,9 @@
}
CompiledMethod* CompilerDriver::CompileArtMethod(Thread* self, ArtMethod* method) {
+ DCHECK_EQ(method,
+ method->GetInterfaceMethodIfProxy(
+ Runtime::Current()->GetClassLinker()->GetImagePointerSize()));
const uint32_t method_idx = method->GetDexMethodIndex();
const uint32_t access_flags = method->GetAccessFlags();
const InvokeType invoke_type = method->GetInvokeType();
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 21d582e..fd6cd82 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -97,8 +97,10 @@
ASSERT_TRUE(dup_oat.get() != nullptr);
{
- bool success_image =
- writer->Write(image_file.GetFilename(), dup_oat->GetPath(), dup_oat->GetPath());
+ bool success_image = writer->Write(kInvalidImageFd,
+ image_file.GetFilename(),
+ dup_oat->GetPath(),
+ dup_oat->GetPath());
ASSERT_TRUE(success_image);
bool success_fixup = ElfWriter::Fixup(dup_oat.get(), writer->GetOatDataBegin());
ASSERT_TRUE(success_fixup);
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 0e5a97f..af2a4f9 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -122,7 +122,8 @@
return true;
}
-bool ImageWriter::Write(const std::string& image_filename,
+bool ImageWriter::Write(int image_fd,
+ const std::string& image_filename,
const std::string& oat_filename,
const std::string& oat_location) {
CHECK(!image_filename.empty());
@@ -178,10 +179,13 @@
LOG(ERROR) << "Failed to flush and close oat file " << oat_filename << " for " << oat_location;
return false;
}
-
- std::unique_ptr<File> image_file(OS::CreateEmptyFile(image_filename.c_str()));
- ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
- if (image_file.get() == nullptr) {
+ std::unique_ptr<File> image_file;
+ if (image_fd != kInvalidImageFd) {
+ image_file.reset(new File(image_fd, image_filename, unix_file::kCheckSafeUsage));
+ } else {
+ image_file.reset(OS::CreateEmptyFile(image_filename.c_str()));
+ }
+ if (image_file == nullptr) {
LOG(ERROR) << "Failed to open image file " << image_filename;
return false;
}
@@ -192,6 +196,7 @@
}
// Write out the image + fields + methods.
+ ImageHeader* const image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
const auto write_count = image_header->GetImageSize();
if (!image_file->WriteFully(image_->Begin(), write_count)) {
PLOG(ERROR) << "Failed to write image file " << image_filename;
@@ -200,7 +205,8 @@
}
// Write out the image bitmap at the page aligned start of the image end.
- const ImageSection& bitmap_section = image_header->GetImageSection(ImageHeader::kSectionImageBitmap);
+ const ImageSection& bitmap_section = image_header->GetImageSection(
+ ImageHeader::kSectionImageBitmap);
CHECK_ALIGNED(bitmap_section.Offset(), kPageSize);
if (!image_file->Write(reinterpret_cast<char*>(image_bitmap_->Begin()),
bitmap_section.Size(), bitmap_section.Offset())) {
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index e235bc4..7a2febc 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -41,6 +41,8 @@
namespace art {
+static constexpr int kInvalidImageFd = -1;
+
// Write a Space built during compilation for use during execution.
class ImageWriter FINAL {
public:
@@ -89,7 +91,11 @@
uint8_t* GetOatFileBegin() const;
- bool Write(const std::string& image_filename, const std::string& oat_filename,
+ // If image_fd is not kInvalidImageFd, then we use that for the file. Otherwise we open
+ // image_filename.
+ bool Write(int image_fd,
+ const std::string& image_filename,
+ const std::string& oat_filename,
const std::string& oat_location)
REQUIRES(!Locks::mutator_lock_);
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index c1b87c9..d520208 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -192,7 +192,10 @@
CompiledMethod* compiled_method = nullptr;
{
TimingLogger::ScopedTiming t2("Compiling", &logger);
- compiled_method = compiler_driver_->CompileArtMethod(self, method);
+ // If we get a request to compile a proxy method, we pass the actual Java method
+ // of that proxy method, as the compiler does not expect a proxy method.
+ ArtMethod* method_to_compile = method->GetInterfaceMethodIfProxy(sizeof(void*));
+ compiled_method = compiler_driver_->CompileArtMethod(self, method_to_compile);
}
// Trim maps to reduce memory usage.
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 0aaa6b3..353881e 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -494,6 +494,26 @@
<< " it is in a different dex file and requires access to the dex cache";
return false;
}
+
+ if (current->IsNewInstance() &&
+ (current->AsNewInstance()->GetEntrypoint() == kQuickAllocObjectWithAccessCheck)) {
+ // Allocation entrypoint does not handle inlined frames.
+ return false;
+ }
+
+ if (current->IsNewArray() &&
+ (current->AsNewArray()->GetEntrypoint() == kQuickAllocArrayWithAccessCheck)) {
+ // Allocation entrypoint does not handle inlined frames.
+ return false;
+ }
+
+ if (current->IsUnresolvedStaticFieldGet() ||
+ current->IsUnresolvedInstanceFieldGet() ||
+ current->IsUnresolvedStaticFieldSet() ||
+ current->IsUnresolvedInstanceFieldSet()) {
+ // Entrypoint for unresolved fields does not handle inlined frames.
+ return false;
+ }
}
}
number_of_inlined_instructions_ += number_of_instructions;
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 90f28e5..6fbb682 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -59,7 +59,7 @@
(use->IsInstanceFieldSet() && (reference_ == use->InputAt(1))) ||
(use->IsUnresolvedInstanceFieldSet() && (reference_ == use->InputAt(1))) ||
(use->IsStaticFieldSet() && (reference_ == use->InputAt(1))) ||
- (use->IsUnresolvedStaticFieldSet() && (reference_ == use->InputAt(1))) ||
+ (use->IsUnresolvedStaticFieldSet() && (reference_ == use->InputAt(0))) ||
(use->IsArraySet() && (reference_ == use->InputAt(2)))) {
// reference_ is merged to a phi, passed to a callee, or stored to heap.
// reference_ isn't the only name that can refer to its value anymore.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 7df5866..0f2c1cf 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -4750,6 +4750,9 @@
return generate_clinit_check_;
}
void SetMustGenerateClinitCheck(bool generate_clinit_check) {
+ // The entrypoint the code generator is going to call does not do
+ // clinit of the class.
+ DCHECK(!NeedsAccessCheck());
generate_clinit_check_ = generate_clinit_check;
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 8773169..2653807 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -126,11 +126,12 @@
// However, we prefer to drop this when we saw --zip-fd.
if (saw_zip_fd) {
- // Drop anything --zip-X, --dex-X, --oat-X, --swap-X.
+ // Drop anything --zip-X, --dex-X, --oat-X, --swap-X, or --app-image-X
if (StartsWith(original_argv[i], "--zip-") ||
StartsWith(original_argv[i], "--dex-") ||
StartsWith(original_argv[i], "--oat-") ||
- StartsWith(original_argv[i], "--swap-")) {
+ StartsWith(original_argv[i], "--swap-") ||
+ StartsWith(original_argv[i], "--app-image-")) {
continue;
}
}
@@ -336,6 +337,12 @@
UsageError(" --swap-fd=<file-descriptor>: specifies a file to use for swap (by descriptor).");
UsageError(" Example: --swap-fd=10");
UsageError("");
+ UsageError(" --app-image-fd=<file-descriptor>: specify output file descriptor for app image.");
+ UsageError(" Example: --app-image-fd=10");
+ UsageError("");
+ UsageError(" --app-image-file=<file-name>: specify a file name for app image.");
+ UsageError(" Example: --app-image-file=/data/dalvik-cache/system@app@Calculator.apk.art");
+ UsageError("");
std::cerr << "See log for usage error information\n";
exit(EXIT_FAILURE);
}
@@ -484,7 +491,8 @@
compiled_classes_filename_(nullptr),
compiled_methods_zip_filename_(nullptr),
compiled_methods_filename_(nullptr),
- image_(false),
+ app_image_(false),
+ boot_image_(false),
is_host_(false),
driver_(nullptr),
dump_stats_(false),
@@ -493,6 +501,7 @@
dump_slow_timing_(kIsDebugBuild),
dump_cfg_append_(false),
swap_fd_(-1),
+ app_image_fd_(kInvalidImageFd),
timings_(timings) {}
~Dex2Oat() {
@@ -608,13 +617,15 @@
}
}
- void ParseSwapFd(const StringPiece& option) {
- ParseUintOption(option, "--swap-fd", &swap_fd_, Usage);
- }
-
void ProcessOptions(ParserOptions* parser_options) {
- image_ = (!image_filename_.empty());
- if (image_) {
+ boot_image_ = !image_filename_.empty();
+ app_image_ = app_image_fd_ != -1 || !app_image_file_name_.empty();
+
+ if (IsAppImage() && IsBootImage()) {
+ Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
+ }
+
+ if (IsBootImage()) {
// We need the boot image to always be debuggable.
compiler_options_->debuggable_ = true;
}
@@ -647,7 +658,7 @@
android_root_ += android_root_env_var;
}
- if (!image_ && parser_options->boot_image_filename.empty()) {
+ if (!boot_image_ && parser_options->boot_image_filename.empty()) {
parser_options->boot_image_filename += android_root_;
parser_options->boot_image_filename += "/framework/boot.art";
}
@@ -656,7 +667,7 @@
boot_image_option_ += parser_options->boot_image_filename;
}
- if (image_classes_filename_ != nullptr && !image_) {
+ if (image_classes_filename_ != nullptr && !IsBootImage()) {
Usage("--image-classes should only be used with --image");
}
@@ -668,7 +679,7 @@
Usage("--image-classes-zip should be used with --image-classes");
}
- if (compiled_classes_filename_ != nullptr && !image_) {
+ if (compiled_classes_filename_ != nullptr && !IsBootImage()) {
Usage("--compiled-classes should only be used with --image");
}
@@ -912,7 +923,11 @@
} else if (option.starts_with("--swap-file=")) {
swap_file_name_ = option.substr(strlen("--swap-file=")).data();
} else if (option.starts_with("--swap-fd=")) {
- ParseSwapFd(option);
+ ParseUintOption(option, "--swap-fd", &swap_fd_, Usage);
+ } else if (option.starts_with("--app-image-file=")) {
+ app_image_file_name_ = option.substr(strlen("--app-image-file=")).data();
+ } else if (option.starts_with("--app-image-fd=")) {
+ ParseUintOption(option, "--app-image-fd", &app_image_fd_, Usage);
} else if (option.starts_with("--verbose-methods=")) {
// TODO: rather than switch off compiler logging, make all VLOG(compiler) messages
// conditional on having verbost methods.
@@ -974,7 +989,6 @@
// released immediately.
unlink(swap_file_name_.c_str());
}
-
return true;
}
@@ -1016,7 +1030,7 @@
callbacks_.reset(new QuickCompilerCallbacks(
verification_results_.get(),
&method_inliner_map_,
- image_ ?
+ IsBootImage() ?
CompilerCallbacks::CallbackMode::kCompileBootImage :
CompilerCallbacks::CallbackMode::kCompileApp));
runtime_options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
@@ -1026,7 +1040,7 @@
// Only allow no boot image for the runtime if we're compiling one. When we compile an app,
// we don't want fallback mode, it will abort as we do not push a boot classpath (it might
// have been stripped in preopting, anyways).
- if (!image_) {
+ if (!IsBootImage()) {
runtime_options.push_back(std::make_pair("-Xno-dex-file-fallback", nullptr));
}
// Disable libsigchain. We don't don't need it during compilation and it prevents us
@@ -1065,7 +1079,7 @@
"': " << error_msg;
return false;
}
- } else if (image_) {
+ } else if (IsBootImage()) {
image_classes_.reset(new std::unordered_set<std::string>);
}
// If --compiled-classes was specified, calculate the full list of classes to compile in the
@@ -1178,7 +1192,7 @@
// If we use a swap file, ensure we are above the threshold to make it necessary.
if (swap_fd_ != -1) {
- if (!UseSwap(image_, dex_files_)) {
+ if (!UseSwap(IsBootImage(), dex_files_)) {
close(swap_fd_);
swap_fd_ = -1;
VLOG(compiler) << "Decided to run without swap.";
@@ -1192,7 +1206,7 @@
* If we're not in interpret-only or verify-none mode, go ahead and compile small applications.
* Don't bother to check if we're doing the image.
*/
- if (!image_ &&
+ if (!IsBootImage() &&
compiler_options_->IsCompilationEnabled() &&
compiler_kind_ == Compiler::kQuick) {
size_t num_methods = 0;
@@ -1246,7 +1260,7 @@
compiler_kind_,
instruction_set_,
instruction_set_features_.get(),
- image_,
+ IsBootImage(),
image_classes_.release(),
compiled_classes_.release(),
nullptr,
@@ -1341,7 +1355,7 @@
uint32_t image_file_location_oat_checksum = 0;
uintptr_t image_file_location_oat_data_begin = 0;
int32_t image_patch_delta = 0;
- if (image_) {
+ if (IsImage()) {
PrepareImageWriter(image_base_);
} else {
TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
@@ -1366,7 +1380,7 @@
key_value_store_.get()));
}
- if (image_) {
+ if (IsImage()) {
// The OatWriter constructor has already updated offsets in methods and we need to
// prepare method offsets in the image address space for direct method patching.
TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_);
@@ -1391,7 +1405,7 @@
// If we are compiling an image, invoke the image creation routine. Else just skip.
bool HandleImage() {
- if (image_) {
+ if (IsImage()) {
TimingLogger::ScopedTiming t("dex2oat ImageWriter", timings_);
if (!CreateImageFile()) {
return false;
@@ -1474,7 +1488,15 @@
}
bool IsImage() const {
- return image_;
+ return IsAppImage() || IsBootImage();
+ }
+
+ bool IsAppImage() const {
+ return app_image_;
+ }
+
+ bool IsBootImage() const {
+ return boot_image_;
}
bool IsHost() const {
@@ -1576,7 +1598,10 @@
bool CreateImageFile()
REQUIRES(!Locks::mutator_lock_) {
CHECK(image_writer_ != nullptr);
- if (!image_writer_->Write(image_filename_, oat_unstripped_, oat_location_)) {
+ if (!image_writer_->Write(app_image_fd_,
+ IsBootImage() ? image_filename_ : app_image_file_name_,
+ oat_unstripped_,
+ oat_location_)) {
LOG(ERROR) << "Failed to create image file " << image_filename_;
return false;
}
@@ -1585,8 +1610,8 @@
// Destroy ImageWriter before doing FixupElf.
image_writer_.reset();
- // Do not fix up the ELF file if we are --compile-pic
- if (!compiler_options_->GetCompilePic()) {
+ // Do not fix up the ELF file if we are --compile-pic or compiing the app image
+ if (!compiler_options_->GetCompilePic() && IsBootImage()) {
std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_unstripped_.c_str()));
if (oat_file.get() == nullptr) {
PLOG(ERROR) << "Failed to open ELF file: " << oat_unstripped_;
@@ -1748,7 +1773,8 @@
std::unique_ptr<std::unordered_set<std::string>> image_classes_;
std::unique_ptr<std::unordered_set<std::string>> compiled_classes_;
std::unique_ptr<std::unordered_set<std::string>> compiled_methods_;
- bool image_;
+ bool app_image_;
+ bool boot_image_;
bool is_host_;
std::string android_root_;
std::vector<const DexFile*> dex_files_;
@@ -1767,6 +1793,8 @@
bool dump_cfg_append_;
std::string swap_file_name_;
int swap_fd_;
+ std::string app_image_file_name_;
+ int app_image_fd_;
std::string profile_file_; // Profile file to use
TimingLogger* timings_;
std::unique_ptr<CumulativeLogger> compiler_phases_timings_;
@@ -1895,7 +1923,7 @@
// 3) Compiling with --host
// 4) Compiling on the host (not a target build)
// Otherwise, print a stripped command line.
- if (kIsDebugBuild || dex2oat.IsImage() || dex2oat.IsHost() || !kIsTargetBuild) {
+ if (kIsDebugBuild || dex2oat.IsBootImage() || dex2oat.IsHost() || !kIsTargetBuild) {
LOG(INFO) << CommandLine();
} else {
LOG(INFO) << StrippedCommandLine();
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index f741732..cf548ad 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -468,12 +468,6 @@
}
}
-inline void ArtMethod::CopyFrom(const ArtMethod* src, size_t image_pointer_size) {
- memcpy(reinterpret_cast<void*>(this), reinterpret_cast<const void*>(src),
- Size(image_pointer_size));
- declaring_class_ = GcRoot<mirror::Class>(const_cast<ArtMethod*>(src)->GetDeclaringClass());
-}
-
} // namespace art
#endif // ART_RUNTIME_ART_METHOD_INL_H_
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index c1279bf..f4a5f23 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -367,7 +367,7 @@
}
const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) {
- if (IsRuntimeMethod() || IsProxyMethod()) {
+ if (IsRuntimeMethod()) {
return nullptr;
}
@@ -381,6 +381,12 @@
return nullptr;
}
+ if (existing_entry_point == GetQuickProxyInvokeHandler()) {
+ DCHECK(IsProxyMethod() && !IsConstructor());
+ // The proxy entry point does not have any method header.
+ return nullptr;
+ }
+
// Check whether the current entry point contains this pc.
if (!class_linker->IsQuickResolutionStub(existing_entry_point) &&
!class_linker->IsQuickToInterpreterBridge(existing_entry_point)) {
@@ -452,4 +458,28 @@
return method_header;
}
+
+void ArtMethod::CopyFrom(ArtMethod* src, size_t image_pointer_size) {
+ memcpy(reinterpret_cast<void*>(this), reinterpret_cast<const void*>(src),
+ Size(image_pointer_size));
+ declaring_class_ = GcRoot<mirror::Class>(const_cast<ArtMethod*>(src)->GetDeclaringClass());
+
+ // If the entry point of the method we are copying from is from JIT code, we just
+ // put the entry point of the new method to interpreter. We could set the entry point
+ // to the JIT code, but this would require taking the JIT code cache lock to notify
+ // it, which we do not want at this level.
+ Runtime* runtime = Runtime::Current();
+ if (runtime->GetJit() != nullptr) {
+ if (runtime->GetJit()->GetCodeCache()->ContainsPc(GetEntryPointFromQuickCompiledCode())) {
+ SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(), image_pointer_size);
+ }
+ }
+ // Clear the profiling info for the same reasons as the JIT code.
+ if (!src->IsNative()) {
+ SetProfilingInfoPtrSize(nullptr, image_pointer_size);
+ }
+ // Clear hotness to let the JIT properly decide when to compile this method.
+ hotness_count_ = 0;
+}
+
} // namespace art
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 551989d..ce9f202 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -49,8 +49,8 @@
ArtMethod() : access_flags_(0), dex_code_item_offset_(0), dex_method_index_(0),
method_index_(0) { }
- ArtMethod(const ArtMethod& src, size_t image_pointer_size) {
- CopyFrom(&src, image_pointer_size);
+ ArtMethod(ArtMethod* src, size_t image_pointer_size) {
+ CopyFrom(src, image_pointer_size);
}
static ArtMethod* FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa,
@@ -313,6 +313,10 @@
SetEntryPointFromJniPtrSize(info, sizeof(void*));
}
+ ALWAYS_INLINE void SetProfilingInfoPtrSize(ProfilingInfo* info, size_t pointer_size) {
+ SetEntryPointFromJniPtrSize(info, pointer_size);
+ }
+
static MemberOffset ProfilingInfoOffset() {
return EntryPointFromJniOffset(sizeof(void*));
}
@@ -429,7 +433,7 @@
return pointer_size;
}
- void CopyFrom(const ArtMethod* src, size_t image_pointer_size)
+ void CopyFrom(ArtMethod* src, size_t image_pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
ALWAYS_INLINE GcRoot<mirror::Class>* GetDexCacheResolvedTypes(size_t pointer_size)
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 5de1cac..da70456 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -5279,7 +5279,7 @@
miranda_method = reinterpret_cast<ArtMethod*>(allocator.Alloc(method_size));
CHECK(miranda_method != nullptr);
// Point the interface table at a phantom slot.
- new(miranda_method) ArtMethod(*interface_method, image_pointer_size_);
+ new(miranda_method) ArtMethod(interface_method, image_pointer_size_);
miranda_methods.push_back(miranda_method);
}
method_array->SetElementPtrSize(j, miranda_method, image_pointer_size_);
diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h
new file mode 100644
index 0000000..26f5ad3
--- /dev/null
+++ b/runtime/gc/collector/concurrent_copying-inl.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_COLLECTOR_CONCURRENT_COPYING_INL_H_
+#define ART_RUNTIME_GC_COLLECTOR_CONCURRENT_COPYING_INL_H_
+
+#include "concurrent_copying.h"
+
+#include "gc/accounting/space_bitmap-inl.h"
+#include "gc/heap.h"
+#include "gc/space/region_space.h"
+#include "lock_word.h"
+
+namespace art {
+namespace gc {
+namespace collector {
+
+inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref) {
+ if (from_ref == nullptr) {
+ return nullptr;
+ }
+ DCHECK(heap_->collector_type_ == kCollectorTypeCC);
+ if (UNLIKELY(kUseBakerReadBarrier && !is_active_)) {
+ // In the lock word forward address state, the read barrier bits
+ // in the lock word are part of the stored forwarding address and
+ // invalid. This is usually OK as the from-space copy of objects
+ // aren't accessed by mutators due to the to-space
+ // invariant. However, during the dex2oat image writing relocation
+ // and the zygote compaction, objects can be in the forward
+ // address state (to store the forward/relocation addresses) and
+ // they can still be accessed and the invalid read barrier bits
+ // are consulted. If they look like gray but aren't really, the
+ // read barriers slow path can trigger when it shouldn't. To guard
+ // against this, return here if the CC collector isn't running.
+ return from_ref;
+ }
+ DCHECK(region_space_ != nullptr) << "Read barrier slow path taken when CC isn't running?";
+ space::RegionSpace::RegionType rtype = region_space_->GetRegionType(from_ref);
+ switch (rtype) {
+ case space::RegionSpace::RegionType::kRegionTypeToSpace:
+ // It's already marked.
+ return from_ref;
+ case space::RegionSpace::RegionType::kRegionTypeFromSpace: {
+ mirror::Object* to_ref = GetFwdPtr(from_ref);
+ if (kUseBakerReadBarrier) {
+ DCHECK_NE(to_ref, ReadBarrier::GrayPtr())
+ << "from_ref=" << from_ref << " to_ref=" << to_ref;
+ }
+ if (to_ref == nullptr) {
+ // It isn't marked yet. Mark it by copying it to the to-space.
+ to_ref = Copy(from_ref);
+ }
+ DCHECK(region_space_->IsInToSpace(to_ref) || heap_->non_moving_space_->HasAddress(to_ref))
+ << "from_ref=" << from_ref << " to_ref=" << to_ref;
+ return to_ref;
+ }
+ case space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace: {
+ // This may or may not succeed, which is ok.
+ if (kUseBakerReadBarrier) {
+ from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+ }
+ mirror::Object* to_ref = from_ref;
+ if (region_space_bitmap_->AtomicTestAndSet(from_ref)) {
+ // Already marked.
+ } else {
+ // Newly marked.
+ if (kUseBakerReadBarrier) {
+ DCHECK_EQ(to_ref->GetReadBarrierPointer(), ReadBarrier::GrayPtr());
+ }
+ PushOntoMarkStack(to_ref);
+ }
+ return to_ref;
+ }
+ case space::RegionSpace::RegionType::kRegionTypeNone:
+ return MarkNonMoving(from_ref);
+ default:
+ UNREACHABLE();
+ }
+}
+
+inline mirror::Object* ConcurrentCopying::GetFwdPtr(mirror::Object* from_ref) {
+ DCHECK(region_space_->IsInFromSpace(from_ref));
+ LockWord lw = from_ref->GetLockWord(false);
+ if (lw.GetState() == LockWord::kForwardingAddress) {
+ mirror::Object* fwd_ptr = reinterpret_cast<mirror::Object*>(lw.ForwardingAddress());
+ DCHECK(fwd_ptr != nullptr);
+ return fwd_ptr;
+ } else {
+ return nullptr;
+ }
+}
+
+} // namespace collector
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_COLLECTOR_CONCURRENT_COPYING_INL_H_
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 20e775c..4a49712 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -368,30 +368,15 @@
}
}
}
- // TODO: Other garbage collectors uses Runtime::VisitConcurrentRoots(), refactor this part
- // to also use the same function.
{
- TimingLogger::ScopedTiming split2("VisitConstantRoots", GetTimings());
- Runtime::Current()->VisitConstantRoots(this);
- }
- {
- TimingLogger::ScopedTiming split3("VisitInternTableRoots", GetTimings());
- Runtime::Current()->GetInternTable()->VisitRoots(this, kVisitRootFlagAllRoots);
- }
- {
- TimingLogger::ScopedTiming split4("VisitClassLinkerRoots", GetTimings());
- Runtime::Current()->GetClassLinker()->VisitRoots(this, kVisitRootFlagAllRoots);
+ TimingLogger::ScopedTiming split2("VisitConcurrentRoots", GetTimings());
+ Runtime::Current()->VisitConcurrentRoots(this, kVisitRootFlagAllRoots);
}
{
// TODO: don't visit the transaction roots if it's not active.
TimingLogger::ScopedTiming split5("VisitNonThreadRoots", GetTimings());
Runtime::Current()->VisitNonThreadRoots(this);
}
- {
- TimingLogger::ScopedTiming split6("Dbg::VisitRoots", GetTimings());
- Dbg::VisitRoots(this);
- }
- Runtime::Current()->GetHeap()->VisitAllocationRecords(this);
// Immune spaces.
for (auto& space : heap_->GetContinuousSpaces()) {
@@ -594,8 +579,8 @@
Thread* self = Thread::Current(); // TODO: pass self as an argument from call sites?
CHECK(thread_running_gc_ != nullptr);
MarkStackMode mark_stack_mode = mark_stack_mode_.LoadRelaxed();
- if (mark_stack_mode == kMarkStackModeThreadLocal) {
- if (self == thread_running_gc_) {
+ if (LIKELY(mark_stack_mode == kMarkStackModeThreadLocal)) {
+ if (LIKELY(self == thread_running_gc_)) {
// If GC-running thread, use the GC mark stack instead of a thread-local mark stack.
CHECK(self->GetThreadLocalMarkStack() == nullptr);
if (UNLIKELY(gc_mark_stack_->IsFull())) {
@@ -663,18 +648,6 @@
return heap_->live_stack_.get();
}
-inline mirror::Object* ConcurrentCopying::GetFwdPtr(mirror::Object* from_ref) {
- DCHECK(region_space_->IsInFromSpace(from_ref));
- LockWord lw = from_ref->GetLockWord(false);
- if (lw.GetState() == LockWord::kForwardingAddress) {
- mirror::Object* fwd_ptr = reinterpret_cast<mirror::Object*>(lw.ForwardingAddress());
- CHECK(fwd_ptr != nullptr);
- return fwd_ptr;
- } else {
- return nullptr;
- }
-}
-
// The following visitors are that used to verify that there's no
// references to the from-space left after marking.
class ConcurrentCopyingVerifyNoFromSpaceRefsVisitor : public SingleRootVisitor {
@@ -1080,7 +1053,7 @@
return count;
}
-void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) {
+inline void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) {
DCHECK(!region_space_->IsInFromSpace(to_ref));
if (kUseBakerReadBarrier) {
DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr())
@@ -1095,9 +1068,10 @@
<< " " << to_ref << " " << to_ref->GetReadBarrierPointer()
<< " is_marked=" << IsMarked(to_ref);
}
- if (to_ref->GetClass<kVerifyNone, kWithoutReadBarrier>()->IsTypeOfReferenceClass() &&
- to_ref->AsReference()->GetReferent<kWithoutReadBarrier>() != nullptr &&
- !IsInToSpace(to_ref->AsReference()->GetReferent<kWithoutReadBarrier>())) {
+#ifdef USE_BAKER_OR_BROOKS_READ_BARRIER
+ if (UNLIKELY((to_ref->GetClass<kVerifyNone, kWithoutReadBarrier>()->IsTypeOfReferenceClass() &&
+ to_ref->AsReference()->GetReferent<kWithoutReadBarrier>() != nullptr &&
+ !IsInToSpace(to_ref->AsReference()->GetReferent<kWithoutReadBarrier>())))) {
// Leave this Reference gray in the queue so that GetReferent() will trigger a read barrier. We
// will change it to black or white later in ReferenceQueue::DequeuePendingReference().
CHECK(to_ref->AsReference()->IsEnqueued()) << "Left unenqueued ref gray " << to_ref;
@@ -1106,14 +1080,13 @@
// be concurrently marked after the Scan() call above has enqueued the Reference, in which case
// the above IsInToSpace() evaluates to true and we change the color from gray to black or white
// here in this else block.
-#ifdef USE_BAKER_OR_BROOKS_READ_BARRIER
if (kUseBakerReadBarrier) {
if (region_space_->IsInToSpace(to_ref)) {
// If to-space, change from gray to white.
bool success = to_ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(),
ReadBarrier::WhitePtr());
CHECK(success) << "Must succeed as we won the race.";
- CHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr());
+ DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr());
} else {
// If non-moving space/unevac from space, change from gray
// to black. We can't change gray to white because it's not
@@ -1125,13 +1098,13 @@
bool success = to_ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(),
ReadBarrier::BlackPtr());
CHECK(success) << "Must succeed as we won the race.";
- CHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
+ DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
}
}
-#else
- DCHECK(!kUseBakerReadBarrier);
-#endif
}
+#else
+ DCHECK(!kUseBakerReadBarrier);
+#endif
if (ReadBarrier::kEnableToSpaceInvariantChecks || kIsDebugBuild) {
ConcurrentCopyingAssertToSpaceInvariantObjectVisitor visitor(this);
visitor(to_ref);
@@ -1622,6 +1595,7 @@
}
void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+ ALWAYS_INLINE
SHARED_REQUIRES(Locks::mutator_lock_) {
if (!root->IsNull()) {
VisitRoot(root);
@@ -1629,6 +1603,7 @@
}
void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+ ALWAYS_INLINE
SHARED_REQUIRES(Locks::mutator_lock_) {
collector_->MarkRoot(root);
}
@@ -1638,7 +1613,7 @@
};
// Scan ref fields of an object.
-void ConcurrentCopying::Scan(mirror::Object* to_ref) {
+inline void ConcurrentCopying::Scan(mirror::Object* to_ref) {
DCHECK(!region_space_->IsInFromSpace(to_ref));
ConcurrentCopyingRefFieldsVisitor visitor(this);
to_ref->VisitReferences(visitor, visitor);
@@ -1648,9 +1623,6 @@
inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset) {
mirror::Object* ref = obj->GetFieldObject<
mirror::Object, kVerifyNone, kWithoutReadBarrier, false>(offset);
- if (ref == nullptr || region_space_->IsInToSpace(ref)) {
- return;
- }
mirror::Object* to_ref = Mark(ref);
if (to_ref == ref) {
return;
@@ -1669,14 +1641,11 @@
}
// Process some roots.
-void ConcurrentCopying::VisitRoots(
+inline void ConcurrentCopying::VisitRoots(
mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) {
for (size_t i = 0; i < count; ++i) {
mirror::Object** root = roots[i];
mirror::Object* ref = *root;
- if (ref == nullptr || region_space_->IsInToSpace(ref)) {
- continue;
- }
mirror::Object* to_ref = Mark(ref);
if (to_ref == ref) {
continue;
@@ -1693,12 +1662,9 @@
}
}
-void ConcurrentCopying::MarkRoot(mirror::CompressedReference<mirror::Object>* root) {
+inline void ConcurrentCopying::MarkRoot(mirror::CompressedReference<mirror::Object>* root) {
DCHECK(!root->IsNull());
mirror::Object* const ref = root->AsMirrorPtr();
- if (region_space_->IsInToSpace(ref)) {
- return;
- }
mirror::Object* to_ref = Mark(ref);
if (to_ref != ref) {
auto* addr = reinterpret_cast<Atomic<mirror::CompressedReference<mirror::Object>>*>(root);
@@ -1714,7 +1680,7 @@
}
}
-void ConcurrentCopying::VisitRoots(
+inline void ConcurrentCopying::VisitRoots(
mirror::CompressedReference<mirror::Object>** roots, size_t count,
const RootInfo& info ATTRIBUTE_UNUSED) {
for (size_t i = 0; i < count; ++i) {
@@ -2013,148 +1979,85 @@
return alloc_stack->Contains(ref);
}
-mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref) {
- if (from_ref == nullptr) {
- return nullptr;
- }
- DCHECK(from_ref != nullptr);
- DCHECK(heap_->collector_type_ == kCollectorTypeCC);
- if (kUseBakerReadBarrier && !is_active_) {
- // In the lock word forward address state, the read barrier bits
- // in the lock word are part of the stored forwarding address and
- // invalid. This is usually OK as the from-space copy of objects
- // aren't accessed by mutators due to the to-space
- // invariant. However, during the dex2oat image writing relocation
- // and the zygote compaction, objects can be in the forward
- // address state (to store the forward/relocation addresses) and
- // they can still be accessed and the invalid read barrier bits
- // are consulted. If they look like gray but aren't really, the
- // read barriers slow path can trigger when it shouldn't. To guard
- // against this, return here if the CC collector isn't running.
- return from_ref;
- }
- DCHECK(region_space_ != nullptr) << "Read barrier slow path taken when CC isn't running?";
- space::RegionSpace::RegionType rtype = region_space_->GetRegionType(from_ref);
- if (rtype == space::RegionSpace::RegionType::kRegionTypeToSpace) {
- // It's already marked.
- return from_ref;
- }
- mirror::Object* to_ref;
- if (rtype == space::RegionSpace::RegionType::kRegionTypeFromSpace) {
- to_ref = GetFwdPtr(from_ref);
- if (kUseBakerReadBarrier) {
- DCHECK(to_ref != ReadBarrier::GrayPtr()) << "from_ref=" << from_ref << " to_ref=" << to_ref;
+mirror::Object* ConcurrentCopying::MarkNonMoving(mirror::Object* ref) {
+ // ref is in a non-moving space (from_ref == to_ref).
+ DCHECK(!region_space_->HasAddress(ref)) << ref;
+ if (immune_region_.ContainsObject(ref)) {
+ accounting::ContinuousSpaceBitmap* cc_bitmap =
+ cc_heap_bitmap_->GetContinuousSpaceBitmap(ref);
+ DCHECK(cc_bitmap != nullptr)
+ << "An immune space object must have a bitmap";
+ if (kIsDebugBuild) {
+ DCHECK(heap_mark_bitmap_->GetContinuousSpaceBitmap(ref)->Test(ref))
+ << "Immune space object must be already marked";
}
- if (to_ref == nullptr) {
- // It isn't marked yet. Mark it by copying it to the to-space.
- to_ref = Copy(from_ref);
- }
- DCHECK(region_space_->IsInToSpace(to_ref) || heap_->non_moving_space_->HasAddress(to_ref))
- << "from_ref=" << from_ref << " to_ref=" << to_ref;
- } else if (rtype == space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace) {
// This may or may not succeed, which is ok.
if (kUseBakerReadBarrier) {
- from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+ ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
}
- if (region_space_bitmap_->AtomicTestAndSet(from_ref)) {
+ if (cc_bitmap->AtomicTestAndSet(ref)) {
// Already marked.
- to_ref = from_ref;
} else {
// Newly marked.
- to_ref = from_ref;
if (kUseBakerReadBarrier) {
- DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
+ DCHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::GrayPtr());
}
- PushOntoMarkStack(to_ref);
+ PushOntoMarkStack(ref);
}
} else {
- // from_ref is in a non-moving space.
- DCHECK(!region_space_->HasAddress(from_ref)) << from_ref;
- if (immune_region_.ContainsObject(from_ref)) {
- accounting::ContinuousSpaceBitmap* cc_bitmap =
- cc_heap_bitmap_->GetContinuousSpaceBitmap(from_ref);
- DCHECK(cc_bitmap != nullptr)
- << "An immune space object must have a bitmap";
- if (kIsDebugBuild) {
- DCHECK(heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref)->Test(from_ref))
- << "Immune space object must be already marked";
- }
- // This may or may not succeed, which is ok.
+ // Use the mark bitmap.
+ accounting::ContinuousSpaceBitmap* mark_bitmap =
+ heap_mark_bitmap_->GetContinuousSpaceBitmap(ref);
+ accounting::LargeObjectBitmap* los_bitmap =
+ heap_mark_bitmap_->GetLargeObjectBitmap(ref);
+ CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
+ bool is_los = mark_bitmap == nullptr;
+ if (!is_los && mark_bitmap->Test(ref)) {
+ // Already marked.
if (kUseBakerReadBarrier) {
- from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+ DCHECK(ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
+ ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
}
- if (cc_bitmap->AtomicTestAndSet(from_ref)) {
- // Already marked.
- to_ref = from_ref;
- } else {
- // Newly marked.
- to_ref = from_ref;
- if (kUseBakerReadBarrier) {
- DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
- }
- PushOntoMarkStack(to_ref);
+ } else if (is_los && los_bitmap->Test(ref)) {
+ // Already marked in LOS.
+ if (kUseBakerReadBarrier) {
+ DCHECK(ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
+ ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
}
} else {
- // Use the mark bitmap.
- accounting::ContinuousSpaceBitmap* mark_bitmap =
- heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref);
- accounting::LargeObjectBitmap* los_bitmap =
- heap_mark_bitmap_->GetLargeObjectBitmap(from_ref);
- CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
- bool is_los = mark_bitmap == nullptr;
- if (!is_los && mark_bitmap->Test(from_ref)) {
- // Already marked.
- to_ref = from_ref;
- if (kUseBakerReadBarrier) {
- DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
- to_ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
+ // Not marked.
+ if (IsOnAllocStack(ref)) {
+ // If it's on the allocation stack, it's considered marked. Keep it white.
+ // Objects on the allocation stack need not be marked.
+ if (!is_los) {
+ DCHECK(!mark_bitmap->Test(ref));
+ } else {
+ DCHECK(!los_bitmap->Test(ref));
}
- } else if (is_los && los_bitmap->Test(from_ref)) {
- // Already marked in LOS.
- to_ref = from_ref;
if (kUseBakerReadBarrier) {
- DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
- to_ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
+ DCHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::WhitePtr());
}
} else {
- // Not marked.
- if (IsOnAllocStack(from_ref)) {
- // If it's on the allocation stack, it's considered marked. Keep it white.
- to_ref = from_ref;
- // Objects on the allocation stack need not be marked.
- if (!is_los) {
- DCHECK(!mark_bitmap->Test(to_ref));
- } else {
- DCHECK(!los_bitmap->Test(to_ref));
- }
- if (kUseBakerReadBarrier) {
- DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr());
- }
+ // Not marked or on the allocation stack. Try to mark it.
+ // This may or may not succeed, which is ok.
+ if (kUseBakerReadBarrier) {
+ ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+ }
+ if (!is_los && mark_bitmap->AtomicTestAndSet(ref)) {
+ // Already marked.
+ } else if (is_los && los_bitmap->AtomicTestAndSet(ref)) {
+ // Already marked in LOS.
} else {
- // Not marked or on the allocation stack. Try to mark it.
- // This may or may not succeed, which is ok.
+ // Newly marked.
if (kUseBakerReadBarrier) {
- from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+ DCHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::GrayPtr());
}
- if (!is_los && mark_bitmap->AtomicTestAndSet(from_ref)) {
- // Already marked.
- to_ref = from_ref;
- } else if (is_los && los_bitmap->AtomicTestAndSet(from_ref)) {
- // Already marked in LOS.
- to_ref = from_ref;
- } else {
- // Newly marked.
- to_ref = from_ref;
- if (kUseBakerReadBarrier) {
- DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
- }
- PushOntoMarkStack(to_ref);
- }
+ PushOntoMarkStack(ref);
}
}
}
}
- return to_ref;
+ return ref;
}
void ConcurrentCopying::FinishPhase() {
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index c32b19e..27726e2 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -93,7 +93,7 @@
DCHECK(ref != nullptr);
return IsMarked(ref) == ref;
}
- mirror::Object* Mark(mirror::Object* from_ref) SHARED_REQUIRES(Locks::mutator_lock_)
+ ALWAYS_INLINE mirror::Object* Mark(mirror::Object* from_ref) SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
bool IsMarking() const {
return is_marking_;
@@ -183,6 +183,8 @@
void DisableMarking() SHARED_REQUIRES(Locks::mutator_lock_);
void IssueDisableMarkingCheckpoint() SHARED_REQUIRES(Locks::mutator_lock_);
void ExpandGcMarkStack() SHARED_REQUIRES(Locks::mutator_lock_);
+ mirror::Object* MarkNonMoving(mirror::Object* from_ref) SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
space::RegionSpace* region_space_; // The underlying region space.
std::unique_ptr<Barrier> gc_barrier_;
diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc
index 8aaa5fa..7931306 100644
--- a/runtime/jit/jit_instrumentation.cc
+++ b/runtime/jit/jit_instrumentation.cc
@@ -102,15 +102,13 @@
} else {
// We failed allocating. Instead of doing the collection on the Java thread, we push
// an allocation to a compiler thread, that will do the collection.
- thread_pool_->AddTask(self, new JitCompileTask(
- method->GetInterfaceMethodIfProxy(sizeof(void*)), JitCompileTask::kAllocateProfile));
+ thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kAllocateProfile));
thread_pool_->StartWorkers(self);
}
}
if (sample_count == hot_method_threshold_) {
- thread_pool_->AddTask(self, new JitCompileTask(
- method->GetInterfaceMethodIfProxy(sizeof(void*)), JitCompileTask::kCompile));
+ thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
thread_pool_->StartWorkers(self);
}
}
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 022f31d..5c6520f 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -99,7 +99,7 @@
#ifndef USE_BAKER_OR_BROOKS_READ_BARRIER
NO_RETURN
#endif
- bool AtomicSetReadBarrierPointer(Object* expected_rb_ptr, Object* rb_ptr)
+ ALWAYS_INLINE bool AtomicSetReadBarrierPointer(Object* expected_rb_ptr, Object* rb_ptr)
SHARED_REQUIRES(Locks::mutator_lock_);
void AssertReadBarrierPointer() const SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index 5b73557..5337760 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -270,7 +270,7 @@
}
template<class T> template<typename Visitor>
-void ObjectArray<T>::VisitReferences(const Visitor& visitor) {
+inline void ObjectArray<T>::VisitReferences(const Visitor& visitor) {
const size_t length = static_cast<size_t>(GetLength());
for (size_t i = 0; i < length; ++i) {
visitor(this, OffsetOfElement(i), false);
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index 4998a6a..7de6c06 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -19,7 +19,7 @@
#include "read_barrier.h"
-#include "gc/collector/concurrent_copying.h"
+#include "gc/collector/concurrent_copying-inl.h"
#include "gc/heap.h"
#include "mirror/object_reference.h"
#include "mirror/reference.h"
diff --git a/runtime/stack.cc b/runtime/stack.cc
index b0727da..d7edfad 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -958,26 +958,18 @@
return runtime->GetRuntimeMethodFrameInfo(method);
}
- // For Proxy method we add special handling for the direct method case (there is only one
- // direct method - constructor). Direct method is cloned from original
- // java.lang.reflect.Proxy class together with code and as a result it is executed as usual
- // quick compiled method without any stubs. So the frame info should be returned as it is a
- // quick method not a stub. However, if instrumentation stubs are installed, the
- // instrumentation->GetQuickCodeFor() returns the artQuickProxyInvokeHandler instead of an
- // oat code pointer, thus we have to add a special case here.
if (method->IsProxyMethod()) {
- if (method->IsDirect()) {
- CHECK(method->IsConstructor());
- const void* code_pointer =
- EntryPointToCodePointer(method->GetEntryPointFromQuickCompiledCode());
- return reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].frame_info_;
- } else {
- return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs);
- }
+ // There is only one direct method of a proxy class: the constructor. A direct method is
+ // cloned from the original java.lang.reflect.Proxy and is executed as usual quick
+ // compiled method without any stubs. Therefore the method must have a OatQuickMethodHeader.
+ DCHECK(!method->IsDirect() && !method->IsConstructor())
+ << "Constructors of proxy classes must have a OatQuickMethodHeader";
+ return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs);
}
- ClassLinker* class_linker = runtime->GetClassLinker();
+ // The only remaining case is if the method is native and uses the generic JNI stub.
DCHECK(method->IsNative());
+ ClassLinker* class_linker = runtime->GetClassLinker();
const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(method, sizeof(void*));
DCHECK(class_linker->IsQuickGenericJniStub(entry_point)) << PrettyMethod(method);
// Generic JNI frame.
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index e1d4160..2db79ab 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -665,20 +665,22 @@
// Interfaces may always have static initializers for their fields. If we are running with
// default methods enabled we also allow other public, static, non-final methods to have code.
// Otherwise that is the only type of method allowed.
- if (runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods)) {
- if (IsInstanceConstructor()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-static constructor";
- return false;
- } else if (method_access_flags_ & kAccFinal) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have final methods";
- return false;
- } else if (!(method_access_flags_ & kAccPublic)) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-public members";
+ if (!(IsConstructor() && IsStatic())) {
+ if (runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods)) {
+ if (IsInstanceConstructor()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-static constructor";
+ return false;
+ } else if (method_access_flags_ & kAccFinal) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have final methods";
+ return false;
+ } else if (!(method_access_flags_ & kAccPublic)) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-public members";
+ return false;
+ }
+ } else {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods must be abstract";
return false;
}
- } else if (!IsConstructor() || !IsStatic()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods must be abstract";
- return false;
}
}
@@ -3662,8 +3664,15 @@
<< PrettyMethod(res_method);
return nullptr;
}
- // Check that interface methods match interface classes.
- if (klass->IsInterface() && method_type != METHOD_INTERFACE) {
+ // Check that interface methods are static or match interface classes.
+ // We only allow statics if we don't have default methods enabled.
+ Runtime* runtime = Runtime::Current();
+ const bool default_methods_supported =
+ runtime == nullptr ||
+ runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods);
+ if (klass->IsInterface() &&
+ method_type != METHOD_INTERFACE &&
+ (!default_methods_supported || method_type != METHOD_STATIC)) {
Fail(VERIFY_ERROR_CLASS_CHANGE) << "non-interface method " << PrettyMethod(res_method)
<< " is in an interface class " << PrettyClass(klass);
return nullptr;
diff --git a/test/542-unresolved-access-check/expected.txt b/test/542-unresolved-access-check/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/542-unresolved-access-check/expected.txt
diff --git a/test/542-unresolved-access-check/info.txt b/test/542-unresolved-access-check/info.txt
new file mode 100644
index 0000000..30d45b8
--- /dev/null
+++ b/test/542-unresolved-access-check/info.txt
@@ -0,0 +1 @@
+Test unresolved/access checks entry points with the JIT.
diff --git a/test/542-unresolved-access-check/src/Main.java b/test/542-unresolved-access-check/src/Main.java
new file mode 100644
index 0000000..2bdf47f
--- /dev/null
+++ b/test/542-unresolved-access-check/src/Main.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+import p1.InP1;
+import p1.PlaceHolder;
+
+
+// Custom class loader to prevent loading while verifying.
+class MyClassLoader extends ClassLoader {
+ MyClassLoader() throws Exception {
+ super(MyClassLoader.class.getClassLoader());
+
+ // Some magic to get access to the pathList field of BaseDexClassLoader.
+ ClassLoader loader = getClass().getClassLoader();
+ Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
+ Field f = baseDexClassLoader.getDeclaredField("pathList");
+ f.setAccessible(true);
+ Object pathList = f.get(loader);
+
+ // Some magic to get access to the dexField field of pathList.
+ f = pathList.getClass().getDeclaredField("dexElements");
+ f.setAccessible(true);
+ dexElements = (Object[]) f.get(pathList);
+ dexFileField = dexElements[0].getClass().getDeclaredField("dexFile");
+ dexFileField.setAccessible(true);
+ }
+
+ Object[] dexElements;
+ Field dexFileField;
+
+ protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
+ if (className.equals("p1.OtherInP1") && !p1.PlaceHolder.entered) {
+ // The request comes from the verifier. Return null to get the access check entry
+ // point in the compiled code.
+ return null;
+ }
+ // Mimic what DexPathList.findClass is doing.
+ try {
+ for (Object element : dexElements) {
+ Object dex = dexFileField.get(element);
+ Method method = dex.getClass().getDeclaredMethod(
+ "loadClassBinaryName", String.class, ClassLoader.class, List.class);
+
+ if (dex != null) {
+ Class clazz = (Class)method.invoke(dex, className, this, null);
+ if (clazz != null) {
+ return clazz;
+ }
+ }
+ }
+ } catch (Exception e) { /* Ignore */ }
+ return getParent().loadClass(className);
+ }
+}
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ MyClassLoader o = new MyClassLoader();
+ Class foo = o.loadClass("LoadedByMyClassLoader");
+ Method m = foo.getDeclaredMethod("main");
+ m.invoke(null);
+ }
+}
+
+class LoadedByMyClassLoader {
+ public static void main() throws Exception {
+ for (int i = 0; i < 10000; ++i) {
+ // Warm up the JIT.
+ doTheCall(i);
+ }
+ // Sleep a while to let the JIT compile things.
+ // TODO(ngeoffray): Remove the sleep. b/25414532
+ Thread.sleep(2000);
+ doTheCall(10001);
+ }
+
+ public static void doTheCall(int i) {
+ InP1.$inline$AllocateOtherInP1(i);
+ InP1.$inline$AllocateArrayOtherInP1(i);
+ InP1.$inline$UseStaticFieldOtherInP1(i);
+ InP1.$inline$SetStaticFieldOtherInP1(i);
+ InP1.$inline$UseInstanceFieldOtherInP1(i);
+ InP1.$inline$SetInstanceFieldOtherInP1(i);
+ InP1.$inline$LoadOtherInP1(i);
+ InP1.$inline$StaticCallOtherInP1(i);
+ InP1.$inline$InstanceCallOtherInP1(i);
+ }
+}
diff --git a/test/542-unresolved-access-check/src/p1/InP1.java b/test/542-unresolved-access-check/src/p1/InP1.java
new file mode 100644
index 0000000..3516c72
--- /dev/null
+++ b/test/542-unresolved-access-check/src/p1/InP1.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package p1;
+
+public class InP1 {
+ public static Object $inline$AllocateOtherInP1(int i) {
+ // Let this method execute a while to make sure the JIT sees it hot.
+ if (i <= 10000) {
+ return null;
+ }
+ // Set the flag that we have entered InP1 code to get OtherInP1 loaded.
+ PlaceHolder.entered = true;
+ return new OtherInP1();
+ }
+
+ public static Object $inline$AllocateArrayOtherInP1(int i) {
+ if (i <= 10000) {
+ return null;
+ }
+ return new OtherInP1[10];
+ }
+
+ public static Object $inline$UseStaticFieldOtherInP1(int i) {
+ if (i <= 10000) {
+ return null;
+ }
+ return OtherInP1.staticField;
+ }
+
+ public static void $inline$SetStaticFieldOtherInP1(int i) {
+ if (i <= 10000) {
+ return;
+ }
+ OtherInP1.staticField = new Object();
+ }
+
+ public static Object $inline$UseInstanceFieldOtherInP1(int i) {
+ if (i <= 10000) {
+ return null;
+ }
+ return $noinline$AllocateOtherInP1().instanceField;
+ }
+
+ public static void $inline$SetInstanceFieldOtherInP1(int i) {
+ if (i <= 10000) {
+ return;
+ }
+ $noinline$AllocateOtherInP1().instanceField = new Object();
+ }
+
+ public static OtherInP1 $noinline$AllocateOtherInP1() {
+ try {
+ return new OtherInP1();
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
+ public static Object $inline$LoadOtherInP1(int i) {
+ if (i <= 10000) {
+ return null;
+ }
+ return OtherInP1.class;
+ }
+
+ public static Object $inline$StaticCallOtherInP1(int i) {
+ if (i <= 10000) {
+ return null;
+ }
+ return OtherInP1.doTheStaticCall();
+ }
+
+ public static Object $inline$InstanceCallOtherInP1(int i) {
+ if (i <= 10000) {
+ return null;
+ }
+ return $noinline$AllocateOtherInP1().doTheInstanceCall();
+ }
+}
diff --git a/test/542-unresolved-access-check/src/p1/OtherInP1.java b/test/542-unresolved-access-check/src/p1/OtherInP1.java
new file mode 100644
index 0000000..adc1ce1
--- /dev/null
+++ b/test/542-unresolved-access-check/src/p1/OtherInP1.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package p1;
+
+class OtherInP1 {
+ OtherInP1() {
+ }
+ static Object staticField = new Object();
+ Object instanceField = new Object();
+
+ static Object doTheStaticCall() {
+ return null;
+ }
+
+ Object doTheInstanceCall() {
+ return null;
+ }
+}
diff --git a/test/542-unresolved-access-check/src/p1/PlaceHolder.java b/test/542-unresolved-access-check/src/p1/PlaceHolder.java
new file mode 100644
index 0000000..2bf4bdf
--- /dev/null
+++ b/test/542-unresolved-access-check/src/p1/PlaceHolder.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package p1;
+
+// Specific class for putting the 'entered' marker. If we were to put the marker
+// in InP1 or in OtherInP1, the code in MyClassLoader using that marker would load
+// InP1 or OtherInP1 in the system class loader, and not in MyClassLoader.
+public class PlaceHolder {
+ public static boolean entered = false;
+}
diff --git a/test/962-iface-static/smali/Displayer.smali b/test/962-iface-static/smali/Displayer.smali
index 06bec16..ed4c013 100644
--- a/test/962-iface-static/smali/Displayer.smali
+++ b/test/962-iface-static/smali/Displayer.smali
@@ -27,7 +27,7 @@
.class public LDisplayer;
.super Ljava/lang/Object;
-.method public static <clinit>()V
+.method static constructor <clinit>()V
.locals 3
sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v0, "init"
diff --git a/test/962-iface-static/smali/iface.smali b/test/962-iface-static/smali/iface.smali
index 441aae6..5b9c03e 100644
--- a/test/962-iface-static/smali/iface.smali
+++ b/test/962-iface-static/smali/iface.smali
@@ -27,7 +27,7 @@
.field public final static f:LDisplayer;
-.method public static <clinit>()V
+.method static constructor <clinit>()V
.locals 3
new-instance v1, LDisplayer;
invoke-direct {v1}, LDisplayer;-><init>()V
diff --git a/test/964-default-iface-init-generated/util-src/generate_smali.py b/test/964-default-iface-init-generated/util-src/generate_smali.py
index be2d3ba..3c138ab 100755
--- a/test/964-default-iface-init-generated/util-src/generate_smali.py
+++ b/test/964-default-iface-init-generated/util-src/generate_smali.py
@@ -334,7 +334,7 @@
# public static final Displayer field = new Displayer("{tree}");
.field public final static field:LDisplayer;
-.method public static constructor <clinit>()V
+.method static constructor <clinit>()V
.locals 3
const-string v2, "{tree}"
new-instance v1, LDisplayer;