merge in nyc-release history after reset to nyc-dev
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 03c94a4..d87762d 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -80,15 +80,6 @@
return (it != verified_methods_.end()) ? it->second : nullptr;
}
-void VerificationResults::RemoveVerifiedMethod(MethodReference ref) {
- WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
- auto it = verified_methods_.find(ref);
- if (it != verified_methods_.end()) {
- delete it->second;
- verified_methods_.erase(it);
- }
-}
-
void VerificationResults::AddRejectedClass(ClassReference ref) {
{
WriterMutexLock mu(Thread::Current(), rejected_classes_lock_);
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index da80bf0..1af11a8 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -48,7 +48,6 @@
const VerifiedMethod* GetVerifiedMethod(MethodReference ref)
REQUIRES(!verified_methods_lock_);
- void RemoveVerifiedMethod(MethodReference ref) REQUIRES(!verified_methods_lock_);
void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_);
bool IsClassRejected(ClassReference ref) REQUIRES(!rejected_classes_lock_);
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 4594e2c..e366e07 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -26,6 +26,7 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
+#include "base/bit_vector.h"
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/time_utils.h"
@@ -66,6 +67,7 @@
#include "thread_pool.h"
#include "trampolines/trampoline_compiler.h"
#include "transaction.h"
+#include "utils/array_ref.h"
#include "utils/dex_cache_arrays_layout-inl.h"
#include "utils/swap_space.h"
#include "verifier/method_verifier.h"
@@ -333,6 +335,24 @@
DISALLOW_COPY_AND_ASSIGN(AOTCompilationStats);
};
+class CompilerDriver::DexFileMethodSet {
+ public:
+ explicit DexFileMethodSet(const DexFile& dex_file)
+ : dex_file_(dex_file),
+ method_indexes_(dex_file.NumMethodIds(), false, Allocator::GetMallocAllocator()) {
+ }
+ DexFileMethodSet(DexFileMethodSet&& other) = default;
+
+ const DexFile& GetDexFile() const { return dex_file_; }
+
+ BitVector& GetMethodIndexes() { return method_indexes_; }
+ const BitVector& GetMethodIndexes() const { return method_indexes_; }
+
+ private:
+ const DexFile& dex_file_;
+ BitVector method_indexes_;
+};
+
CompilerDriver::CompilerDriver(
const CompilerOptions* compiler_options,
VerificationResults* verification_results,
@@ -379,7 +399,10 @@
dex_files_for_oat_file_(nullptr),
compiled_method_storage_(swap_fd),
profile_compilation_info_(profile_compilation_info),
- max_arena_alloc_(0) {
+ max_arena_alloc_(0),
+ dex_to_dex_references_lock_("dex-to-dex references lock"),
+ dex_to_dex_references_(),
+ current_dex_to_dex_methods_(nullptr) {
DCHECK(compiler_options_ != nullptr);
DCHECK(method_inliner_map_ != nullptr);
@@ -552,7 +575,29 @@
uint64_t start_ns = kTimeCompileMethod ? NanoTime() : 0;
MethodReference method_ref(&dex_file, method_idx);
- if ((access_flags & kAccNative) != 0) {
+ if (driver->GetCurrentDexToDexMethods() != nullptr) {
+ // This is the second pass when we dex-to-dex compile previously marked methods.
+ // TODO: Refactor the compilation to avoid having to distinguish the two passes
+ // here. That should be done on a higher level. http://b/29089975
+ if (driver->GetCurrentDexToDexMethods()->IsBitSet(method_idx)) {
+ const VerifiedMethod* verified_method =
+ driver->GetVerificationResults()->GetVerifiedMethod(method_ref);
+ // Do not optimize if a VerifiedMethod is missing. SafeCast elision,
+ // for example, relies on it.
+ compiled_method = optimizer::ArtCompileDEX(
+ driver,
+ code_item,
+ access_flags,
+ invoke_type,
+ class_def_idx,
+ method_idx,
+ class_loader,
+ dex_file,
+ (verified_method != nullptr)
+ ? dex_to_dex_compilation_level
+ : optimizer::DexToDexCompilationLevel::kRequired);
+ }
+ } else if ((access_flags & kAccNative) != 0) {
// Are we extracting only and have support for generic JNI down calls?
if (!driver->GetCompilerOptions().IsJniCompilationEnabled() &&
InstructionSetHasGenericJniStub(driver->GetInstructionSet())) {
@@ -588,21 +633,9 @@
}
if (compiled_method == nullptr &&
dex_to_dex_compilation_level != optimizer::DexToDexCompilationLevel::kDontDexToDexCompile) {
+ DCHECK(!Runtime::Current()->UseJitCompilation());
// TODO: add a command-line option to disable DEX-to-DEX compilation ?
- // Do not optimize if a VerifiedMethod is missing. SafeCast elision, for example, relies on
- // it.
- compiled_method = optimizer::ArtCompileDEX(
- driver,
- code_item,
- access_flags,
- invoke_type,
- class_def_idx,
- method_idx,
- class_loader,
- dex_file,
- (verified_method != nullptr)
- ? dex_to_dex_compilation_level
- : optimizer::DexToDexCompilationLevel::kRequired);
+ driver->MarkForDexToDexCompilation(self, method_ref);
}
}
if (kTimeCompileMethod) {
@@ -628,12 +661,6 @@
driver->AddCompiledMethod(method_ref, compiled_method, non_relative_linker_patch_count);
}
- // Done compiling, delete the verified method to reduce native memory usage. Do not delete in
- // optimizing compiler, which may need the verified method again for inlining.
- if (driver->GetCompilerKind() != Compiler::kOptimizing) {
- driver->GetVerificationResults()->RemoveVerifiedMethod(method_ref);
- }
-
if (self->IsExceptionPending()) {
ScopedObjectAccess soa(self);
LOG(FATAL) << "Unexpected exception compiling: " << PrettyMethod(method_idx, dex_file) << "\n"
@@ -680,6 +707,7 @@
*dex_file,
dex_file->GetClassDef(class_def_idx));
+ DCHECK(current_dex_to_dex_methods_ == nullptr);
CompileMethod(self,
this,
code_item,
@@ -693,6 +721,34 @@
true,
dex_cache);
+ ArrayRef<DexFileMethodSet> dex_to_dex_references;
+ {
+ // From this point on, we shall not modify dex_to_dex_references_, so
+ // just grab a reference to it that we use without holding the mutex.
+ MutexLock lock(Thread::Current(), dex_to_dex_references_lock_);
+ dex_to_dex_references = ArrayRef<DexFileMethodSet>(dex_to_dex_references_);
+ }
+ if (!dex_to_dex_references.empty()) {
+ DCHECK_EQ(dex_to_dex_references.size(), 1u);
+ DCHECK(&dex_to_dex_references[0].GetDexFile() == dex_file);
+ current_dex_to_dex_methods_ = &dex_to_dex_references.front().GetMethodIndexes();
+ DCHECK(current_dex_to_dex_methods_->IsBitSet(method_idx));
+ DCHECK_EQ(current_dex_to_dex_methods_->NumSetBits(), 1u);
+ CompileMethod(self,
+ this,
+ code_item,
+ access_flags,
+ invoke_type,
+ class_def_idx,
+ method_idx,
+ jclass_loader,
+ *dex_file,
+ dex_to_dex_compilation_level,
+ true,
+ dex_cache);
+ current_dex_to_dex_methods_ = nullptr;
+ }
+
FreeThreadPools();
self->GetJniEnv()->DeleteGlobalRef(jclass_loader);
@@ -1285,6 +1341,17 @@
return IsImageClass(descriptor);
}
+void CompilerDriver::MarkForDexToDexCompilation(Thread* self, const MethodReference& method_ref) {
+ MutexLock lock(self, dex_to_dex_references_lock_);
+ // Since we're compiling one dex file at a time, we need to look for the
+ // current dex file entry only at the end of dex_to_dex_references_.
+ if (dex_to_dex_references_.empty() ||
+ &dex_to_dex_references_.back().GetDexFile() != method_ref.dex_file) {
+ dex_to_dex_references_.emplace_back(*method_ref.dex_file);
+ }
+ dex_to_dex_references_.back().GetMethodIndexes().SetBit(method_ref.dex_method_index);
+}
+
bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(Handle<mirror::DexCache> dex_cache,
uint32_t type_idx) {
bool result = false;
@@ -2496,8 +2563,9 @@
? "null"
: profile_compilation_info_->DumpInfo(&dex_files));
}
- for (size_t i = 0; i != dex_files.size(); ++i) {
- const DexFile* dex_file = dex_files[i];
+
+ DCHECK(current_dex_to_dex_methods_ == nullptr);
+ for (const DexFile* dex_file : dex_files) {
CHECK(dex_file != nullptr);
CompileDexFile(class_loader,
*dex_file,
@@ -2510,6 +2578,25 @@
max_arena_alloc_ = std::max(arena_alloc, max_arena_alloc_);
Runtime::Current()->ReclaimArenaPoolMemory();
}
+
+ ArrayRef<DexFileMethodSet> dex_to_dex_references;
+ {
+ // From this point on, we shall not modify dex_to_dex_references_, so
+ // just grab a reference to it that we use without holding the mutex.
+ MutexLock lock(Thread::Current(), dex_to_dex_references_lock_);
+ dex_to_dex_references = ArrayRef<DexFileMethodSet>(dex_to_dex_references_);
+ }
+ for (const auto& method_set : dex_to_dex_references) {
+ current_dex_to_dex_methods_ = &method_set.GetMethodIndexes();
+ CompileDexFile(class_loader,
+ method_set.GetDexFile(),
+ dex_files,
+ parallel_thread_pool_.get(),
+ parallel_thread_count_,
+ timings);
+ }
+ current_dex_to_dex_methods_ = nullptr;
+
VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
}
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 19a1ecc..2dd4651 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -52,6 +52,7 @@
class MethodVerifier;
} // namespace verifier
+class BitVector;
class CompiledClass;
class CompiledMethod;
class CompilerOptions;
@@ -120,12 +121,12 @@
void CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings)
- REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
+ REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_);
// Compile a single Method.
void CompileOne(Thread* self, ArtMethod* method, TimingLogger* timings)
SHARED_REQUIRES(Locks::mutator_lock_)
- REQUIRES(!compiled_methods_lock_, !compiled_classes_lock_);
+ REQUIRES(!compiled_methods_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_);
VerificationResults* GetVerificationResults() const {
DCHECK(Runtime::Current()->IsAotCompiler());
@@ -475,6 +476,13 @@
return true;
}
+ void MarkForDexToDexCompilation(Thread* self, const MethodReference& method_ref)
+ REQUIRES(!dex_to_dex_references_lock_);
+
+ const BitVector* GetCurrentDexToDexMethods() const {
+ return current_dex_to_dex_methods_;
+ }
+
private:
// Return whether the declaring class of `resolved_member` is
// available to `referrer_class` for read or write access using two
@@ -601,7 +609,7 @@
void Compile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings);
+ TimingLogger* timings) REQUIRES(!dex_to_dex_references_lock_);
void CompileDexFile(jobject class_loader,
const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
@@ -702,6 +710,16 @@
const ProfileCompilationInfo* const profile_compilation_info_;
size_t max_arena_alloc_;
+
+ // Data for delaying dex-to-dex compilation.
+ Mutex dex_to_dex_references_lock_;
+ // In the first phase, dex_to_dex_references_ collects methods for dex-to-dex compilation.
+ class DexFileMethodSet;
+ std::vector<DexFileMethodSet> dex_to_dex_references_ GUARDED_BY(dex_to_dex_references_lock_);
+ // In the second phase, current_dex_to_dex_methods_ points to the BitVector with method
+ // indexes for dex-to-dex compilation in the current dex file.
+ const BitVector* current_dex_to_dex_methods_;
+
friend class CompileClassVisitor;
DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
};
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 37197af..0e0a30b 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -321,6 +321,7 @@
jobject class_loader,
const DexFile& dex_file,
Handle<mirror::DexCache> dex_cache,
+ ArtMethod* method,
bool osr) const;
std::unique_ptr<OptimizingCompilerStats> compilation_stats_;
@@ -614,6 +615,7 @@
jobject class_loader,
const DexFile& dex_file,
Handle<mirror::DexCache> dex_cache,
+ ArtMethod* method,
bool osr) const {
MaybeRecordStat(MethodCompilationStat::kAttemptCompilation);
CompilerDriver* compiler_driver = GetCompilerDriver();
@@ -679,18 +681,21 @@
osr);
const uint8_t* interpreter_metadata = nullptr;
- {
+ if (method == nullptr) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<1> hs(soa.Self());
Handle<mirror::ClassLoader> loader(hs.NewHandle(
soa.Decode<mirror::ClassLoader*>(class_loader)));
- ArtMethod* art_method = compiler_driver->ResolveMethod(
+ method = compiler_driver->ResolveMethod(
soa, dex_cache, loader, &dex_compilation_unit, method_idx, invoke_type);
- // We may not get a method, for example if its class is erroneous.
- if (art_method != nullptr) {
- graph->SetArtMethod(art_method);
- interpreter_metadata = art_method->GetQuickenedInfo();
- }
+ }
+ // For AOT compilation, we may not get a method, for example if its class is erroneous.
+ // JIT should always have a method.
+ DCHECK(Runtime::Current()->IsAotCompiler() || method != nullptr);
+ if (method != nullptr) {
+ graph->SetArtMethod(method);
+ ScopedObjectAccess soa(Thread::Current());
+ interpreter_metadata = method->GetQuickenedInfo();
}
std::unique_ptr<CodeGenerator> codegen(
@@ -798,6 +803,7 @@
jclass_loader,
dex_file,
dex_cache,
+ nullptr,
/* osr */ false));
if (codegen.get() != nullptr) {
MaybeRecordStat(MethodCompilationStat::kCompiled);
@@ -884,6 +890,7 @@
jclass_loader,
*dex_file,
dex_cache,
+ method,
osr));
if (codegen.get() == nullptr) {
return false;
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index f2394f6..15f4928 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -809,7 +809,11 @@
// Make sure that we don't go over the bounded type.
ReferenceTypeInfo upper_bound_rti = instr->GetUpperBound();
if (!upper_bound_rti.IsSupertypeOf(new_rti)) {
- new_rti = upper_bound_rti;
+ // Note that the input might be exact, in which case we know the branch leading
+ // to the bound type is dead. We play it safe by not marking the bound type as
+ // exact.
+ bool is_exact = upper_bound_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes();
+ new_rti = ReferenceTypeInfo::Create(upper_bound_rti.GetTypeHandle(), is_exact);
}
instr->SetReferenceTypeInfo(new_rti);
}
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 1790df6..f86cb13 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -79,10 +79,10 @@
InvokeType ArtMethod::GetInvokeType() {
// TODO: kSuper?
- if (GetDeclaringClass()->IsInterface()) {
- return kInterface;
- } else if (IsStatic()) {
+ if (IsStatic()) {
return kStatic;
+ } else if (GetDeclaringClass()->IsInterface()) {
+ return kInterface;
} else if (IsDirect()) {
return kDirect;
} else {
diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h
index 424ebb7..5609067 100644
--- a/runtime/base/bit_vector.h
+++ b/runtime/base/bit_vector.h
@@ -111,6 +111,20 @@
const BitVector* const bit_vector_;
};
+ // MoveConstructible but not MoveAssignable, CopyConstructible or CopyAssignable.
+
+ BitVector(const BitVector& other) = delete;
+ BitVector& operator=(const BitVector& other) = delete;
+
+ BitVector(BitVector&& other)
+ : storage_(other.storage_),
+ storage_size_(other.storage_size_),
+ allocator_(other.allocator_),
+ expandable_(other.expandable_) {
+ other.storage_ = nullptr;
+ other.storage_size_ = 0u;
+ }
+
BitVector(uint32_t start_bits,
bool expandable,
Allocator* allocator);
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 9822f6e..794ea27 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -74,19 +74,6 @@
total_number_of_hot_spikes_(0),
total_number_of_wake_ups_(0) {
AddTrackedLocations(output_filename, app_data_dir, code_paths);
- if (!app_data_dir.empty()) {
- // The application directory is used to determine which dex files are owned by app.
- // Since it could be a symlink (e.g. /data/data instead of /data/user/0), and we
- // don't have control over how the dex files are actually loaded (symlink or canonical path),
- // store it's canonical form to be sure we use the same base when comparing.
- UniqueCPtr<const char[]> app_data_dir_real_path(realpath(app_data_dir.c_str(), nullptr));
- if (app_data_dir_real_path != nullptr) {
- app_data_dirs_.emplace(app_data_dir_real_path.get());
- } else {
- LOG(WARNING) << "Failed to get the real path for app dir: " << app_data_dir
- << ". The app dir will not be used to determine which dex files belong to the app";
- }
- }
}
void ProfileSaver::Run() {
@@ -503,12 +490,18 @@
if (it == tracked_dex_base_locations_.end()) {
tracked_dex_base_locations_.Put(output_filename,
std::set<std::string>(code_paths.begin(), code_paths.end()));
- app_data_dirs_.insert(app_data_dir);
+ if (!app_data_dir.empty()) {
+ app_data_dirs_.insert(app_data_dir);
+ }
} else {
it->second.insert(code_paths.begin(), code_paths.end());
}
}
+// TODO(calin): This may lead to several calls to realpath.
+// Consider moving the logic to the saver thread (i.e. when notified,
+// only cache the location, and then wake up the saver thread to do the
+// comparisons with the real file paths and to create the markers).
void ProfileSaver::NotifyDexUse(const std::string& dex_location) {
if (!ShouldProfileLocation(dex_location)) {
return;
@@ -541,63 +534,32 @@
}
}
-bool ProfileSaver::MaybeRecordDexUseInternal(
- const std::string& dex_location,
- const std::set<std::string>& app_code_paths,
- const std::string& foreign_dex_profile_path,
- const std::set<std::string>& app_data_dirs) {
- if (dex_location.empty()) {
- LOG(WARNING) << "Asked to record foreign dex use with an empty dex location.";
- return false;
- }
- if (foreign_dex_profile_path.empty()) {
- LOG(WARNING) << "Asked to record foreign dex use without a valid profile path ";
- return false;
- }
-
- UniqueCPtr<const char[]> dex_location_real_path(realpath(dex_location.c_str(), nullptr));
- if (dex_location_real_path == nullptr) {
- PLOG(WARNING) << "Could not get realpath for " << dex_location;
- }
- std::string dex_location_real_path_str((dex_location_real_path == nullptr)
- ? dex_location.c_str()
- : dex_location_real_path.get());
-
- if (app_data_dirs.find(dex_location_real_path_str) != app_data_dirs.end()) {
- // The dex location is under the application folder. Nothing to record.
- return false;
- }
-
- if (app_code_paths.find(dex_location) != app_code_paths.end()) {
- // The dex location belongs to the application code paths. Nothing to record.
- return false;
- }
- // Do another round of checks with the real paths.
- // Note that we could cache all the real locations in the saver (since it's an expensive
- // operation). However we expect that app_code_paths is small (usually 1 element), and
- // NotifyDexUse is called just a few times in the app lifetime. So we make the compromise
- // to save some bytes of memory usage.
- for (const auto& app_code_location : app_code_paths) {
- UniqueCPtr<const char[]> real_app_code_location(realpath(app_code_location.c_str(), nullptr));
- if (real_app_code_location == nullptr) {
- PLOG(WARNING) << "Could not get realpath for " << app_code_location;
+static bool CheckContainsWithRealPath(const std::set<std::string>& paths_set,
+ const std::string& path_to_check) {
+ for (const auto& path : paths_set) {
+ UniqueCPtr<const char[]> real_path(realpath(path.c_str(), nullptr));
+ if (real_path == nullptr) {
+ PLOG(WARNING) << "Could not get realpath for " << path;
+ continue;
}
- std::string real_app_code_location_str((real_app_code_location == nullptr)
- ? app_code_location.c_str()
- : real_app_code_location.get());
- if (real_app_code_location_str == dex_location_real_path_str) {
- // The dex location belongs to the application code paths. Nothing to record.
- return false;
+ std::string real_path_str(real_path.get());
+ if (real_path_str == path_to_check) {
+ return true;
}
}
+ return false;
+}
+// After the call, dex_location_real_path will contain the marker's name.
+static bool CreateForeignDexMarker(const std::string& foreign_dex_profile_path,
+ /*in-out*/ std::string* dex_location_real_path) {
// For foreign dex files we record a flag on disk. PackageManager will (potentially) take this
// into account when deciding how to optimize the loaded dex file.
// The expected flag name is the canonical path of the apk where '/' is substituted to '@'.
// (it needs to be kept in sync with
// frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java)
- std::replace(dex_location_real_path_str.begin(), dex_location_real_path_str.end(), '/', '@');
- std::string flag_path = foreign_dex_profile_path + "/" + dex_location_real_path_str;
+ std::replace(dex_location_real_path->begin(), dex_location_real_path->end(), '/', '@');
+ std::string flag_path = foreign_dex_profile_path + "/" + *dex_location_real_path;
// We use O_RDONLY as the access mode because we must supply some access
// mode, and there is no access mode that means 'create but do not read' the
// file. We will not not actually read from the file.
@@ -619,6 +581,57 @@
}
}
+bool ProfileSaver::MaybeRecordDexUseInternal(
+ const std::string& dex_location,
+ const std::set<std::string>& app_code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::set<std::string>& app_data_dirs) {
+ if (dex_location.empty()) {
+ LOG(WARNING) << "Asked to record foreign dex use with an empty dex location.";
+ return false;
+ }
+ if (foreign_dex_profile_path.empty()) {
+ LOG(WARNING) << "Asked to record foreign dex use without a valid profile path ";
+ return false;
+ }
+
+ if (app_code_paths.find(dex_location) != app_code_paths.end()) {
+ // The dex location belongs to the application code paths. Nothing to record.
+ return false;
+ }
+
+ if (app_data_dirs.find(dex_location) != app_data_dirs.end()) {
+ // The dex location is under the application folder. Nothing to record.
+ return false;
+ }
+
+ // Do another round of checks with the real paths.
+ // Application directory could be a symlink (e.g. /data/data instead of /data/user/0), and we
+ // don't have control over how the dex files are actually loaded (symlink or canonical path),
+
+ // Note that we could cache all the real locations in the saver (since it's an expensive
+ // operation). However we expect that app_code_paths is small (usually 1 element), and
+ // NotifyDexUse is called just a few times in the app lifetime. So we make the compromise
+ // to save some bytes of memory usage.
+
+ UniqueCPtr<const char[]> dex_location_real_path(realpath(dex_location.c_str(), nullptr));
+ if (dex_location_real_path == nullptr) {
+ PLOG(WARNING) << "Could not get realpath for " << dex_location;
+ return false;
+ }
+ std::string dex_location_real_path_str(dex_location_real_path.get());
+
+ if (CheckContainsWithRealPath(app_code_paths, dex_location_real_path_str)) {
+ return false;
+ }
+
+ if (CheckContainsWithRealPath(app_data_dirs, dex_location_real_path_str)) {
+ return false;
+ }
+
+ return CreateForeignDexMarker(foreign_dex_profile_path, &dex_location_real_path_str);
+}
+
void ProfileSaver::DumpInstanceInfo(std::ostream& os) {
MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
if (instance_ != nullptr) {
diff --git a/test/603-checker-instanceof/expected.txt b/test/603-checker-instanceof/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/603-checker-instanceof/expected.txt
diff --git a/test/603-checker-instanceof/info.txt b/test/603-checker-instanceof/info.txt
new file mode 100644
index 0000000..5907abc
--- /dev/null
+++ b/test/603-checker-instanceof/info.txt
@@ -0,0 +1,2 @@
+Regression test for the compiler that used to wrongly optimize
+an instanceof.
diff --git a/test/603-checker-instanceof/src/Main.java b/test/603-checker-instanceof/src/Main.java
new file mode 100644
index 0000000..ddf4b92
--- /dev/null
+++ b/test/603-checker-instanceof/src/Main.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+class SuperClass {
+}
+
+class ChildClass extends SuperClass {
+}
+
+public class Main {
+
+ /// CHECK-START: void Main.main(java.lang.String[]) builder (after)
+ /// CHECK: BoundType klass:SuperClass can_be_null:false exact:false
+
+ /// CHECK-START: void Main.main(java.lang.String[]) builder (after)
+ /// CHECK-NOT: BoundType klass:SuperClass can_be_null:false exact:true
+ public static void main(String[] args) {
+ Object obj = new ChildClass();
+
+ // We need a fixed point iteration to hit the bogus type update
+ // of 'obj' below, so create a loop that updates the type of 'obj'.
+ for (int i = 1; i < 1; i++) {
+ obj = new Object();
+ }
+
+ if (obj instanceof SuperClass) {
+ // We used to wrongly type obj as an exact SuperClass from this point,
+ // meaning we were statically determining that the following instanceof
+ // would always fail.
+ if (!(obj instanceof ChildClass)) {
+ throw new Error("Expected a ChildClass, got " + obj.getClass());
+ }
+ }
+ }
+}
diff --git a/test/604-hot-static-interface/build b/test/604-hot-static-interface/build
new file mode 100755
index 0000000..1ca2daf
--- /dev/null
+++ b/test/604-hot-static-interface/build
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ != *"--jvm"* ]]; then
+ # Don't do anything with jvm
+ # Hard-wired use of experimental jack.
+ # TODO: fix this temporary work-around for default-methods, see b/19467889
+ export USE_JACK=true
+fi
+
+./default-build "$@" --experimental default-methods
diff --git a/test/604-hot-static-interface/expected.txt b/test/604-hot-static-interface/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/604-hot-static-interface/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/604-hot-static-interface/hot_static_interface.cc b/test/604-hot-static-interface/hot_static_interface.cc
new file mode 100644
index 0000000..9c51ca6
--- /dev/null
+++ b/test/604-hot-static-interface/hot_static_interface.cc
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#include "art_method.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
+#include "oat_quick_method_header.h"
+#include "scoped_thread_state_change.h"
+#include "ScopedUtfChars.h"
+#include "stack_map.h"
+
+namespace art {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_waitUntilJitted(JNIEnv* env,
+ jclass,
+ jclass itf,
+ jstring method_name) {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit == nullptr) {
+ return;
+ }
+
+ ScopedObjectAccess soa(Thread::Current());
+
+ ScopedUtfChars chars(env, method_name);
+ CHECK(chars.c_str() != nullptr);
+
+ mirror::Class* klass = soa.Decode<mirror::Class*>(itf);
+ ArtMethod* method = klass->FindDeclaredDirectMethodByName(chars.c_str(), sizeof(void*));
+
+ jit::JitCodeCache* code_cache = jit->GetCodeCache();
+ OatQuickMethodHeader* header = nullptr;
+ while (true) {
+ header = OatQuickMethodHeader::FromEntryPoint(method->GetEntryPointFromQuickCompiledCode());
+ if (code_cache->ContainsPc(header->GetCode())) {
+ break;
+ } else {
+ // yield to scheduler to give time to the JIT compiler.
+ sched_yield();
+ }
+ }
+}
+
+} // namespace art
diff --git a/test/604-hot-static-interface/info.txt b/test/604-hot-static-interface/info.txt
new file mode 100644
index 0000000..bc00bda
--- /dev/null
+++ b/test/604-hot-static-interface/info.txt
@@ -0,0 +1,2 @@
+Regression test for the JIT that used to crash when compiling
+a static method of an interface.
diff --git a/test/604-hot-static-interface/src/Main.java b/test/604-hot-static-interface/src/Main.java
new file mode 100644
index 0000000..559f15d
--- /dev/null
+++ b/test/604-hot-static-interface/src/Main.java
@@ -0,0 +1,39 @@
+/*
+ * 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 main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ // Loop enough to get Itf.foo JITed.
+ for (int i = 0; i < 100000; i++) {
+ Itf.foo(new Object());
+ }
+
+ waitUntilJitted(Itf.class, "foo");
+
+ if (!Itf.foo(new Object())) {
+ throw new Error("Unexpected result");
+ }
+ }
+
+ private static native void waitUntilJitted(Class itf, String method_name);
+}
+
+interface Itf {
+ public static boolean foo(Object o) {
+ return o.equals(o);
+ }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 5b1af27..6d40cdf 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -44,7 +44,8 @@
566-polymorphic-inlining/polymorphic_inline.cc \
570-checker-osr/osr.cc \
595-profile-saving/profile-saving.cc \
- 597-deopt-new-string/deopt.cc
+ 597-deopt-new-string/deopt.cc \
+ 604-hot-static-interface/hot_static_interface.cc
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so