Merge "Mark __jit_debug_register_code as a unique symbol"
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index a93d8a8..43e1457 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -206,6 +206,7 @@
ART_C_INCLUDES := \
external/gtest/include \
external/icu/icu4c/source/common \
+ external/lz4/lib \
external/valgrind/include \
external/valgrind \
external/vixl/src \
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
index 3b2d1cc..cb6d340 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -201,6 +201,9 @@
# $(5): library dependencies (host only)
# $(6): extra include directories
# $(7): multilib (default: empty), valid values: {,32,64,both})
+# $(8): host prefer 32-bit: {true, false} (default: false). If argument
+# `multilib` is explicitly set to 64, ignore the "host prefer 32-bit"
+# setting and only build a 64-bit executable on host.
define build-art-multi-executable
$(foreach debug_flavor,ndebug debug,
$(foreach target_flavor,host target,
@@ -211,6 +214,7 @@
art-multi-lib-dependencies-host := $(5)
art-multi-include-extra := $(6)
art-multi-multilib := $(7)
+ art-multi-host-prefer-32-bit := $(8)
# Add either -host or -target specific lib dependencies to the lib dependencies.
art-multi-lib-dependencies += $$(art-multi-lib-dependencies-$(target_flavor))
@@ -223,6 +227,14 @@
# Build the env guard var name, e.g. ART_BUILD_HOST_NDEBUG.
art-multi-env-guard := $$(call art-string-to-uppercase,ART_BUILD_$(target_flavor)_$(debug_flavor))
+ ifeq ($(target_flavor),host)
+ ifeq ($$(art-multi-host-prefer-32-bit),true)
+ ifneq ($$(art-multi-multilib),64)
+ art-multi-multilib := 32
+ endif
+ endif
+ endif
+
# Build the art executable only if the corresponding env guard was set.
ifeq ($$($$(art-multi-env-guard)),true)
$$(eval $$(call build-art-executable,$$(art-multi-binary-name),$$(art-multi-source-files),$$(art-multi-lib-dependencies),$$(art-multi-include-extra),$(target_flavor),$(debug_flavor),$$(art-multi-multilib)))
@@ -236,6 +248,7 @@
art-multi-lib-dependencies-host :=
art-multi-include-extra :=
art-multi-multilib :=
+ art-multi-host-prefer-32-bit :=
art-multi-env-guard :=
)
)
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 348eabd..bdd9a84 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -258,9 +258,9 @@
ifeq ($$(art_ndebug_or_debug),ndebug)
LOCAL_MODULE := libart-compiler
ifeq ($$(art_static_or_shared), static)
- LOCAL_STATIC_LIBRARIES += libart
+ LOCAL_STATIC_LIBRARIES += libart liblz4
else
- LOCAL_SHARED_LIBRARIES += libart
+ LOCAL_SHARED_LIBRARIES += libart liblz4
endif
ifeq ($$(art_target_or_host),target)
LOCAL_FDO_SUPPORT := true
@@ -268,9 +268,9 @@
else # debug
LOCAL_MODULE := libartd-compiler
ifeq ($$(art_static_or_shared), static)
- LOCAL_STATIC_LIBRARIES += libartd
+ LOCAL_STATIC_LIBRARIES += libartd liblz4
else
- LOCAL_SHARED_LIBRARIES += libartd
+ LOCAL_SHARED_LIBRARIES += libartd liblz4
endif
endif
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index e6cc50c..638b897 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -189,17 +189,29 @@
}
timer_.reset(new CumulativeLogger("Compilation times"));
- compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
- verification_results_.get(),
- method_inliner_map_.get(),
- compiler_kind_, instruction_set,
- instruction_set_features_.get(),
- true,
- GetImageClasses(),
- GetCompiledClasses(),
- GetCompiledMethods(),
- 2, true, true, "", false, timer_.get(), -1, ""));
+ CreateCompilerDriver(compiler_kind_, instruction_set);
}
+}
+
+void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa) {
+ compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
+ verification_results_.get(),
+ method_inliner_map_.get(),
+ kind,
+ isa,
+ instruction_set_features_.get(),
+ true,
+ GetImageClasses(),
+ GetCompiledClasses(),
+ GetCompiledMethods(),
+ 2,
+ true,
+ true,
+ "",
+ false,
+ timer_.get(),
+ -1,
+ ""));
// We typically don't generate an image in unit tests, disable this optimization by default.
compiler_driver_->SetSupportBootImageFixup(false);
}
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index 1b57b7d..b491946 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -90,6 +90,8 @@
const char* method_name, const char* signature)
SHARED_REQUIRES(Locks::mutator_lock_);
+ void CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa);
+
void ReserveImageSpace();
void UnreserveImageSpace();
diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc
index 39f8ee8..18ce563 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -1430,8 +1430,4 @@
method_lowering_infos_.data(), count);
}
-bool MIRGraph::SkipCompilationByName(const std::string& methodname) {
- return cu_->compiler_driver->SkipCompilation(methodname);
-}
-
} // namespace art
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 2da8a98..3191fe9 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -564,11 +564,6 @@
bool SkipCompilation(std::string* skip_message);
/*
- * Should we skip the compilation of this method based on its name?
- */
- bool SkipCompilationByName(const std::string& methodname);
-
- /*
* Parse dex method and add MIR at current insert point. Returns id (which is
* actually the index of the method in the m_units_ array).
*/
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index eaf2408..f48947d 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -50,6 +50,23 @@
true, // kIntrinsicMinMaxLong
true, // kIntrinsicMinMaxFloat
true, // kIntrinsicMinMaxDouble
+ true, // kIntrinsicCos
+ true, // kIntrinsicSin
+ true, // kIntrinsicAcos
+ true, // kIntrinsicAsin
+ true, // kIntrinsicAtan
+ true, // kIntrinsicAtan2
+ true, // kIntrinsicCbrt
+ true, // kIntrinsicCosh
+ true, // kIntrinsicExp
+ true, // kIntrinsicExpm1
+ true, // kIntrinsicHypot
+ true, // kIntrinsicLog
+ true, // kIntrinsicLog10
+ true, // kIntrinsicNextAfter
+ true, // kIntrinsicSinh
+ true, // kIntrinsicTan
+ true, // kIntrinsicTanh
true, // kIntrinsicSqrt
true, // kIntrinsicCeil
true, // kIntrinsicFloor
@@ -95,6 +112,23 @@
static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxLong], "MinMaxLong_must_be_static");
static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxFloat], "MinMaxFloat_must_be_static");
static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxDouble], "MinMaxDouble_must_be_static");
+static_assert(kIntrinsicIsStatic[kIntrinsicCos], "Cos must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicSin], "Sin must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicAcos], "Acos must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicAsin], "Asin must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicAtan], "Atan must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicAtan2], "Atan2 must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicCbrt], "Cbrt must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicCosh], "Cosh must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicExp], "Exp must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicExpm1], "Expm1 must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicHypot], "Hypot must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicLog], "Log must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicLog10], "Log10 must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicNextAfter], "NextAfter must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicSinh], "Sinh must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicTan], "Tan must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicTanh], "Tanh must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicSqrt], "Sqrt must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicCeil], "Ceil must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicFloor], "Floor must be static");
@@ -196,6 +230,23 @@
"abs", // kNameCacheAbs
"max", // kNameCacheMax
"min", // kNameCacheMin
+ "cos", // kNameCacheCos
+ "sin", // kNameCacheSin
+ "acos", // kNameCacheAcos
+ "asin", // kNameCacheAsin
+ "atan", // kNameCacheAtan
+ "atan2", // kNameCacheAtan2
+ "cbrt", // kNameCacheCbrt
+ "cosh", // kNameCacheCosh
+ "exp", // kNameCacheExp
+ "expm1", // kNameCacheExpm1
+ "hypot", // kNameCacheHypot
+ "log", // kNameCacheLog
+ "log10", // kNameCacheLog10
+ "nextAfter", // kNameCacheNextAfter
+ "sinh", // kNameCacheSinh
+ "tan", // kNameCacheTan
+ "tanh", // kNameCacheTanh
"sqrt", // kNameCacheSqrt
"ceil", // kNameCacheCeil
"floor", // kNameCacheFloor
@@ -425,6 +476,23 @@
INTRINSIC(JavaLangMath, Max, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMax),
INTRINSIC(JavaLangStrictMath, Max, DD_D, kIntrinsicMinMaxDouble, kIntrinsicFlagMax),
+ INTRINSIC(JavaLangMath, Cos, D_D, kIntrinsicCos, 0),
+ INTRINSIC(JavaLangMath, Sin, D_D, kIntrinsicSin, 0),
+ INTRINSIC(JavaLangMath, Acos, D_D, kIntrinsicAcos, 0),
+ INTRINSIC(JavaLangMath, Asin, D_D, kIntrinsicAsin, 0),
+ INTRINSIC(JavaLangMath, Atan, D_D, kIntrinsicAtan, 0),
+ INTRINSIC(JavaLangMath, Atan2, DD_D, kIntrinsicAtan2, 0),
+ INTRINSIC(JavaLangMath, Cbrt, D_D, kIntrinsicCbrt, 0),
+ INTRINSIC(JavaLangMath, Cosh, D_D, kIntrinsicCosh, 0),
+ INTRINSIC(JavaLangMath, Exp, D_D, kIntrinsicExp, 0),
+ INTRINSIC(JavaLangMath, Expm1, D_D, kIntrinsicExpm1, 0),
+ INTRINSIC(JavaLangMath, Hypot, DD_D, kIntrinsicHypot, 0),
+ INTRINSIC(JavaLangMath, Log, D_D, kIntrinsicLog, 0),
+ INTRINSIC(JavaLangMath, Log10, D_D, kIntrinsicLog10, 0),
+ INTRINSIC(JavaLangMath, NextAfter, DD_D, kIntrinsicNextAfter, 0),
+ INTRINSIC(JavaLangMath, Sinh, D_D, kIntrinsicSinh, 0),
+ INTRINSIC(JavaLangMath, Tan, D_D, kIntrinsicTan, 0),
+ INTRINSIC(JavaLangMath, Tanh, D_D, kIntrinsicTanh, 0),
INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0),
INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0),
@@ -603,6 +671,25 @@
return backend->GenInlinedMinMaxFP(info, intrinsic.d.data & kIntrinsicFlagMin, false /* is_double */);
case kIntrinsicMinMaxDouble:
return backend->GenInlinedMinMaxFP(info, intrinsic.d.data & kIntrinsicFlagMin, true /* is_double */);
+ case kIntrinsicCos:
+ case kIntrinsicSin:
+ case kIntrinsicAcos:
+ case kIntrinsicAsin:
+ case kIntrinsicAtan:
+ case kIntrinsicAtan2:
+ case kIntrinsicCbrt:
+ case kIntrinsicCosh:
+ case kIntrinsicExp:
+ case kIntrinsicExpm1:
+ case kIntrinsicHypot:
+ case kIntrinsicLog:
+ case kIntrinsicLog10:
+ case kIntrinsicNextAfter:
+ case kIntrinsicSinh:
+ case kIntrinsicTan:
+ case kIntrinsicTanh:
+ // Not implemented in Quick.
+ return false;
case kIntrinsicSqrt:
return backend->GenInlinedSqrt(info);
case kIntrinsicCeil:
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index 5ce110c..ac70577 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -162,6 +162,23 @@
kNameCacheAbs,
kNameCacheMax,
kNameCacheMin,
+ kNameCacheCos,
+ kNameCacheSin,
+ kNameCacheAcos,
+ kNameCacheAsin,
+ kNameCacheAtan,
+ kNameCacheAtan2,
+ kNameCacheCbrt,
+ kNameCacheCosh,
+ kNameCacheExp,
+ kNameCacheExpm1,
+ kNameCacheHypot,
+ kNameCacheLog,
+ kNameCacheLog10,
+ kNameCacheNextAfter,
+ kNameCacheSinh,
+ kNameCacheTan,
+ kNameCacheTanh,
kNameCacheSqrt,
kNameCacheCeil,
kNameCacheFloor,
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index 05dde9f..3260a7a 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -780,14 +780,6 @@
PassDriverMEOpts pass_driver(GetPreOptPassManager(), GetPostOptPassManager(), &cu);
pass_driver.Launch();
- /* For non-leaf methods check if we should skip compilation when the profiler is enabled. */
- if (cu.compiler_driver->ProfilePresent()
- && !cu.mir_graph->MethodIsLeaf()
- && cu.mir_graph->SkipCompilationByName(PrettyMethod(method_idx, dex_file))) {
- cu.EndTiming();
- return nullptr;
- }
-
if (cu.enable_debug & (1 << kDebugDumpCheckStats)) {
cu.mir_graph->DumpCheckStats();
}
diff --git a/compiler/dex/quick_compiler_callbacks.cc b/compiler/dex/quick_compiler_callbacks.cc
index 03bda78..2532bda 100644
--- a/compiler/dex/quick_compiler_callbacks.cc
+++ b/compiler/dex/quick_compiler_callbacks.cc
@@ -22,14 +22,10 @@
namespace art {
-bool QuickCompilerCallbacks::MethodVerified(verifier::MethodVerifier* verifier) {
- bool result = verification_results_->ProcessVerifiedMethod(verifier);
- if (result) {
- MethodReference ref = verifier->GetMethodReference();
- method_inliner_map_->GetMethodInliner(ref.dex_file)
- ->AnalyseMethodCode(verifier);
- }
- return result;
+void QuickCompilerCallbacks::MethodVerified(verifier::MethodVerifier* verifier) {
+ verification_results_->ProcessVerifiedMethod(verifier);
+ MethodReference ref = verifier->GetMethodReference();
+ method_inliner_map_->GetMethodInliner(ref.dex_file)->AnalyseMethodCode(verifier);
}
void QuickCompilerCallbacks::ClassRejected(ClassReference ref) {
diff --git a/compiler/dex/quick_compiler_callbacks.h b/compiler/dex/quick_compiler_callbacks.h
index 03bf57b..4f5ea76 100644
--- a/compiler/dex/quick_compiler_callbacks.h
+++ b/compiler/dex/quick_compiler_callbacks.h
@@ -37,7 +37,7 @@
~QuickCompilerCallbacks() { }
- bool MethodVerified(verifier::MethodVerifier* verifier)
+ void MethodVerified(verifier::MethodVerifier* verifier)
SHARED_REQUIRES(Locks::mutator_lock_) OVERRIDE;
void ClassRejected(ClassReference ref) OVERRIDE;
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 65b0ad6..dd24220 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -44,14 +44,14 @@
}
}
-bool VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
+void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
DCHECK(method_verifier != nullptr);
MethodReference ref = method_verifier->GetMethodReference();
bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags());
const VerifiedMethod* verified_method = VerifiedMethod::Create(method_verifier, compile);
if (verified_method == nullptr) {
- // Do not report an error to the verifier. We'll just punt this later.
- return true;
+ // We'll punt this later.
+ return;
}
WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
@@ -69,11 +69,10 @@
// is unsafe to replace the existing one since the JIT may be using it to generate a
// native GC map.
delete verified_method;
- return true;
+ return;
}
verified_methods_.Put(ref, verified_method);
DCHECK(verified_methods_.find(ref) != verified_methods_.end());
- return true;
}
const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) {
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 9934f6b..da80bf0 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -42,7 +42,7 @@
explicit VerificationResults(const CompilerOptions* compiler_options);
~VerificationResults();
- bool ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
+ void ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!verified_methods_lock_);
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index a05105b..ba8f1d0 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -80,6 +80,9 @@
// given, too all compilations.
static constexpr bool kRestrictCompilationFiltersToImage = true;
+// Print additional info during profile guided compilation.
+static constexpr bool kDebugProfileGuidedCompilation = false;
+
static double Percentage(size_t x, size_t y) {
return 100.0 * (static_cast<double>(x)) / (static_cast<double>(x + y));
}
@@ -344,8 +347,7 @@
const std::string& dump_cfg_file_name, bool dump_cfg_append,
CumulativeLogger* timer, int swap_fd,
const std::string& profile_file)
- : profile_present_(false),
- compiler_options_(compiler_options),
+ : compiler_options_(compiler_options),
verification_results_(verification_results),
method_inliner_map_(method_inliner_map),
compiler_(Compiler::Create(this, compiler_kind)),
@@ -383,12 +385,8 @@
// Read the profile file if one is provided.
if (!profile_file.empty()) {
- profile_present_ = profile_file_.LoadFile(profile_file);
- if (profile_present_) {
- LOG(INFO) << "Using profile data form file " << profile_file;
- } else {
- LOG(INFO) << "Failed to load profile file " << profile_file;
- }
+ profile_compilation_info_.reset(new ProfileCompilationInfo(profile_file));
+ LOG(INFO) << "Using profile data from file " << profile_file;
}
}
@@ -569,7 +567,9 @@
(verified_method->GetEncounteredVerificationFailures() &
(verifier::VERIFY_ERROR_FORCE_INTERPRETER | verifier::VERIFY_ERROR_LOCKING)) == 0 &&
// Is eligable for compilation by methods-to-compile filter.
- driver->IsMethodToCompile(method_ref);
+ driver->IsMethodToCompile(method_ref) &&
+ driver->ShouldCompileBasedOnProfile(method_ref);
+
if (compile) {
// NOTE: if compiler declines to compile this method, it will return null.
compiled_method = driver->GetCompiler()->Compile(code_item, access_flags, invoke_type,
@@ -766,6 +766,22 @@
return methods_to_compile_->find(tmp.c_str()) != methods_to_compile_->end();
}
+bool CompilerDriver::ShouldCompileBasedOnProfile(const MethodReference& method_ref) const {
+ if (profile_compilation_info_ == nullptr) {
+ // If we miss profile information it means that we don't do a profile guided compilation.
+ // Return true, and let the other filters decide if the method should be compiled.
+ return true;
+ }
+ bool result = profile_compilation_info_->ContainsMethod(method_ref);
+
+ if (kDebugProfileGuidedCompilation) {
+ LOG(INFO) << "[ProfileGuidedCompilation] "
+ << (result ? "Compiled" : "Skipped") << " method:"
+ << PrettyMethod(method_ref.dex_method_index, *method_ref.dex_file, true);
+ }
+ return result;
+}
+
class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
public:
ResolveCatchBlockExceptionsClassVisitor(
@@ -1134,15 +1150,21 @@
// See also Compiler::ResolveDexFile
bool result = false;
- if (IsBootImage()) {
- // We resolve all const-string strings when building for the image.
+ if (IsBootImage() || Runtime::Current()->UseJit()) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<1> hs(soa.Self());
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(
soa.Self(), dex_file, false)));
- class_linker->ResolveString(dex_file, string_idx, dex_cache);
- result = true;
+ if (IsBootImage()) {
+ // We resolve all const-string strings when building for the image.
+ class_linker->ResolveString(dex_file, string_idx, dex_cache);
+ result = true;
+ } else {
+ // Just check whether the dex cache already has the string.
+ DCHECK(Runtime::Current()->UseJit());
+ result = (dex_cache->GetResolvedString(string_idx) != nullptr);
+ }
}
if (result) {
stats_->StringInDexCache();
@@ -2037,6 +2059,7 @@
dex_cache,
class_loader,
&class_def,
+ Runtime::Current()->GetCompilerCallbacks(),
true /* allow soft failures */,
true /* log hard failures */,
&error_msg) ==
@@ -2273,6 +2296,16 @@
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();
+ }
+ }
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
CHECK(dex_file != nullptr);
@@ -2510,39 +2543,6 @@
return freezing_constructor_classes_.count(ClassReference(dex_file, class_def_index)) != 0;
}
-bool CompilerDriver::SkipCompilation(const std::string& method_name) {
- if (!profile_present_) {
- return false;
- }
- // First find the method in the profile file.
- ProfileFile::ProfileData data;
- if (!profile_file_.GetProfileData(&data, method_name)) {
- // Not in profile, no information can be determined.
- if (kIsDebugBuild) {
- VLOG(compiler) << "not compiling " << method_name << " because it's not in the profile";
- }
- return true;
- }
-
- // Methods that comprise top_k_threshold % of the total samples will be compiled.
- // Compare against the start of the topK percentage bucket just in case the threshold
- // falls inside a bucket.
- bool compile = data.GetTopKUsedPercentage() - data.GetUsedPercent()
- <= compiler_options_->GetTopKProfileThreshold();
- if (kIsDebugBuild) {
- if (compile) {
- LOG(INFO) << "compiling method " << method_name << " because its usage is part of top "
- << data.GetTopKUsedPercentage() << "% with a percent of " << data.GetUsedPercent() << "%"
- << " (topKThreshold=" << compiler_options_->GetTopKProfileThreshold() << ")";
- } else {
- VLOG(compiler) << "not compiling method " << method_name
- << " because it's not part of leading " << compiler_options_->GetTopKProfileThreshold()
- << "% samples)";
- }
- }
- return !compile;
-}
-
std::string CompilerDriver::GetMemoryUsageString(bool extended) const {
std::ostringstream oss;
Runtime* const runtime = Runtime::Current();
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 1347b37..f0360ce 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -31,11 +31,11 @@
#include "compiler.h"
#include "dex_file.h"
#include "driver/compiled_method_storage.h"
+#include "jit/offline_profiling_info.h"
#include "invoke_type.h"
#include "method_reference.h"
#include "mirror/class.h" // For mirror::Class::Status.
#include "os.h"
-#include "profiler.h"
#include "runtime.h"
#include "safe_map.h"
#include "thread_pool.h"
@@ -147,10 +147,6 @@
return compiler_.get();
}
- bool ProfilePresent() const {
- return profile_present_;
- }
-
// Are we compiling and creating an image file?
bool IsBootImage() const {
return boot_image_;
@@ -161,16 +157,11 @@
}
// Generate the trampolines that are invoked by unresolved direct methods.
- const std::vector<uint8_t>* CreateJniDlsymLookup() const
- SHARED_REQUIRES(Locks::mutator_lock_);
- const std::vector<uint8_t>* CreateQuickGenericJniTrampoline() const
- SHARED_REQUIRES(Locks::mutator_lock_);
- const std::vector<uint8_t>* CreateQuickImtConflictTrampoline() const
- SHARED_REQUIRES(Locks::mutator_lock_);
- const std::vector<uint8_t>* CreateQuickResolutionTrampoline() const
- SHARED_REQUIRES(Locks::mutator_lock_);
- const std::vector<uint8_t>* CreateQuickToInterpreterBridge() const
- SHARED_REQUIRES(Locks::mutator_lock_);
+ const std::vector<uint8_t>* CreateJniDlsymLookup() const;
+ const std::vector<uint8_t>* CreateQuickGenericJniTrampoline() const;
+ const std::vector<uint8_t>* CreateQuickImtConflictTrampoline() const;
+ const std::vector<uint8_t>* CreateQuickResolutionTrampoline() const;
+ const std::vector<uint8_t>* CreateQuickToInterpreterBridge() const;
CompiledClass* GetCompiledClass(ClassReference ref) const
REQUIRES(!compiled_classes_lock_);
@@ -445,6 +436,10 @@
// Checks whether the provided method should be compiled, i.e., is in method_to_compile_.
bool IsMethodToCompile(const MethodReference& method_ref) const;
+ // Checks whether profile guided compilation is enabled and if the method should be compiled
+ // according to the profile file.
+ bool ShouldCompileBasedOnProfile(const MethodReference& method_ref) const;
+
void RecordClassStatus(ClassReference ref, mirror::Class::Status status)
REQUIRES(!compiled_classes_lock_);
@@ -454,9 +449,6 @@
uint16_t class_def_idx,
const DexFile& dex_file) const;
- // Should the compiler run on this method given profile information?
- bool SkipCompilation(const std::string& method_name);
-
// Get memory usage during compilation.
std::string GetMemoryUsageString(bool extended) const;
@@ -595,9 +587,6 @@
ThreadPool* thread_pool, TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
- ProfileFile profile_file_;
- bool profile_present_;
-
const CompilerOptions* const compiler_options_;
VerificationResults* const verification_results_;
DexFileToMethodInlinerMap* const method_inliner_map_;
@@ -647,6 +636,9 @@
// 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_;
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index 9dc6565..e806acf 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -293,12 +293,137 @@
}
}
-struct CompilationUnit {
- std::vector<const MethodDebugInfo*> methods_;
- size_t debug_line_offset_ = 0;
- uint32_t low_pc_ = 0xFFFFFFFFU;
- uint32_t high_pc_ = 0;
-};
+namespace {
+ struct CompilationUnit {
+ std::vector<const MethodDebugInfo*> methods_;
+ size_t debug_line_offset_ = 0;
+ uint32_t low_pc_ = 0xFFFFFFFFU;
+ uint32_t high_pc_ = 0;
+ };
+
+ struct LocalVariable {
+ uint16_t vreg;
+ uint32_t dex_pc_low;
+ uint32_t dex_pc_high;
+ const char* name;
+ const char* type;
+ const char* sig;
+ };
+
+ struct DebugInfoCallback {
+ static void NewLocal(void* ctx,
+ uint16_t vreg,
+ uint32_t start,
+ uint32_t end,
+ const char* name,
+ const char* type,
+ const char* sig) {
+ auto* context = static_cast<DebugInfoCallback*>(ctx);
+ if (name != nullptr && type != nullptr) {
+ context->local_variables_.push_back({vreg, start, end, name, type, sig});
+ }
+ }
+ std::vector<LocalVariable> local_variables_;
+ };
+
+ std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) {
+ std::vector<const char*> names;
+ const uint8_t* stream = mi->dex_file_->GetDebugInfoStream(mi->code_item_);
+ if (stream != nullptr) {
+ DecodeUnsignedLeb128(&stream); // line.
+ uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
+ for (uint32_t i = 0; i < parameters_size; ++i) {
+ uint32_t id = DecodeUnsignedLeb128P1(&stream);
+ names.push_back(mi->dex_file_->StringDataByIdx(id));
+ }
+ }
+ return names;
+ }
+
+ struct VariableLocation {
+ uint32_t low_pc;
+ uint32_t high_pc;
+ DexRegisterLocation reg_lo; // May be None if the location is unknown.
+ DexRegisterLocation reg_hi; // Most significant bits of 64-bit value.
+ };
+
+ // Get the location of given dex register (e.g. stack or machine register).
+ // Note that the location might be different based on the current pc.
+ // The result will cover all ranges where the variable is in scope.
+ std::vector<VariableLocation> GetVariableLocations(const MethodDebugInfo* method_info,
+ uint16_t vreg,
+ bool is64bitValue,
+ uint32_t dex_pc_low,
+ uint32_t dex_pc_high) {
+ std::vector<VariableLocation> variable_locations;
+
+ // Get stack maps sorted by pc (they might not be sorted internally).
+ const CodeInfo code_info(method_info->compiled_method_->GetVmapTable().data());
+ const StackMapEncoding encoding = code_info.ExtractEncoding();
+ std::map<uint32_t, StackMap> stack_maps;
+ for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
+ StackMap stack_map = code_info.GetStackMapAt(s, encoding);
+ DCHECK(stack_map.IsValid());
+ const uint32_t low_pc = method_info->low_pc_ + stack_map.GetNativePcOffset(encoding);
+ DCHECK_LE(low_pc, method_info->high_pc_);
+ stack_maps.emplace(low_pc, stack_map);
+ }
+
+ // Create entries for the requested register based on stack map data.
+ for (auto it = stack_maps.begin(); it != stack_maps.end(); it++) {
+ const StackMap& stack_map = it->second;
+ const uint32_t low_pc = it->first;
+ auto next_it = it;
+ next_it++;
+ const uint32_t high_pc = next_it != stack_maps.end() ? next_it->first
+ : method_info->high_pc_;
+ DCHECK_LE(low_pc, high_pc);
+ if (low_pc == high_pc) {
+ continue; // Ignore if the address range is empty.
+ }
+
+ // Check that the stack map is in the requested range.
+ uint32_t dex_pc = stack_map.GetDexPc(encoding);
+ if (!(dex_pc_low <= dex_pc && dex_pc < dex_pc_high)) {
+ continue;
+ }
+
+ // Find the location of the dex register.
+ DexRegisterLocation reg_lo = DexRegisterLocation::None();
+ DexRegisterLocation reg_hi = DexRegisterLocation::None();
+ if (stack_map.HasDexRegisterMap(encoding)) {
+ DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(
+ stack_map, encoding, method_info->code_item_->registers_size_);
+ reg_lo = dex_register_map.GetDexRegisterLocation(
+ vreg, method_info->code_item_->registers_size_, code_info, encoding);
+ if (is64bitValue) {
+ reg_hi = dex_register_map.GetDexRegisterLocation(
+ vreg + 1, method_info->code_item_->registers_size_, code_info, encoding);
+ }
+ }
+
+ // Add location entry for this address range.
+ if (!variable_locations.empty() &&
+ variable_locations.back().reg_lo == reg_lo &&
+ variable_locations.back().reg_hi == reg_hi &&
+ variable_locations.back().high_pc == low_pc) {
+ // Merge with the previous entry (extend its range).
+ variable_locations.back().high_pc = high_pc;
+ } else {
+ variable_locations.push_back({low_pc, high_pc, reg_lo, reg_hi});
+ }
+ }
+
+ return variable_locations;
+ }
+
+ bool IsFromOptimizingCompiler(const MethodDebugInfo* method_info) {
+ return method_info->compiled_method_->GetQuickCode().size() > 0 &&
+ method_info->compiled_method_->GetVmapTable().size() > 0 &&
+ method_info->compiled_method_->GetGcMap().size() == 0 &&
+ method_info->code_item_ != nullptr;
+ }
+} // namespace
// Helper class to write .debug_info and its supporting sections.
template<typename ElfTypes>
@@ -321,6 +446,7 @@
info_.StartTag(DW_TAG_compile_unit);
info_.WriteStrp(DW_AT_producer, owner_->WriteString("Android dex2oat"));
info_.WriteData1(DW_AT_language, DW_LANG_Java);
+ info_.WriteStrp(DW_AT_comp_dir, owner_->WriteString("$JAVA_SRC_ROOT"));
info_.WriteAddr(DW_AT_low_pc, text_address + compilation_unit.low_pc_);
info_.WriteUdata(DW_AT_high_pc, compilation_unit.high_pc_ - compilation_unit.low_pc_);
info_.WriteSecOffset(DW_AT_stmt_list, compilation_unit.debug_line_offset_);
@@ -332,6 +458,7 @@
const DexFile::ProtoId& dex_proto = dex->GetMethodPrototype(dex_method);
const DexFile::TypeList* dex_params = dex->GetProtoParameters(dex_proto);
const char* dex_class_desc = dex->GetMethodDeclaringClassDescriptor(dex_method);
+ const bool is_static = (mi->access_flags_ & kAccStatic) != 0;
// Enclose the method in correct class definition.
if (last_dex_class_desc != dex_class_desc) {
@@ -346,17 +473,17 @@
last_dex_class_desc = dex_class_desc;
}
+ // Collect information about local variables and parameters.
+ DebugInfoCallback debug_info_callback;
std::vector<const char*> param_names;
if (mi->code_item_ != nullptr) {
- const uint8_t* stream = dex->GetDebugInfoStream(mi->code_item_);
- if (stream != nullptr) {
- DecodeUnsignedLeb128(&stream); // line.
- uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
- for (uint32_t i = 0; i < parameters_size; ++i) {
- uint32_t id = DecodeUnsignedLeb128P1(&stream);
- param_names.push_back(mi->dex_file_->StringDataByIdx(id));
- }
- }
+ dex->DecodeDebugInfo(mi->code_item_,
+ is_static,
+ mi->dex_method_index_,
+ nullptr,
+ DebugInfoCallback::NewLocal,
+ &debug_info_callback);
+ param_names = GetParamNames(mi);
}
int start_depth = info_.Depth();
@@ -367,19 +494,19 @@
uint8_t frame_base[] = { DW_OP_call_frame_cfa };
info_.WriteExprLoc(DW_AT_frame_base, &frame_base, sizeof(frame_base));
WriteLazyType(dex->GetReturnTypeDescriptor(dex_proto));
+ uint32_t vreg = mi->code_item_ == nullptr ? 0 :
+ mi->code_item_->registers_size_ - mi->code_item_->ins_size_;
+ if (!is_static) {
+ info_.StartTag(DW_TAG_formal_parameter);
+ WriteName("this");
+ info_.WriteFlag(DW_AT_artificial, true);
+ WriteLazyType(dex_class_desc);
+ const bool is64bitValue = false;
+ WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc_);
+ vreg++;
+ info_.EndTag();
+ }
if (dex_params != nullptr) {
- uint32_t vreg = mi->code_item_ == nullptr ? 0 :
- mi->code_item_->registers_size_ - mi->code_item_->ins_size_;
- if ((mi->access_flags_ & kAccStatic) == 0) {
- info_.StartTag(DW_TAG_formal_parameter);
- WriteName("this");
- info_.WriteFlag(DW_AT_artificial, true);
- WriteLazyType(dex_class_desc);
- const bool is64bitValue = false;
- WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc_);
- vreg++;
- info_.EndTag();
- }
for (uint32_t i = 0; i < dex_params->Size(); ++i) {
info_.StartTag(DW_TAG_formal_parameter);
// Parameter names may not be always available.
@@ -399,6 +526,18 @@
CHECK_EQ(vreg, mi->code_item_->registers_size_);
}
}
+ for (const LocalVariable& var : debug_info_callback.local_variables_) {
+ const uint32_t first_arg = mi->code_item_->registers_size_ - mi->code_item_->ins_size_;
+ if (var.vreg < first_arg) {
+ info_.StartTag(DW_TAG_variable);
+ WriteName(var.name);
+ WriteLazyType(var.type);
+ bool is64bitValue = var.type[0] == 'D' || var.type[0] == 'J';
+ WriteRegLocation(mi, var.vreg, is64bitValue, compilation_unit.low_pc_,
+ var.dex_pc_low, var.dex_pc_high);
+ info_.EndTag();
+ }
+ }
info_.EndTag();
CHECK_EQ(info_.Depth(), start_depth); // Balanced start/end.
}
@@ -420,57 +559,40 @@
// Write table into .debug_loc which describes location of dex register.
// The dex register might be valid only at some points and it might
// move between machine registers and stack.
- void WriteRegLocation(const MethodDebugInfo* method_info, uint16_t vreg,
- bool is64bitValue, uint32_t compilation_unit_low_pc) {
+ void WriteRegLocation(const MethodDebugInfo* method_info,
+ uint16_t vreg,
+ bool is64bitValue,
+ uint32_t compilation_unit_low_pc,
+ uint32_t dex_pc_low = 0,
+ uint32_t dex_pc_high = 0xFFFFFFFF) {
using Kind = DexRegisterLocation::Kind;
- bool is_optimizing = method_info->compiled_method_->GetQuickCode().size() > 0 &&
- method_info->compiled_method_->GetVmapTable().size() > 0 &&
- method_info->compiled_method_->GetGcMap().size() == 0 &&
- method_info->code_item_ != nullptr;
- if (!is_optimizing) {
+ if (!IsFromOptimizingCompiler(method_info)) {
return;
}
- Writer<> writer(&owner_->debug_loc_);
- info_.WriteSecOffset(DW_AT_location, writer.size());
+ Writer<> debug_loc(&owner_->debug_loc_);
+ Writer<> debug_ranges(&owner_->debug_ranges_);
+ info_.WriteSecOffset(DW_AT_location, debug_loc.size());
+ info_.WriteSecOffset(DW_AT_start_scope, debug_ranges.size());
+ std::vector<VariableLocation> variable_locations = GetVariableLocations(
+ method_info,
+ vreg,
+ is64bitValue,
+ dex_pc_low,
+ dex_pc_high);
+
+ // Write .debug_loc entries.
const InstructionSet isa = owner_->builder_->GetIsa();
const bool is64bit = Is64BitInstructionSet(isa);
- const CodeInfo code_info(method_info->compiled_method_->GetVmapTable().data());
- const StackMapEncoding encoding = code_info.ExtractEncoding();
- DexRegisterLocation last_reg_lo = DexRegisterLocation::None();
- DexRegisterLocation last_reg_hi = DexRegisterLocation::None();
- size_t offset_of_last_end_address = 0;
- for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
- StackMap stack_map = code_info.GetStackMapAt(s, encoding);
- DCHECK(stack_map.IsValid());
-
- // Find the location of the dex register.
- DexRegisterLocation reg_lo = DexRegisterLocation::None();
- DexRegisterLocation reg_hi = DexRegisterLocation::None();
- if (stack_map.HasDexRegisterMap(encoding)) {
- DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(
- stack_map, encoding, method_info->code_item_->registers_size_);
- reg_lo = dex_register_map.GetDexRegisterLocation(
- vreg, method_info->code_item_->registers_size_, code_info, encoding);
- if (is64bitValue) {
- reg_hi = dex_register_map.GetDexRegisterLocation(
- vreg + 1, method_info->code_item_->registers_size_, code_info, encoding);
- }
- }
- if ((reg_lo == last_reg_lo && reg_hi == last_reg_hi) ||
- reg_lo.GetKind() == Kind::kNone) {
- // Skip identical or undefined locations.
- continue;
- }
- last_reg_lo = reg_lo;
- last_reg_hi = reg_hi;
-
+ for (const VariableLocation& variable_location : variable_locations) {
// Translate dex register location to DWARF expression.
// Note that 64-bit value might be split to two distinct locations.
// (for example, two 32-bit machine registers, or even stack and register)
uint8_t buffer[64];
uint8_t* pos = buffer;
+ DexRegisterLocation reg_lo = variable_location.reg_lo;
+ DexRegisterLocation reg_hi = variable_location.reg_hi;
for (int piece = 0; piece < (is64bitValue ? 2 : 1); piece++) {
DexRegisterLocation reg_loc = (piece == 0 ? reg_lo : reg_hi);
const Kind kind = reg_loc.GetKind();
@@ -529,43 +651,56 @@
}
}
- // Write end address for previous entry.
- const uint32_t pc = method_info->low_pc_ + stack_map.GetNativePcOffset(encoding);
- if (offset_of_last_end_address != 0) {
- if (is64bit) {
- writer.UpdateUint64(offset_of_last_end_address, pc - compilation_unit_low_pc);
- } else {
- writer.UpdateUint32(offset_of_last_end_address, pc - compilation_unit_low_pc);
- }
- }
- offset_of_last_end_address = 0;
-
- DCHECK_LE(static_cast<size_t>(pos - buffer), sizeof(buffer));
+ // Check that the buffer is large enough; keep half of it empty for safety.
+ DCHECK_LE(static_cast<size_t>(pos - buffer), sizeof(buffer) / 2);
if (pos > buffer) {
- // Write start/end address.
if (is64bit) {
- writer.PushUint64(pc - compilation_unit_low_pc);
- offset_of_last_end_address = writer.size();
- writer.PushUint64(method_info->high_pc_ - compilation_unit_low_pc);
+ debug_loc.PushUint64(variable_location.low_pc - compilation_unit_low_pc);
+ debug_loc.PushUint64(variable_location.high_pc - compilation_unit_low_pc);
} else {
- writer.PushUint32(pc - compilation_unit_low_pc);
- offset_of_last_end_address = writer.size();
- writer.PushUint32(method_info->high_pc_ - compilation_unit_low_pc);
+ debug_loc.PushUint32(variable_location.low_pc - compilation_unit_low_pc);
+ debug_loc.PushUint32(variable_location.high_pc - compilation_unit_low_pc);
}
// Write the expression.
- writer.PushUint16(pos - buffer);
- writer.PushData(buffer, pos - buffer);
+ debug_loc.PushUint16(pos - buffer);
+ debug_loc.PushData(buffer, pos - buffer);
} else {
- // Otherwise leave the address range undefined.
+ // Do not generate .debug_loc if the location is not known.
}
}
// Write end-of-list entry.
if (is64bit) {
- writer.PushUint64(0);
- writer.PushUint64(0);
+ debug_loc.PushUint64(0);
+ debug_loc.PushUint64(0);
} else {
- writer.PushUint32(0);
- writer.PushUint32(0);
+ debug_loc.PushUint32(0);
+ debug_loc.PushUint32(0);
+ }
+
+ // Write .debug_ranges entries.
+ // This includes ranges where the variable is in scope but the location is not known.
+ for (size_t i = 0; i < variable_locations.size(); i++) {
+ uint32_t low_pc = variable_locations[i].low_pc;
+ uint32_t high_pc = variable_locations[i].high_pc;
+ while (i + 1 < variable_locations.size() && variable_locations[i+1].low_pc == high_pc) {
+ // Merge address range with the next entry.
+ high_pc = variable_locations[++i].high_pc;
+ }
+ if (is64bit) {
+ debug_ranges.PushUint64(low_pc - compilation_unit_low_pc);
+ debug_ranges.PushUint64(high_pc - compilation_unit_low_pc);
+ } else {
+ debug_ranges.PushUint32(low_pc - compilation_unit_low_pc);
+ debug_ranges.PushUint32(high_pc - compilation_unit_low_pc);
+ }
+ }
+ // Write end-of-list entry.
+ if (is64bit) {
+ debug_ranges.PushUint64(0);
+ debug_ranges.PushUint64(0);
+ } else {
+ debug_ranges.PushUint32(0);
+ debug_ranges.PushUint32(0);
}
}
@@ -748,6 +883,7 @@
builder_->WriteSection(".debug_abbrev", &debug_abbrev_.Data());
builder_->WriteSection(".debug_str", &debug_str_.Data());
builder_->WriteSection(".debug_loc", &debug_loc_);
+ builder_->WriteSection(".debug_ranges", &debug_ranges_);
}
private:
@@ -760,6 +896,7 @@
DedupVector debug_abbrev_;
DedupVector debug_str_;
std::vector<uint8_t> debug_loc_;
+ std::vector<uint8_t> debug_ranges_;
std::unordered_set<const char*> defined_dex_classes_; // For CHECKs only.
};
@@ -807,10 +944,6 @@
break;
}
DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_);
- opcodes.SetAddress(text_address + compilation_unit.low_pc_);
- if (dwarf_isa != -1) {
- opcodes.SetISA(dwarf_isa);
- }
for (const MethodDebugInfo* mi : compilation_unit.methods_) {
// Ignore function if we have already generated line table for the same address.
// It would confuse the debugger and the DWARF specification forbids it.
@@ -818,9 +951,35 @@
continue;
}
+ ArrayRef<const SrcMapElem> src_mapping_table;
+ std::vector<SrcMapElem> src_mapping_table_from_stack_maps;
+ if (IsFromOptimizingCompiler(mi)) {
+ // Use stack maps to create mapping table from pc to dex.
+ const CodeInfo code_info(mi->compiled_method_->GetVmapTable().data());
+ const StackMapEncoding encoding = code_info.ExtractEncoding();
+ for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
+ StackMap stack_map = code_info.GetStackMapAt(s, encoding);
+ DCHECK(stack_map.IsValid());
+ const uint32_t pc = stack_map.GetNativePcOffset(encoding);
+ const int32_t dex = stack_map.GetDexPc(encoding);
+ src_mapping_table_from_stack_maps.push_back({pc, dex});
+ }
+ std::sort(src_mapping_table_from_stack_maps.begin(),
+ src_mapping_table_from_stack_maps.end());
+ src_mapping_table = ArrayRef<const SrcMapElem>(src_mapping_table_from_stack_maps);
+ } else {
+ // Use the mapping table provided by the quick compiler.
+ src_mapping_table = mi->compiled_method_->GetSrcMappingTable();
+ }
+
+ if (src_mapping_table.empty()) {
+ continue;
+ }
+
+ // Create mapping table from dex to source line.
struct DebugInfoCallbacks {
static bool NewPosition(void* ctx, uint32_t address, uint32_t line) {
- auto* context = reinterpret_cast<DebugInfoCallbacks*>(ctx);
+ auto* context = static_cast<DebugInfoCallbacks*>(ctx);
context->dex2line_.push_back({address, static_cast<int32_t>(line)});
return false;
}
@@ -839,6 +998,15 @@
&debug_info_callbacks);
}
+ if (debug_info_callbacks.dex2line_.empty()) {
+ continue;
+ }
+
+ opcodes.SetAddress(method_address);
+ if (dwarf_isa != -1) {
+ opcodes.SetISA(dwarf_isa);
+ }
+
// Get and deduplicate directory and filename.
int file_index = 0; // 0 - primary source file of the compilation.
auto& dex_class_def = dex->GetClassDef(mi->class_def_index_);
@@ -888,7 +1056,7 @@
const DefaultSrcMap& dex2line_map = debug_info_callbacks.dex2line_;
if (file_index != 0 && !dex2line_map.empty()) {
bool first = true;
- for (SrcMapElem pc2dex : mi->compiled_method_->GetSrcMappingTable()) {
+ for (SrcMapElem pc2dex : src_mapping_table) {
uint32_t pc = pc2dex.from_;
int dex_pc = pc2dex.to_;
auto dex2line = dex2line_map.Find(static_cast<uint32_t>(dex_pc));
@@ -915,9 +1083,10 @@
// line 0 - instruction cannot be attributed to any source line.
opcodes.AddRow(method_address, 0);
}
+
+ opcodes.AdvancePC(text_address + mi->high_pc_);
+ opcodes.EndSequence();
}
- opcodes.AdvancePC(text_address + compilation_unit.high_pc_);
- opcodes.EndSequence();
std::vector<uint8_t> buffer;
buffer.reserve(opcodes.data()->size() + KB);
size_t offset = builder_->GetDebugLine()->GetSize();
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index cda6240..15812dc 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -43,10 +43,17 @@
ReserveImageSpace();
CommonCompilerTest::SetUp();
}
+ void TestWriteRead(ImageHeader::StorageMode storage_mode);
};
-TEST_F(ImageTest, WriteRead) {
- TEST_DISABLED_FOR_NON_PIC_COMPILING_WITH_OPTIMIZING();
+void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) {
+ // TODO: Test does not currently work with optimizing.
+ CreateCompilerDriver(Compiler::kQuick, kRuntimeISA);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ // Enable write for dex2dex.
+ for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
+ dex_file->EnableWrite();
+ }
// Create a generic location tmp file, to be the base of the .art and .oat temporary files.
ScratchFile location;
ScratchFile image_location(location, ".art");
@@ -68,17 +75,14 @@
std::unique_ptr<ImageWriter> writer(new ImageWriter(*compiler_driver_,
requested_image_base,
/*compile_pic*/false,
- /*compile_app_image*/false));
+ /*compile_app_image*/false,
+ storage_mode));
// TODO: compile_pic should be a test argument.
{
{
jobject class_loader = nullptr;
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
TimingLogger timings("ImageTest::WriteRead", false, false);
TimingLogger::ScopedTiming t("CompileAll", &timings);
- for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
- dex_file->EnableWrite();
- }
compiler_driver_->SetDexFilesForOatFile(class_linker->GetBootClassPath());
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
@@ -113,14 +117,9 @@
elf_writer->EndText(text);
elf_writer->SetBssSize(oat_writer.GetBssSize());
-
elf_writer->WriteDynamicSection();
-
- ArrayRef<const dwarf::MethodDebugInfo> method_infos(oat_writer.GetMethodDebugInfo());
- elf_writer->WriteDebugInfo(method_infos);
-
- ArrayRef<const uintptr_t> patch_locations(oat_writer.GetAbsolutePatchLocations());
- elf_writer->WritePatchLocations(patch_locations);
+ elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo());
+ elf_writer->WritePatchLocations(oat_writer.GetAbsolutePatchLocations());
success = elf_writer->End();
@@ -209,7 +208,13 @@
gc::space::ImageSpace* image_space = heap->GetBootImageSpace();
ASSERT_TRUE(image_space != nullptr);
- ASSERT_LE(image_space->Size(), image_file_size);
+ if (storage_mode == ImageHeader::kStorageModeUncompressed) {
+ // Uncompressed, image should be smaller than file.
+ ASSERT_LE(image_space->Size(), image_file_size);
+ } else {
+ // Compressed, file should be smaller than image.
+ ASSERT_LE(image_file_size, image_space->Size());
+ }
image_space->VerifyImageAllocations();
uint8_t* image_begin = image_space->Begin();
@@ -237,6 +242,14 @@
CHECK_EQ(0, rmdir_result);
}
+TEST_F(ImageTest, WriteReadUncompressed) {
+ TestWriteRead(ImageHeader::kStorageModeUncompressed);
+}
+
+TEST_F(ImageTest, WriteReadLZ4) {
+ TestWriteRead(ImageHeader::kStorageModeLZ4);
+}
+
TEST_F(ImageTest, ImageHeaderIsValid) {
uint32_t image_begin = ART_BASE_ADDRESS;
uint32_t image_size_ = 16 * KB;
@@ -257,7 +270,9 @@
oat_data_end,
oat_file_end,
sizeof(void*),
- /*compile_pic*/false);
+ /*compile_pic*/false,
+ ImageHeader::kDefaultStorageMode,
+ /*data_size*/0u);
ASSERT_TRUE(image_header.IsValid());
char* magic = const_cast<char*>(image_header.GetMagic());
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index bf1fcdd..f9f0eb8 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -17,6 +17,7 @@
#include "image_writer.h"
#include <sys/stat.h>
+#include <lz4.h>
#include <memory>
#include <numeric>
@@ -225,27 +226,72 @@
return EXIT_FAILURE;
}
- // Write out the image + fields + methods.
+ std::unique_ptr<char[]> compressed_data;
+ // Image data size excludes the bitmap and the header.
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;
+ const size_t image_data_size = image_header->GetImageSize() - sizeof(ImageHeader);
+ char* image_data = reinterpret_cast<char*>(image_->Begin()) + sizeof(ImageHeader);
+ size_t data_size;
+ const char* image_data_to_write;
+
+ CHECK_EQ(image_header->storage_mode_, image_storage_mode_);
+ switch (image_storage_mode_) {
+ case ImageHeader::kStorageModeLZ4: {
+ size_t compressed_max_size = LZ4_compressBound(image_data_size);
+ compressed_data.reset(new char[compressed_max_size]);
+ data_size = LZ4_compress(
+ reinterpret_cast<char*>(image_->Begin()) + sizeof(ImageHeader),
+ &compressed_data[0],
+ image_data_size);
+ image_data_to_write = &compressed_data[0];
+ VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size;
+ break;
+ }
+ case ImageHeader::kStorageModeUncompressed: {
+ data_size = image_data_size;
+ image_data_to_write = image_data;
+ break;
+ }
+ default: {
+ LOG(FATAL) << "Unsupported";
+ UNREACHABLE();
+ }
+ }
+
+ // Write header first, as uncompressed.
+ image_header->data_size_ = data_size;
+ if (!image_file->WriteFully(image_->Begin(), sizeof(ImageHeader))) {
+ PLOG(ERROR) << "Failed to write image file header " << image_filename;
image_file->Erase();
return false;
}
- // Write out the image bitmap at the page aligned start of the image end.
+ // Write out the image + fields + methods.
+ const bool is_compressed = compressed_data != nullptr;
+ if (!image_file->WriteFully(image_data_to_write, data_size)) {
+ PLOG(ERROR) << "Failed to write image file data " << image_filename;
+ image_file->Erase();
+ return false;
+ }
+
+ // Write out the image bitmap at the page aligned start of the image end, also uncompressed for
+ // convenience.
const ImageSection& bitmap_section = image_header->GetImageSection(
ImageHeader::kSectionImageBitmap);
- CHECK_ALIGNED(bitmap_section.Offset(), kPageSize);
+ // Align up since data size may be unaligned if the image is compressed.
+ size_t bitmap_position_in_file = RoundUp(sizeof(ImageHeader) + data_size, kPageSize);
+ if (!is_compressed) {
+ CHECK_EQ(bitmap_position_in_file, bitmap_section.Offset());
+ }
if (!image_file->Write(reinterpret_cast<char*>(image_bitmap_->Begin()),
- bitmap_section.Size(), bitmap_section.Offset())) {
+ bitmap_section.Size(),
+ bitmap_position_in_file)) {
PLOG(ERROR) << "Failed to write image file " << image_filename;
image_file->Erase();
return false;
}
-
- CHECK_EQ(bitmap_section.End(), static_cast<size_t>(image_file->GetLength()));
+ CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(),
+ static_cast<size_t>(image_file->GetLength()));
if (image_file->FlushCloseOrErase() != 0) {
PLOG(ERROR) << "Failed to flush and close image file " << image_filename;
return false;
@@ -1247,7 +1293,8 @@
}
CHECK_EQ(AlignUp(image_begin_ + image_end, kPageSize), oat_file_begin) <<
"Oat file should be right after the image.";
- // Create the header.
+ // Create the header, leave 0 for data size since we will fill this in as we are writing the
+ // image.
new (image_->Begin()) ImageHeader(PointerToLowMemUInt32(image_begin_),
image_end,
sections,
@@ -1258,7 +1305,9 @@
PointerToLowMemUInt32(oat_data_end),
PointerToLowMemUInt32(oat_file_end),
target_ptr_size_,
- compile_pic_);
+ compile_pic_,
+ image_storage_mode_,
+ /*data_size*/0u);
}
ArtMethod* ImageWriter::GetImageMethodAddress(ArtMethod* method) {
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 386838f..c20d836 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -30,6 +30,7 @@
#include "base/macros.h"
#include "driver/compiler_driver.h"
#include "gc/space/space.h"
+#include "image.h"
#include "length_prefixed_array.h"
#include "lock_word.h"
#include "mem_map.h"
@@ -54,7 +55,8 @@
ImageWriter(const CompilerDriver& compiler_driver,
uintptr_t image_begin,
bool compile_pic,
- bool compile_app_image)
+ bool compile_app_image,
+ ImageHeader::StorageMode image_storage_mode)
: compiler_driver_(compiler_driver),
image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
image_end_(0),
@@ -73,7 +75,8 @@
image_method_array_(ImageHeader::kImageMethodsCount),
dirty_methods_(0u),
clean_methods_(0u),
- class_table_bytes_(0u) {
+ class_table_bytes_(0u),
+ image_storage_mode_(image_storage_mode) {
CHECK_NE(image_begin, 0U);
std::fill_n(image_methods_, arraysize(image_methods_), nullptr);
std::fill_n(oat_address_offsets_, arraysize(oat_address_offsets_), 0);
@@ -460,6 +463,9 @@
// 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_;
+
friend class ContainsBootClassLoaderNonImageClassVisitor;
friend class FixupClassVisitor;
friend class FixupRootVisitor;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index b8610d0..a2b29a3 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -157,14 +157,9 @@
elf_writer->EndText(text);
elf_writer->SetBssSize(oat_writer.GetBssSize());
-
elf_writer->WriteDynamicSection();
-
- ArrayRef<const dwarf::MethodDebugInfo> method_infos(oat_writer.GetMethodDebugInfo());
- elf_writer->WriteDebugInfo(method_infos);
-
- ArrayRef<const uintptr_t> patch_locations(oat_writer.GetAbsolutePatchLocations());
- elf_writer->WritePatchLocations(patch_locations);
+ elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo());
+ elf_writer->WritePatchLocations(oat_writer.GetAbsolutePatchLocations());
return elf_writer->End();
}
@@ -260,7 +255,7 @@
EXPECT_EQ(72U, sizeof(OatHeader));
EXPECT_EQ(4U, sizeof(OatMethodOffsets));
EXPECT_EQ(28U, sizeof(OatQuickMethodHeader));
- EXPECT_EQ(114 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
+ EXPECT_EQ(131 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
}
TEST_F(OatTest, OatHeaderIsValid) {
@@ -269,14 +264,9 @@
std::unique_ptr<const InstructionSetFeatures> insn_features(
InstructionSetFeatures::FromVariant(insn_set, "default", &error_msg));
ASSERT_TRUE(insn_features.get() != nullptr) << error_msg;
- std::vector<const DexFile*> dex_files;
- uint32_t image_file_location_oat_checksum = 0;
- uint32_t image_file_location_oat_begin = 0;
std::unique_ptr<OatHeader> oat_header(OatHeader::Create(insn_set,
insn_features.get(),
- &dex_files,
- image_file_location_oat_checksum,
- image_file_location_oat_begin,
+ 0u,
nullptr));
ASSERT_NE(oat_header.get(), nullptr);
ASSERT_TRUE(oat_header->IsValid());
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index e8e775f..2b2f0e8 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -52,6 +52,97 @@
namespace art {
+class OatWriter::OatClass {
+ public:
+ OatClass(size_t offset,
+ const dchecked_vector<CompiledMethod*>& compiled_methods,
+ uint32_t num_non_null_compiled_methods,
+ mirror::Class::Status status);
+ OatClass(OatClass&& src) = default;
+ size_t GetOatMethodOffsetsOffsetFromOatHeader(size_t class_def_method_index_) const;
+ size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const;
+ size_t SizeOf() const;
+ bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
+
+ CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const {
+ return compiled_methods_[class_def_method_index];
+ }
+
+ // Offset of start of OatClass from beginning of OatHeader. It is
+ // used to validate file position when writing.
+ size_t offset_;
+
+ // CompiledMethods for each class_def_method_index, or null if no method is available.
+ dchecked_vector<CompiledMethod*> compiled_methods_;
+
+ // Offset from OatClass::offset_ to the OatMethodOffsets for the
+ // class_def_method_index. If 0, it means the corresponding
+ // CompiledMethod entry in OatClass::compiled_methods_ should be
+ // null and that the OatClass::type_ should be kOatClassBitmap.
+ dchecked_vector<uint32_t> oat_method_offsets_offsets_from_oat_class_;
+
+ // Data to write.
+
+ static_assert(mirror::Class::Status::kStatusMax < (1 << 16), "class status won't fit in 16bits");
+ int16_t status_;
+
+ static_assert(OatClassType::kOatClassMax < (1 << 16), "oat_class type won't fit in 16bits");
+ uint16_t type_;
+
+ uint32_t method_bitmap_size_;
+
+ // bit vector indexed by ClassDef method index. When
+ // OatClassType::type_ is kOatClassBitmap, a set bit indicates the
+ // method has an OatMethodOffsets in methods_offsets_, otherwise
+ // the entry was ommited to save space. If OatClassType::type_ is
+ // not is kOatClassBitmap, the bitmap will be null.
+ std::unique_ptr<BitVector> method_bitmap_;
+
+ // OatMethodOffsets and OatMethodHeaders for each CompiledMethod
+ // present in the OatClass. Note that some may be missing if
+ // OatClass::compiled_methods_ contains null values (and
+ // oat_method_offsets_offsets_from_oat_class_ should contain 0
+ // values in this case).
+ dchecked_vector<OatMethodOffsets> method_offsets_;
+ dchecked_vector<OatQuickMethodHeader> method_headers_;
+
+ private:
+ size_t GetMethodOffsetsRawSize() const {
+ return method_offsets_.size() * sizeof(method_offsets_[0]);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(OatClass);
+};
+
+class OatWriter::OatDexFile {
+ public:
+ OatDexFile(size_t offset, const DexFile& dex_file);
+ OatDexFile(OatDexFile&& src) = default;
+
+ size_t SizeOf() const;
+ bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
+
+ // Offset of start of OatDexFile from beginning of OatHeader. It is
+ // used to validate file position when writing.
+ size_t offset_;
+
+ // Data to write.
+ uint32_t dex_file_location_size_;
+ const uint8_t* dex_file_location_data_;
+ uint32_t dex_file_location_checksum_;
+ uint32_t dex_file_offset_;
+ uint32_t lookup_table_offset_;
+ TypeLookupTable* lookup_table_; // Owned by the dex file.
+ dchecked_vector<uint32_t> class_offsets_;
+
+ private:
+ size_t GetClassOffsetsRawSize() const {
+ return class_offsets_.size() * sizeof(class_offsets_[0]);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(OatDexFile);
+};
+
#define DCHECK_OFFSET() \
DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out->Seek(0, kSeekCurrent)) \
<< "file_offset=" << file_offset << " relative_offset=" << relative_offset
@@ -106,14 +197,14 @@
size_oat_dex_file_location_data_(0),
size_oat_dex_file_location_checksum_(0),
size_oat_dex_file_offset_(0),
- size_oat_dex_file_methods_offsets_(0),
+ size_oat_dex_file_lookup_table_offset_(0),
+ size_oat_dex_file_class_offsets_(0),
+ size_oat_lookup_table_alignment_(0),
+ size_oat_lookup_table_(0),
size_oat_class_type_(0),
size_oat_class_status_(0),
size_oat_class_method_bitmaps_(0),
size_oat_class_method_offsets_(0),
- size_oat_lookup_table_alignment_(0),
- size_oat_lookup_table_offset_(0),
- size_oat_lookup_table_(0),
method_offset_map_() {
CHECK(key_value_store != nullptr);
if (compiling_boot_image) {
@@ -180,9 +271,6 @@
}
OatWriter::~OatWriter() {
- delete oat_header_;
- STLDeleteElements(&oat_dex_files_);
- STLDeleteElements(&oat_classes_);
}
struct OatWriter::GcMapDataAccess {
@@ -326,6 +414,11 @@
: DexMethodVisitor(writer, offset),
compiled_methods_(),
num_non_null_compiled_methods_(0u) {
+ size_t num_classes = 0u;
+ for (const OatDexFile& oat_dex_file : writer_->oat_dex_files_) {
+ num_classes += oat_dex_file.class_offsets_.size();
+ }
+ writer_->oat_classes_.reserve(num_classes);
compiled_methods_.reserve(256u);
}
@@ -364,16 +457,16 @@
status = mirror::Class::kStatusNotReady;
}
- OatClass* oat_class = new OatClass(offset_, compiled_methods_,
- num_non_null_compiled_methods_, status);
- writer_->oat_classes_.push_back(oat_class);
- oat_class->UpdateChecksum(writer_->oat_header_);
- offset_ += oat_class->SizeOf();
+ writer_->oat_classes_.emplace_back(offset_,
+ compiled_methods_,
+ num_non_null_compiled_methods_,
+ status);
+ offset_ += writer_->oat_classes_.back().SizeOf();
return DexMethodVisitor::EndClass();
}
private:
- std::vector<CompiledMethod*> compiled_methods_;
+ dchecked_vector<CompiledMethod*> compiled_methods_;
size_t num_non_null_compiled_methods_;
};
@@ -396,7 +489,7 @@
bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
SHARED_REQUIRES(Locks::mutator_lock_) {
- OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+ OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
if (compiled_method != nullptr) {
@@ -583,7 +676,7 @@
bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it ATTRIBUTE_UNUSED)
SHARED_REQUIRES(Locks::mutator_lock_) {
- OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+ OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
if (compiled_method != nullptr) {
@@ -600,7 +693,6 @@
DataAccess::SetOffset(oat_class, method_offsets_index_, offset_);
dedupe_map_.PutBefore(lb, map.data(), offset_);
offset_ += map_size;
- writer_->oat_header_->UpdateChecksum(&map[0], map_size);
}
}
++method_offsets_index_;
@@ -624,7 +716,7 @@
bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
SHARED_REQUIRES(Locks::mutator_lock_) {
- OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+ OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
OatMethodOffsets offsets(0u);
@@ -715,7 +807,7 @@
bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
SHARED_REQUIRES(Locks::mutator_lock_) {
- OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+ OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
// No thread suspension since dex_cache_ that may get invalidated if that occurs.
@@ -752,8 +844,7 @@
<< PrettyMethod(it.GetMemberIndex(), *dex_file_);
const OatQuickMethodHeader& method_header =
oat_class->method_headers_[method_offsets_index_];
- writer_->oat_header_->UpdateChecksum(&method_header, sizeof(method_header));
- if (!out->WriteFully(&method_header, sizeof(method_header))) {
+ if (!writer_->WriteData(out, &method_header, sizeof(method_header))) {
ReportWriteFailure("method header", it);
return false;
}
@@ -790,8 +881,7 @@
}
}
- writer_->oat_header_->UpdateChecksum(quick_code.data(), code_size);
- if (!out->WriteFully(quick_code.data(), code_size)) {
+ if (!writer_->WriteData(out, quick_code.data(), code_size)) {
ReportWriteFailure("method code", it);
return false;
}
@@ -945,7 +1035,7 @@
}
bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) {
- OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+ OatClass* oat_class = &writer_->oat_classes_[oat_class_index_];
const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
if (compiled_method != nullptr) { // ie. not an abstract method
@@ -963,7 +1053,7 @@
<< map_size << " " << map_offset << " " << offset_ << " "
<< PrettyMethod(it.GetMemberIndex(), *dex_file_) << " for " << DataAccess::Name();
if (map_size != 0u && map_offset == offset_) {
- if (UNLIKELY(!out->WriteFully(&map[0], map_size))) {
+ if (UNLIKELY(!writer_->WriteData(out, map.data(), map_size))) {
ReportWriteFailure(it);
return false;
}
@@ -1028,12 +1118,12 @@
}
size_t OatWriter::InitOatHeader() {
- oat_header_ = OatHeader::Create(compiler_driver_->GetInstructionSet(),
- compiler_driver_->GetInstructionSetFeatures(),
- dex_files_,
- image_file_location_oat_checksum_,
- image_file_location_oat_begin_,
- key_value_store_);
+ oat_header_.reset(OatHeader::Create(compiler_driver_->GetInstructionSet(),
+ compiler_driver_->GetInstructionSetFeatures(),
+ dchecked_integral_cast<uint32_t>(dex_files_->size()),
+ key_value_store_));
+ oat_header_->SetImageFileLocationOatChecksum(image_file_location_oat_checksum_);
+ oat_header_->SetImageFileLocationOatDataBegin(image_file_location_oat_begin_);
return oat_header_->GetHeaderSize();
}
@@ -1043,9 +1133,8 @@
for (size_t i = 0; i != dex_files_->size(); ++i) {
const DexFile* dex_file = (*dex_files_)[i];
CHECK(dex_file != nullptr);
- OatDexFile* oat_dex_file = new OatDexFile(offset, *dex_file);
- oat_dex_files_.push_back(oat_dex_file);
- offset += oat_dex_file->SizeOf();
+ oat_dex_files_.emplace_back(offset, *dex_file);
+ offset += oat_dex_files_.back().SizeOf();
}
return offset;
}
@@ -1059,12 +1148,12 @@
size_dex_file_alignment_ += offset - original_offset;
// set offset in OatDexFile to DexFile
- oat_dex_files_[i]->dex_file_offset_ = offset;
+ oat_dex_files_[i].dex_file_offset_ = offset;
const DexFile* dex_file = (*dex_files_)[i];
// Initialize type lookup table
- oat_dex_files_[i]->lookup_table_ = dex_file->GetTypeLookupTable();
+ oat_dex_files_[i].lookup_table_ = dex_file->GetTypeLookupTable();
offset += dex_file->GetHeader().file_size_;
}
@@ -1072,14 +1161,14 @@
}
size_t OatWriter::InitLookupTables(size_t offset) {
- for (OatDexFile* oat_dex_file : oat_dex_files_) {
- if (oat_dex_file->lookup_table_ != nullptr) {
+ for (OatDexFile& oat_dex_file : oat_dex_files_) {
+ if (oat_dex_file.lookup_table_ != nullptr) {
uint32_t aligned_offset = RoundUp(offset, 4);
- oat_dex_file->lookup_table_offset_ = aligned_offset;
+ oat_dex_file.lookup_table_offset_ = aligned_offset;
size_oat_lookup_table_alignment_ += aligned_offset - offset;
- offset = aligned_offset + oat_dex_file->lookup_table_->RawDataLength();
+ offset = aligned_offset + oat_dex_file.lookup_table_->RawDataLength();
} else {
- oat_dex_file->lookup_table_offset_ = 0;
+ oat_dex_file.lookup_table_offset_ = 0;
}
}
return offset;
@@ -1094,13 +1183,12 @@
// Update oat_dex_files_.
auto oat_class_it = oat_classes_.begin();
- for (OatDexFile* oat_dex_file : oat_dex_files_) {
- for (uint32_t& method_offset : oat_dex_file->methods_offsets_) {
+ for (OatDexFile& oat_dex_file : oat_dex_files_) {
+ for (uint32_t& class_offset : oat_dex_file.class_offsets_) {
DCHECK(oat_class_it != oat_classes_.end());
- method_offset = (*oat_class_it)->offset_;
+ class_offset = oat_class_it->offset_;
++oat_class_it;
}
- oat_dex_file->UpdateChecksum(oat_header_);
}
CHECK(oat_class_it == oat_classes_.end());
@@ -1184,17 +1272,14 @@
}
bool OatWriter::WriteRodata(OutputStream* out) {
- const off_t raw_file_offset = out->Seek(0, kSeekCurrent);
- if (raw_file_offset == (off_t) -1) {
- LOG(ERROR) << "Failed to get file offset in " << out->GetLocation();
+ if (!GetOatDataOffset(out)) {
return false;
}
- const size_t file_offset = static_cast<size_t>(raw_file_offset);
- oat_data_offset_ = file_offset;
+ const size_t file_offset = oat_data_offset_;
// Reserve space for header. It will be written last - after updating the checksum.
size_t header_size = oat_header_->GetHeaderSize();
- if (out->Seek(header_size, kSeekCurrent) == (off_t) -1) {
+ if (out->Seek(header_size, kSeekCurrent) == static_cast<off_t>(-1)) {
PLOG(ERROR) << "Failed to reserve space for oat header in " << out->GetLocation();
return false;
}
@@ -1207,7 +1292,7 @@
}
off_t tables_end_offset = out->Seek(0, kSeekCurrent);
- if (tables_end_offset == (off_t) -1) {
+ if (tables_end_offset == static_cast<off_t>(-1)) {
LOG(ERROR) << "Failed to seek to oat code position in " << out->GetLocation();
return false;
}
@@ -1252,7 +1337,7 @@
}
const off_t oat_end_file_offset = out->Seek(0, kSeekCurrent);
- if (oat_end_file_offset == (off_t) -1) {
+ if (oat_end_file_offset == static_cast<off_t>(-1)) {
LOG(ERROR) << "Failed to get oat end file offset in " << out->GetLocation();
return false;
}
@@ -1288,14 +1373,14 @@
DO_STAT(size_oat_dex_file_location_data_);
DO_STAT(size_oat_dex_file_location_checksum_);
DO_STAT(size_oat_dex_file_offset_);
- DO_STAT(size_oat_dex_file_methods_offsets_);
+ DO_STAT(size_oat_dex_file_lookup_table_offset_);
+ DO_STAT(size_oat_dex_file_class_offsets_);
+ DO_STAT(size_oat_lookup_table_alignment_);
+ DO_STAT(size_oat_lookup_table_);
DO_STAT(size_oat_class_type_);
DO_STAT(size_oat_class_status_);
DO_STAT(size_oat_class_method_bitmaps_);
DO_STAT(size_oat_class_method_offsets_);
- DO_STAT(size_oat_lookup_table_alignment_);
- DO_STAT(size_oat_lookup_table_offset_);
- DO_STAT(size_oat_lookup_table_);
#undef DO_STAT
VLOG(compiler) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; \
@@ -1306,17 +1391,20 @@
CHECK_EQ(file_offset + size_, static_cast<size_t>(oat_end_file_offset));
CHECK_EQ(size_, relative_offset);
+ // Finalize the header checksum.
+ oat_header_->UpdateChecksumWithHeaderData();
+
// Write the header now that the checksum is final.
- if (out->Seek(file_offset, kSeekSet) == (off_t) -1) {
+ if (out->Seek(file_offset, kSeekSet) == static_cast<off_t>(-1)) {
PLOG(ERROR) << "Failed to seek to oat header position in " << out->GetLocation();
return false;
}
DCHECK_EQ(file_offset, static_cast<size_t>(out->Seek(0, kSeekCurrent)));
- if (!out->WriteFully(oat_header_, header_size)) {
+ if (!out->WriteFully(oat_header_.get(), header_size)) {
PLOG(ERROR) << "Failed to write oat header to " << out->GetLocation();
return false;
}
- if (out->Seek(oat_end_file_offset, kSeekSet) == (off_t) -1) {
+ if (out->Seek(oat_end_file_offset, kSeekSet) == static_cast<off_t>(-1)) {
PLOG(ERROR) << "Failed to seek to end after writing oat header to " << out->GetLocation();
return false;
}
@@ -1327,13 +1415,13 @@
bool OatWriter::WriteTables(OutputStream* out, const size_t file_offset) {
for (size_t i = 0; i != oat_dex_files_.size(); ++i) {
- if (!oat_dex_files_[i]->Write(this, out, file_offset)) {
+ if (!oat_dex_files_[i].Write(this, out, file_offset)) {
PLOG(ERROR) << "Failed to write oat dex information to " << out->GetLocation();
return false;
}
}
for (size_t i = 0; i != oat_dex_files_.size(); ++i) {
- uint32_t expected_offset = file_offset + oat_dex_files_[i]->dex_file_offset_;
+ uint32_t expected_offset = file_offset + oat_dex_files_[i].dex_file_offset_;
off_t actual_offset = out->Seek(expected_offset, kSeekSet);
if (static_cast<uint32_t>(actual_offset) != expected_offset) {
const DexFile* dex_file = (*dex_files_)[i];
@@ -1353,7 +1441,7 @@
return false;
}
for (size_t i = 0; i != oat_classes_.size(); ++i) {
- if (!oat_classes_[i]->Write(this, out, file_offset)) {
+ if (!oat_classes_[i].Write(this, out, file_offset)) {
PLOG(ERROR) << "Failed to write oat methods information to " << out->GetLocation();
return false;
}
@@ -1363,8 +1451,8 @@
bool OatWriter::WriteLookupTables(OutputStream* out, const size_t file_offset) {
for (size_t i = 0; i < oat_dex_files_.size(); ++i) {
- const uint32_t lookup_table_offset = oat_dex_files_[i]->lookup_table_offset_;
- const TypeLookupTable* table = oat_dex_files_[i]->lookup_table_;
+ const uint32_t lookup_table_offset = oat_dex_files_[i].lookup_table_offset_;
+ const TypeLookupTable* table = oat_dex_files_[i].lookup_table_;
DCHECK_EQ(lookup_table_offset == 0, table == nullptr);
if (lookup_table_offset == 0) {
continue;
@@ -1378,7 +1466,7 @@
return false;
}
if (table != nullptr) {
- if (!out->WriteFully(table->RawData(), table->RawDataLength())) {
+ if (!WriteData(out, table->RawData(), table->RawDataLength())) {
const DexFile* dex_file = (*dex_files_)[i];
PLOG(ERROR) << "Failed to write lookup table for " << dex_file->GetLocation()
<< " to " << out->GetLocation();
@@ -1427,7 +1515,7 @@
uint32_t alignment_padding = aligned_offset - relative_offset; \
out->Seek(alignment_padding, kSeekCurrent); \
size_trampoline_alignment_ += alignment_padding; \
- if (!out->WriteFully(&(*field)[0], field->size())) { \
+ if (!WriteData(out, field->data(), field->size())) { \
PLOG(ERROR) << "Failed to write " # field " to " << out->GetLocation(); \
return false; \
} \
@@ -1469,6 +1557,17 @@
return relative_offset;
}
+bool OatWriter::GetOatDataOffset(OutputStream* out) {
+ // Get the elf file offset of the oat file.
+ const off_t raw_file_offset = out->Seek(0, kSeekCurrent);
+ if (raw_file_offset == static_cast<off_t>(-1)) {
+ LOG(ERROR) << "Failed to get file offset in " << out->GetLocation();
+ return false;
+ }
+ oat_data_offset_ = static_cast<size_t>(raw_file_offset);
+ return true;
+}
+
bool OatWriter::WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta) {
static const uint8_t kPadding[] = {
0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
@@ -1481,6 +1580,11 @@
return true;
}
+bool OatWriter::WriteData(OutputStream* out, const void* data, size_t size) {
+ oat_header_->UpdateChecksum(data, size);
+ return out->WriteFully(data, size);
+}
+
std::pair<bool, uint32_t> OatWriter::MethodOffsetMap::FindMethodOffset(MethodReference ref) {
auto it = map.find(ref);
if (it == map.end()) {
@@ -1498,7 +1602,7 @@
dex_file_location_checksum_ = dex_file.GetLocationChecksum();
dex_file_offset_ = 0;
lookup_table_offset_ = 0;
- methods_offsets_.resize(dex_file.NumClassDefs());
+ class_offsets_.resize(dex_file.NumClassDefs());
}
size_t OatWriter::OatDexFile::SizeOf() const {
@@ -1507,63 +1611,50 @@
+ sizeof(dex_file_location_checksum_)
+ sizeof(dex_file_offset_)
+ sizeof(lookup_table_offset_)
- + (sizeof(methods_offsets_[0]) * methods_offsets_.size());
-}
-
-void OatWriter::OatDexFile::UpdateChecksum(OatHeader* oat_header) const {
- oat_header->UpdateChecksum(&dex_file_location_size_, sizeof(dex_file_location_size_));
- oat_header->UpdateChecksum(dex_file_location_data_, dex_file_location_size_);
- oat_header->UpdateChecksum(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_));
- oat_header->UpdateChecksum(&dex_file_offset_, sizeof(dex_file_offset_));
- oat_header->UpdateChecksum(&lookup_table_offset_, sizeof(lookup_table_offset_));
- if (lookup_table_ != nullptr) {
- oat_header->UpdateChecksum(lookup_table_->RawData(), lookup_table_->RawDataLength());
- }
- oat_header->UpdateChecksum(&methods_offsets_[0],
- sizeof(methods_offsets_[0]) * methods_offsets_.size());
+ + (sizeof(class_offsets_[0]) * class_offsets_.size());
}
bool OatWriter::OatDexFile::Write(OatWriter* oat_writer,
OutputStream* out,
const size_t file_offset) const {
DCHECK_OFFSET_();
- if (!out->WriteFully(&dex_file_location_size_, sizeof(dex_file_location_size_))) {
+ if (!oat_writer->WriteData(out, &dex_file_location_size_, sizeof(dex_file_location_size_))) {
PLOG(ERROR) << "Failed to write dex file location length to " << out->GetLocation();
return false;
}
oat_writer->size_oat_dex_file_location_size_ += sizeof(dex_file_location_size_);
- if (!out->WriteFully(dex_file_location_data_, dex_file_location_size_)) {
+ if (!oat_writer->WriteData(out, dex_file_location_data_, dex_file_location_size_)) {
PLOG(ERROR) << "Failed to write dex file location data to " << out->GetLocation();
return false;
}
oat_writer->size_oat_dex_file_location_data_ += dex_file_location_size_;
- if (!out->WriteFully(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_))) {
+ if (!oat_writer->WriteData(out,
+ &dex_file_location_checksum_,
+ sizeof(dex_file_location_checksum_))) {
PLOG(ERROR) << "Failed to write dex file location checksum to " << out->GetLocation();
return false;
}
oat_writer->size_oat_dex_file_location_checksum_ += sizeof(dex_file_location_checksum_);
- if (!out->WriteFully(&dex_file_offset_, sizeof(dex_file_offset_))) {
+ if (!oat_writer->WriteData(out, &dex_file_offset_, sizeof(dex_file_offset_))) {
PLOG(ERROR) << "Failed to write dex file offset to " << out->GetLocation();
return false;
}
oat_writer->size_oat_dex_file_offset_ += sizeof(dex_file_offset_);
- if (!out->WriteFully(&lookup_table_offset_, sizeof(lookup_table_offset_))) {
+ if (!oat_writer->WriteData(out, &lookup_table_offset_, sizeof(lookup_table_offset_))) {
PLOG(ERROR) << "Failed to write lookup table offset to " << out->GetLocation();
return false;
}
- oat_writer->size_oat_lookup_table_offset_ += sizeof(lookup_table_offset_);
- if (!out->WriteFully(&methods_offsets_[0],
- sizeof(methods_offsets_[0]) * methods_offsets_.size())) {
+ oat_writer->size_oat_dex_file_lookup_table_offset_ += sizeof(lookup_table_offset_);
+ if (!oat_writer->WriteData(out, class_offsets_.data(), GetClassOffsetsRawSize())) {
PLOG(ERROR) << "Failed to write methods offsets to " << out->GetLocation();
return false;
}
- oat_writer->size_oat_dex_file_methods_offsets_ +=
- sizeof(methods_offsets_[0]) * methods_offsets_.size();
+ oat_writer->size_oat_dex_file_class_offsets_ += GetClassOffsetsRawSize();
return true;
}
OatWriter::OatClass::OatClass(size_t offset,
- const std::vector<CompiledMethod*>& compiled_methods,
+ const dchecked_vector<CompiledMethod*>& compiled_methods,
uint32_t num_non_null_compiled_methods,
mirror::Class::Status status)
: compiled_methods_(compiled_methods) {
@@ -1593,7 +1684,7 @@
uint32_t oat_method_offsets_offset_from_oat_class = sizeof(type_) + sizeof(status_);
if (type_ == kOatClassSomeCompiled) {
- method_bitmap_ = new BitVector(num_methods, false, Allocator::GetMallocAllocator());
+ method_bitmap_.reset(new BitVector(num_methods, false, Allocator::GetMallocAllocator()));
method_bitmap_size_ = method_bitmap_->GetSizeOf();
oat_method_offsets_offset_from_oat_class += sizeof(method_bitmap_size_);
oat_method_offsets_offset_from_oat_class += method_bitmap_size_;
@@ -1616,10 +1707,6 @@
}
}
-OatWriter::OatClass::~OatClass() {
- delete method_bitmap_;
-}
-
size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatHeader(
size_t class_def_method_index_) const {
uint32_t method_offset = GetOatMethodOffsetsOffsetFromOatClass(class_def_method_index_);
@@ -1642,51 +1729,42 @@
+ (sizeof(method_offsets_[0]) * method_offsets_.size());
}
-void OatWriter::OatClass::UpdateChecksum(OatHeader* oat_header) const {
- oat_header->UpdateChecksum(&status_, sizeof(status_));
- oat_header->UpdateChecksum(&type_, sizeof(type_));
- if (method_bitmap_size_ != 0) {
- CHECK_EQ(kOatClassSomeCompiled, type_);
- oat_header->UpdateChecksum(&method_bitmap_size_, sizeof(method_bitmap_size_));
- oat_header->UpdateChecksum(method_bitmap_->GetRawStorage(), method_bitmap_size_);
- }
- oat_header->UpdateChecksum(&method_offsets_[0],
- sizeof(method_offsets_[0]) * method_offsets_.size());
-}
-
bool OatWriter::OatClass::Write(OatWriter* oat_writer,
OutputStream* out,
const size_t file_offset) const {
DCHECK_OFFSET_();
- if (!out->WriteFully(&status_, sizeof(status_))) {
+ if (!oat_writer->WriteData(out, &status_, sizeof(status_))) {
PLOG(ERROR) << "Failed to write class status to " << out->GetLocation();
return false;
}
oat_writer->size_oat_class_status_ += sizeof(status_);
- if (!out->WriteFully(&type_, sizeof(type_))) {
+
+ if (!oat_writer->WriteData(out, &type_, sizeof(type_))) {
PLOG(ERROR) << "Failed to write oat class type to " << out->GetLocation();
return false;
}
oat_writer->size_oat_class_type_ += sizeof(type_);
+
if (method_bitmap_size_ != 0) {
CHECK_EQ(kOatClassSomeCompiled, type_);
- if (!out->WriteFully(&method_bitmap_size_, sizeof(method_bitmap_size_))) {
+ if (!oat_writer->WriteData(out, &method_bitmap_size_, sizeof(method_bitmap_size_))) {
PLOG(ERROR) << "Failed to write method bitmap size to " << out->GetLocation();
return false;
}
oat_writer->size_oat_class_method_bitmaps_ += sizeof(method_bitmap_size_);
- if (!out->WriteFully(method_bitmap_->GetRawStorage(), method_bitmap_size_)) {
+
+ if (!oat_writer->WriteData(out, method_bitmap_->GetRawStorage(), method_bitmap_size_)) {
PLOG(ERROR) << "Failed to write method bitmap to " << out->GetLocation();
return false;
}
oat_writer->size_oat_class_method_bitmaps_ += method_bitmap_size_;
}
- if (!out->WriteFully(&method_offsets_[0],
- sizeof(method_offsets_[0]) * method_offsets_.size())) {
+
+ if (!oat_writer->WriteData(out, method_offsets_.data(), GetMethodOffsetsRawSize())) {
PLOG(ERROR) << "Failed to write method offsets to " << out->GetLocation();
return false;
}
- oat_writer->size_oat_class_method_offsets_ += sizeof(method_offsets_[0]) * method_offsets_.size();
+ oat_writer->size_oat_class_method_offsets_ += GetMethodOffsetsRawSize();
return true;
}
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 6c46ebc..5feb5fc 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -21,12 +21,14 @@
#include <cstddef>
#include <memory>
+#include "base/dchecked_vector.h"
#include "linker/relative_patcher.h" // For linker::RelativePatcherTargetProvider.
#include "mem_map.h"
#include "method_reference.h"
#include "mirror/class.h"
#include "oat.h"
#include "safe_map.h"
+#include "utils/array_ref.h"
namespace art {
@@ -124,8 +126,8 @@
return bss_size_;
}
- const std::vector<uintptr_t>& GetAbsolutePatchLocations() const {
- return absolute_patch_locations_;
+ ArrayRef<const uintptr_t> GetAbsolutePatchLocations() const {
+ return ArrayRef<const uintptr_t>(absolute_patch_locations_);
}
bool WriteRodata(OutputStream* out);
@@ -133,8 +135,8 @@
~OatWriter();
- const std::vector<dwarf::MethodDebugInfo>& GetMethodDebugInfo() const {
- return method_info_;
+ ArrayRef<const dwarf::MethodDebugInfo> GetMethodDebugInfo() const {
+ return ArrayRef<const dwarf::MethodDebugInfo>(method_info_);
}
const CompilerDriver* GetCompilerDriver() {
@@ -142,6 +144,9 @@
}
private:
+ class OatClass;
+ class OatDexFile;
+
// The DataAccess classes are helper classes that provide access to members related to
// a given map, i.e. GC map, mapping table or vmap table. By abstracting these away
// we can share a lot of code for processing the maps with template classes below.
@@ -175,10 +180,8 @@
size_t InitDexFiles(size_t offset);
size_t InitOatClasses(size_t offset);
size_t InitOatMaps(size_t offset);
- size_t InitOatCode(size_t offset)
- SHARED_REQUIRES(Locks::mutator_lock_);
- size_t InitOatCodeDexFiles(size_t offset)
- SHARED_REQUIRES(Locks::mutator_lock_);
+ size_t InitOatCode(size_t offset);
+ size_t InitOatCodeDexFiles(size_t offset);
bool WriteTables(OutputStream* out, const size_t file_offset);
bool WriteLookupTables(OutputStream* out, const size_t file_offset);
@@ -186,93 +189,11 @@
size_t WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset);
size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset);
+ bool GetOatDataOffset(OutputStream* out);
bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta);
+ bool WriteData(OutputStream* out, const void* data, size_t size);
- class OatDexFile {
- public:
- OatDexFile(size_t offset, const DexFile& dex_file);
- size_t SizeOf() const;
- void UpdateChecksum(OatHeader* oat_header) const;
- bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
-
- // Offset of start of OatDexFile from beginning of OatHeader. It is
- // used to validate file position when writing.
- size_t offset_;
-
- // data to write
- uint32_t dex_file_location_size_;
- const uint8_t* dex_file_location_data_;
- uint32_t dex_file_location_checksum_;
- uint32_t dex_file_offset_;
- uint32_t lookup_table_offset_;
- TypeLookupTable* lookup_table_; // Owned by the dex file.
- std::vector<uint32_t> methods_offsets_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(OatDexFile);
- };
-
- class OatClass {
- public:
- OatClass(size_t offset,
- const std::vector<CompiledMethod*>& compiled_methods,
- uint32_t num_non_null_compiled_methods,
- mirror::Class::Status status);
- ~OatClass();
- size_t GetOatMethodOffsetsOffsetFromOatHeader(size_t class_def_method_index_) const;
- size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const;
- size_t SizeOf() const;
- void UpdateChecksum(OatHeader* oat_header) const;
- bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
-
- CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const {
- DCHECK_LT(class_def_method_index, compiled_methods_.size());
- return compiled_methods_[class_def_method_index];
- }
-
- // Offset of start of OatClass from beginning of OatHeader. It is
- // used to validate file position when writing.
- size_t offset_;
-
- // CompiledMethods for each class_def_method_index, or null if no method is available.
- std::vector<CompiledMethod*> compiled_methods_;
-
- // Offset from OatClass::offset_ to the OatMethodOffsets for the
- // class_def_method_index. If 0, it means the corresponding
- // CompiledMethod entry in OatClass::compiled_methods_ should be
- // null and that the OatClass::type_ should be kOatClassBitmap.
- std::vector<uint32_t> oat_method_offsets_offsets_from_oat_class_;
-
- // data to write
-
- static_assert(mirror::Class::Status::kStatusMax < (2 ^ 16), "class status won't fit in 16bits");
- int16_t status_;
-
- static_assert(OatClassType::kOatClassMax < (2 ^ 16), "oat_class type won't fit in 16bits");
- uint16_t type_;
-
- uint32_t method_bitmap_size_;
-
- // bit vector indexed by ClassDef method index. When
- // OatClassType::type_ is kOatClassBitmap, a set bit indicates the
- // method has an OatMethodOffsets in methods_offsets_, otherwise
- // the entry was ommited to save space. If OatClassType::type_ is
- // not is kOatClassBitmap, the bitmap will be null.
- BitVector* method_bitmap_;
-
- // OatMethodOffsets and OatMethodHeaders for each CompiledMethod
- // present in the OatClass. Note that some may be missing if
- // OatClass::compiled_methods_ contains null values (and
- // oat_method_offsets_offsets_from_oat_class_ should contain 0
- // values in this case).
- std::vector<OatMethodOffsets> method_offsets_;
- std::vector<OatQuickMethodHeader> method_headers_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(OatClass);
- };
-
- std::vector<dwarf::MethodDebugInfo> method_info_;
+ dchecked_vector<dwarf::MethodDebugInfo> method_info_;
const CompilerDriver* const compiler_driver_;
ImageWriter* const image_writer_;
@@ -301,9 +222,9 @@
// data to write
SafeMap<std::string, std::string>* key_value_store_;
- OatHeader* oat_header_;
- std::vector<OatDexFile*> oat_dex_files_;
- std::vector<OatClass*> oat_classes_;
+ std::unique_ptr<OatHeader> oat_header_;
+ dchecked_vector<OatDexFile> oat_dex_files_;
+ dchecked_vector<OatClass> oat_classes_;
std::unique_ptr<const std::vector<uint8_t>> jni_dlsym_lookup_;
std::unique_ptr<const std::vector<uint8_t>> quick_generic_jni_trampoline_;
std::unique_ptr<const std::vector<uint8_t>> quick_imt_conflict_trampoline_;
@@ -336,14 +257,14 @@
uint32_t size_oat_dex_file_location_data_;
uint32_t size_oat_dex_file_location_checksum_;
uint32_t size_oat_dex_file_offset_;
- uint32_t size_oat_dex_file_methods_offsets_;
+ uint32_t size_oat_dex_file_lookup_table_offset_;
+ uint32_t size_oat_dex_file_class_offsets_;
+ uint32_t size_oat_lookup_table_alignment_;
+ uint32_t size_oat_lookup_table_;
uint32_t size_oat_class_type_;
uint32_t size_oat_class_status_;
uint32_t size_oat_class_method_bitmaps_;
uint32_t size_oat_class_method_offsets_;
- uint32_t size_oat_lookup_table_alignment_;
- uint32_t size_oat_lookup_table_offset_;
- uint32_t size_oat_lookup_table_;
std::unique_ptr<linker::RelativePatcher> relative_patcher_;
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 7dbfd7c..4c3f66a 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -1138,8 +1138,8 @@
void VisitArrayGet(HArrayGet* array_get) OVERRIDE {
if (!has_deoptimization_on_constant_subscripts_ && array_get->IsInLoop()) {
HLoopInformation* loop = array_get->GetBlock()->GetLoopInformation();
- if (loop->IsLoopInvariant(array_get->InputAt(0), false) &&
- loop->IsLoopInvariant(array_get->InputAt(1), false)) {
+ if (loop->IsDefinedOutOfTheLoop(array_get->InputAt(0)) &&
+ loop->IsDefinedOutOfTheLoop(array_get->InputAt(1))) {
SideEffects loop_effects = side_effects_.GetLoopEffects(loop->GetHeader());
if (!array_get->GetSideEffects().MayDependOn(loop_effects)) {
HoistToPreheaderOrDeoptBlock(loop, array_get);
@@ -1349,7 +1349,7 @@
* by handling the null check under the hood of the array length operation.
*/
bool CanHandleLength(HLoopInformation* loop, HInstruction* length, bool needs_taken_test) {
- if (loop->IsLoopInvariant(length, false)) {
+ if (loop->IsDefinedOutOfTheLoop(length)) {
return true;
} else if (length->IsArrayLength() && length->GetBlock()->GetLoopInformation() == loop) {
if (CanHandleNullCheck(loop, length->InputAt(0), needs_taken_test)) {
@@ -1365,11 +1365,11 @@
* by generating a deoptimization test.
*/
bool CanHandleNullCheck(HLoopInformation* loop, HInstruction* check, bool needs_taken_test) {
- if (loop->IsLoopInvariant(check, false)) {
+ if (loop->IsDefinedOutOfTheLoop(check)) {
return true;
} else if (check->IsNullCheck() && check->GetBlock()->GetLoopInformation() == loop) {
HInstruction* array = check->InputAt(0);
- if (loop->IsLoopInvariant(array, false)) {
+ if (loop->IsDefinedOutOfTheLoop(array)) {
// Generate: if (array == null) deoptimize;
HBasicBlock* block = TransformLoopForDeoptimizationIfNeeded(loop, needs_taken_test);
HInstruction* cond =
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 2bbf500..1178d0f 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -785,7 +785,7 @@
ArtMethod* actual_method = compiling_class->GetSuperClass()->GetVTableEntry(
vtable_index, class_linker->GetImagePointerSize());
if (actual_method != resolved_method &&
- !IsSameDexFile(*resolved_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
+ !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
// TODO: The actual method could still be referenced in the current dex file, so we
// could try locating it.
// TODO: Remove the dex_file restriction.
@@ -2841,15 +2841,21 @@
}
case Instruction::CONST_STRING: {
+ uint32_t string_index = instruction.VRegB_21c();
+ bool in_dex_cache = compiler_driver_->CanAssumeStringIsPresentInDexCache(
+ *dex_file_, string_index);
current_block_->AddInstruction(
- new (arena_) HLoadString(graph_->GetCurrentMethod(), instruction.VRegB_21c(), dex_pc));
+ new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, dex_pc, in_dex_cache));
UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction(), dex_pc);
break;
}
case Instruction::CONST_STRING_JUMBO: {
+ uint32_t string_index = instruction.VRegB_31c();
+ bool in_dex_cache = compiler_driver_->CanAssumeStringIsPresentInDexCache(
+ *dex_file_, string_index);
current_block_->AddInstruction(
- new (arena_) HLoadString(graph_->GetCurrentMethod(), instruction.VRegB_31c(), dex_pc));
+ new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, dex_pc, in_dex_cache));
UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction(), dex_pc);
break;
}
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 0baa0e3..53d3615 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -246,10 +246,7 @@
InitLocationsBaseline(current);
}
DCHECK(CheckTypeConsistency(current));
- uintptr_t native_pc_begin = GetAssembler()->CodeSize();
current->Accept(instruction_visitor);
- uintptr_t native_pc_end = GetAssembler()->CodeSize();
- RecordNativeDebugInfo(current->GetDexPc(), native_pc_begin, native_pc_end);
}
}
@@ -926,17 +923,6 @@
stack_map_stream_.FillIn(region);
}
-void CodeGenerator::RecordNativeDebugInfo(uint32_t dex_pc,
- uintptr_t native_pc_begin,
- uintptr_t native_pc_end) {
- if (compiler_options_.GetGenerateDebugInfo() &&
- dex_pc != kNoDexPc &&
- native_pc_begin != native_pc_end) {
- src_map_.push_back(SrcMapElem({static_cast<uint32_t>(native_pc_begin),
- static_cast<int32_t>(dex_pc)}));
- }
-}
-
void CodeGenerator::RecordPcInfo(HInstruction* instruction,
uint32_t dex_pc,
SlowPathCode* slow_path) {
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 114d97b..eade05d 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -269,8 +269,6 @@
// Record native to dex mapping for a suspend point. Required by runtime.
void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path = nullptr);
- // Record additional native to dex mappings for native debugging/profiling tools.
- void RecordNativeDebugInfo(uint32_t dex_pc, uintptr_t native_pc_begin, uintptr_t native_pc_end);
bool CanMoveNullCheckToUser(HNullCheck* null_check);
void MaybeRecordImplicitNullCheck(HInstruction* instruction);
@@ -452,10 +450,6 @@
// Copy the result of a call into the given target.
virtual void MoveFromReturnRegister(Location trg, Primitive::Type type) = 0;
- const ArenaVector<SrcMapElem>& GetSrcMappingTable() const {
- return src_map_;
- }
-
protected:
// Method patch info used for recording locations of required linker patches and
// target methods. The target method can be used for various purposes, whether for
@@ -498,7 +492,6 @@
stats_(stats),
graph_(graph),
compiler_options_(compiler_options),
- src_map_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
slow_paths_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
current_slow_path_(nullptr),
current_block_index_(0),
@@ -616,8 +609,6 @@
HGraph* const graph_;
const CompilerOptions& compiler_options_;
- // Native to dex_pc map used for native debugging/profiling tools.
- ArenaVector<SrcMapElem> src_map_;
ArenaVector<SlowPathCode*> slow_paths_;
// The current slow path that we're generating code for.
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 0a26786..a999c4c 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -5067,16 +5067,15 @@
}
void LocationsBuilderARM::VisitLoadString(HLoadString* load) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+ LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) {
- SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load);
- codegen_->AddSlowPath(slow_path);
-
LocationSummary* locations = load->GetLocations();
Location out_loc = locations->Out();
Register out = out_loc.AsRegister<Register>();
@@ -5107,8 +5106,12 @@
__ LoadFromOffset(kLoadWord, out, out, cache_offset);
}
- __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
- __ Bind(slow_path->GetExitLabel());
+ if (!load->IsInDexCache()) {
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM(load);
+ codegen_->AddSlowPath(slow_path);
+ __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+ }
}
static int32_t GetExceptionTlsOffset() {
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 227f4be..f6834d4 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -3850,16 +3850,15 @@
}
void LocationsBuilderARM64::VisitLoadString(HLoadString* load) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+ LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) {
- SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM64(load);
- codegen_->AddSlowPath(slow_path);
-
Location out_loc = load->GetLocations()->Out();
Register out = OutputRegister(load);
Register current_method = InputRegisterAt(load, 0);
@@ -3889,8 +3888,12 @@
__ Ldr(out, MemOperand(out.X(), cache_offset));
}
- __ Cbz(out, slow_path->GetEntryLabel());
- __ Bind(slow_path->GetExitLabel());
+ if (!load->IsInDexCache()) {
+ SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM64(load);
+ codegen_->AddSlowPath(slow_path);
+ __ Cbz(out, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+ }
}
void LocationsBuilderARM64::VisitLocal(HLocal* local) {
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index f872bfe..b000c77 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -2218,6 +2218,171 @@
}
}
+void InstructionCodeGeneratorMIPS::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+ DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimInt);
+
+ LocationSummary* locations = instruction->GetLocations();
+ Location second = locations->InAt(1);
+ DCHECK(second.IsConstant());
+
+ Register out = locations->Out().AsRegister<Register>();
+ Register dividend = locations->InAt(0).AsRegister<Register>();
+ int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+ DCHECK(imm == 1 || imm == -1);
+
+ if (instruction->IsRem()) {
+ __ Move(out, ZERO);
+ } else {
+ if (imm == -1) {
+ __ Subu(out, ZERO, dividend);
+ } else if (out != dividend) {
+ __ Move(out, dividend);
+ }
+ }
+}
+
+void InstructionCodeGeneratorMIPS::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+ DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimInt);
+
+ LocationSummary* locations = instruction->GetLocations();
+ Location second = locations->InAt(1);
+ DCHECK(second.IsConstant());
+
+ 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));
+ int ctz_imm = CTZ(abs_imm);
+
+ if (instruction->IsDiv()) {
+ if (ctz_imm == 1) {
+ // Fast path for division by +/-2, which is very common.
+ __ Srl(TMP, dividend, 31);
+ } else {
+ __ Sra(TMP, dividend, 31);
+ __ Srl(TMP, TMP, 32 - ctz_imm);
+ }
+ __ Addu(out, dividend, TMP);
+ __ Sra(out, out, ctz_imm);
+ if (imm < 0) {
+ __ Subu(out, ZERO, out);
+ }
+ } else {
+ if (ctz_imm == 1) {
+ // Fast path for modulo +/-2, which is very common.
+ __ Sra(TMP, dividend, 31);
+ __ Subu(out, dividend, TMP);
+ __ Andi(out, out, 1);
+ __ Addu(out, out, TMP);
+ } else {
+ __ Sra(TMP, dividend, 31);
+ __ Srl(TMP, TMP, 32 - ctz_imm);
+ __ Addu(out, dividend, TMP);
+ if (IsUint<16>(abs_imm - 1)) {
+ __ Andi(out, out, abs_imm - 1);
+ } else {
+ __ Sll(out, out, 32 - ctz_imm);
+ __ Srl(out, out, 32 - ctz_imm);
+ }
+ __ Subu(out, out, TMP);
+ }
+ }
+}
+
+void InstructionCodeGeneratorMIPS::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+ DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimInt);
+
+ LocationSummary* locations = instruction->GetLocations();
+ Location second = locations->InAt(1);
+ DCHECK(second.IsConstant());
+
+ Register out = locations->Out().AsRegister<Register>();
+ Register dividend = locations->InAt(0).AsRegister<Register>();
+ int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+
+ int64_t magic;
+ int shift;
+ CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
+
+ bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
+
+ __ LoadConst32(TMP, magic);
+ if (isR6) {
+ __ MuhR6(TMP, dividend, TMP);
+ } else {
+ __ MultR2(dividend, TMP);
+ __ Mfhi(TMP);
+ }
+ if (imm > 0 && magic < 0) {
+ __ Addu(TMP, TMP, dividend);
+ } else if (imm < 0 && magic > 0) {
+ __ Subu(TMP, TMP, dividend);
+ }
+
+ if (shift != 0) {
+ __ Sra(TMP, TMP, shift);
+ }
+
+ if (instruction->IsDiv()) {
+ __ Sra(out, TMP, 31);
+ __ Subu(out, TMP, out);
+ } else {
+ __ Sra(AT, TMP, 31);
+ __ Subu(AT, TMP, AT);
+ __ LoadConst32(TMP, imm);
+ if (isR6) {
+ __ MulR6(TMP, AT, TMP);
+ } else {
+ __ MulR2(TMP, AT, TMP);
+ }
+ __ Subu(out, dividend, TMP);
+ }
+}
+
+void InstructionCodeGeneratorMIPS::GenerateDivRemIntegral(HBinaryOperation* instruction) {
+ DCHECK(instruction->IsDiv() || instruction->IsRem());
+ DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimInt);
+
+ LocationSummary* locations = instruction->GetLocations();
+ Register out = locations->Out().AsRegister<Register>();
+ Location second = locations->InAt(1);
+
+ if (second.IsConstant()) {
+ int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+ if (imm == 0) {
+ // 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))) {
+ DivRemByPowerOfTwo(instruction);
+ } else {
+ DCHECK(imm <= -2 || imm >= 2);
+ GenerateDivRemWithAnyConstant(instruction);
+ }
+ } else {
+ Register dividend = locations->InAt(0).AsRegister<Register>();
+ Register divisor = second.AsRegister<Register>();
+ bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
+ if (instruction->IsDiv()) {
+ if (isR6) {
+ __ DivR6(out, dividend, divisor);
+ } else {
+ __ DivR2(out, dividend, divisor);
+ }
+ } else {
+ if (isR6) {
+ __ ModR6(out, dividend, divisor);
+ } else {
+ __ ModR2(out, dividend, divisor);
+ }
+ }
+ }
+}
+
void LocationsBuilderMIPS::VisitDiv(HDiv* div) {
Primitive::Type type = div->GetResultType();
LocationSummary::CallKind call_kind = (type == Primitive::kPrimLong)
@@ -2229,7 +2394,7 @@
switch (type) {
case Primitive::kPrimInt:
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(div->InputAt(1)));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
@@ -2258,20 +2423,11 @@
void InstructionCodeGeneratorMIPS::VisitDiv(HDiv* instruction) {
Primitive::Type type = instruction->GetType();
LocationSummary* locations = instruction->GetLocations();
- bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
switch (type) {
- case Primitive::kPrimInt: {
- Register dst = locations->Out().AsRegister<Register>();
- Register lhs = locations->InAt(0).AsRegister<Register>();
- Register rhs = locations->InAt(1).AsRegister<Register>();
- if (isR6) {
- __ DivR6(dst, lhs, rhs);
- } else {
- __ DivR2(dst, lhs, rhs);
- }
+ case Primitive::kPrimInt:
+ GenerateDivRemIntegral(instruction);
break;
- }
case Primitive::kPrimLong: {
codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLdiv),
instruction,
@@ -3257,24 +3413,28 @@
}
void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+ LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) {
- SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS(load);
- codegen_->AddSlowPath(slow_path);
-
LocationSummary* locations = load->GetLocations();
Register out = locations->Out().AsRegister<Register>();
Register current_method = locations->InAt(0).AsRegister<Register>();
__ LoadFromOffset(kLoadWord, out, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
__ LoadFromOffset(kLoadWord, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
__ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
- __ Beqz(out, slow_path->GetEntryLabel());
- __ Bind(slow_path->GetExitLabel());
+
+ if (!load->IsInDexCache()) {
+ SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS(load);
+ codegen_->AddSlowPath(slow_path);
+ __ Beqz(out, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+ }
}
void LocationsBuilderMIPS::VisitLocal(HLocal* local) {
@@ -3666,7 +3826,7 @@
switch (type) {
case Primitive::kPrimInt:
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(rem->InputAt(1)));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
break;
@@ -3696,21 +3856,11 @@
void InstructionCodeGeneratorMIPS::VisitRem(HRem* instruction) {
Primitive::Type type = instruction->GetType();
- LocationSummary* locations = instruction->GetLocations();
- bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
switch (type) {
- case Primitive::kPrimInt: {
- Register dst = locations->Out().AsRegister<Register>();
- Register lhs = locations->InAt(0).AsRegister<Register>();
- Register rhs = locations->InAt(1).AsRegister<Register>();
- if (isR6) {
- __ ModR6(dst, lhs, rhs);
- } else {
- __ ModR2(dst, lhs, rhs);
- }
+ case Primitive::kPrimInt:
+ GenerateDivRemIntegral(instruction);
break;
- }
case Primitive::kPrimLong: {
codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLmod),
instruction,
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index e3a2cb4..caf3174 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -229,6 +229,10 @@
size_t condition_input_index,
MipsLabel* true_target,
MipsLabel* false_target);
+ void DivRemOneOrMinusOne(HBinaryOperation* instruction);
+ void DivRemByPowerOfTwo(HBinaryOperation* instruction);
+ void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
+ void GenerateDivRemIntegral(HBinaryOperation* instruction);
void HandleGoto(HInstruction* got, HBasicBlock* successor);
MipsAssembler* const assembler_;
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 78f5644..9f8558d 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -3105,16 +3105,15 @@
}
void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+ LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) {
- SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load);
- codegen_->AddSlowPath(slow_path);
-
LocationSummary* locations = load->GetLocations();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
GpuRegister current_method = locations->InAt(0).AsRegister<GpuRegister>();
@@ -3123,8 +3122,13 @@
__ LoadFromOffset(kLoadDoubleword, out, out, mirror::Class::DexCacheStringsOffset().Int32Value());
__ LoadFromOffset(kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(load->GetStringIndex()));
// TODO: We will need a read barrier here.
- __ Beqzc(out, slow_path->GetEntryLabel());
- __ Bind(slow_path->GetExitLabel());
+
+ if (!load->IsInDexCache()) {
+ SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load);
+ codegen_->AddSlowPath(slow_path);
+ __ Beqzc(out, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+ }
}
void LocationsBuilderMIPS64::VisitLocal(HLocal* local) {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 19f03df..8c50650 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -5611,16 +5611,15 @@
}
void LocationsBuilderX86::VisitLoadString(HLoadString* load) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+ LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) {
- SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86(load);
- codegen_->AddSlowPath(slow_path);
-
LocationSummary* locations = load->GetLocations();
Location out_loc = locations->Out();
Register out = out_loc.AsRegister<Register>();
@@ -5651,9 +5650,13 @@
__ movl(out, Address(out, cache_offset));
}
- __ testl(out, out);
- __ j(kEqual, slow_path->GetEntryLabel());
- __ Bind(slow_path->GetExitLabel());
+ if (!load->IsInDexCache()) {
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86(load);
+ codegen_->AddSlowPath(slow_path);
+ __ testl(out, out);
+ __ j(kEqual, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+ }
}
static Address GetExceptionTlsAddress() {
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 44a51ea..9cb6a2c 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -5220,16 +5220,15 @@
}
void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath);
+ LocationSummary::CallKind call_kind = (!load->IsInDexCache() || kEmitCompilerReadBarrier)
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) {
- SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86_64(load);
- codegen_->AddSlowPath(slow_path);
-
LocationSummary* locations = load->GetLocations();
Location out_loc = locations->Out();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
@@ -5260,9 +5259,13 @@
__ movl(out, Address(out, cache_offset));
}
- __ testl(out, out);
- __ j(kEqual, slow_path->GetEntryLabel());
- __ Bind(slow_path->GetExitLabel());
+ if (!load->IsInDexCache()) {
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathX86_64(load);
+ codegen_->AddSlowPath(slow_path);
+ __ testl(out, out);
+ __ j(kEqual, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+ }
}
static Address GetExceptionTlsAddress() {
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 145b1f3..7351fed 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -47,6 +47,12 @@
static constexpr size_t kRuntimeParameterFpuRegistersLength =
arraysize(kRuntimeParameterFpuRegisters);
+// These XMM registers are non-volatile in ART ABI, but volatile in native ABI.
+// If the ART ABI changes, this list must be updated. It is used to ensure that
+// these are not clobbered by any direct call to native code (such as math intrinsics).
+static constexpr FloatRegister non_volatile_xmm_regs[] = { XMM12, XMM13, XMM14, XMM15 };
+
+
class InvokeRuntimeCallingConvention : public CallingConvention<Register, FloatRegister> {
public:
InvokeRuntimeCallingConvention()
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index fdf8cc9..0b7fdf8 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -705,7 +705,8 @@
return loop_it->second;
}
}
- if (loop->IsLoopInvariant(instruction, true)) {
+ if (loop->IsDefinedOutOfTheLoop(instruction)) {
+ DCHECK(instruction->GetBlock()->Dominates(loop->GetPreHeader()));
InductionInfo* info = CreateInvariantFetch(instruction);
AssignInfo(loop, instruction, info);
return info;
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 8340811..7127215 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -189,6 +189,42 @@
return ((method.d.data & kIntrinsicFlagMin) == 0) ?
Intrinsics::kMathMaxLongLong : Intrinsics::kMathMinLongLong;
+ // More math builtins.
+ case kIntrinsicCos:
+ return Intrinsics::kMathCos;
+ case kIntrinsicSin:
+ return Intrinsics::kMathSin;
+ case kIntrinsicAcos:
+ return Intrinsics::kMathAcos;
+ case kIntrinsicAsin:
+ return Intrinsics::kMathAsin;
+ case kIntrinsicAtan:
+ return Intrinsics::kMathAtan;
+ case kIntrinsicAtan2:
+ return Intrinsics::kMathAtan2;
+ case kIntrinsicCbrt:
+ return Intrinsics::kMathCbrt;
+ case kIntrinsicCosh:
+ return Intrinsics::kMathCosh;
+ case kIntrinsicExp:
+ return Intrinsics::kMathExp;
+ case kIntrinsicExpm1:
+ return Intrinsics::kMathExpm1;
+ case kIntrinsicHypot:
+ return Intrinsics::kMathHypot;
+ case kIntrinsicLog:
+ return Intrinsics::kMathLog;
+ case kIntrinsicLog10:
+ return Intrinsics::kMathLog10;
+ case kIntrinsicNextAfter:
+ return Intrinsics::kMathNextAfter;
+ case kIntrinsicSinh:
+ return Intrinsics::kMathSinh;
+ case kIntrinsicTan:
+ return Intrinsics::kMathTan;
+ case kIntrinsicTanh:
+ return Intrinsics::kMathTanh;
+
// Misc math.
case kIntrinsicSqrt:
return Intrinsics::kMathSqrt;
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 5329b5c..9820c5d 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -1718,6 +1718,23 @@
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
+UNIMPLEMENTED_INTRINSIC(MathCos)
+UNIMPLEMENTED_INTRINSIC(MathSin)
+UNIMPLEMENTED_INTRINSIC(MathAcos)
+UNIMPLEMENTED_INTRINSIC(MathAsin)
+UNIMPLEMENTED_INTRINSIC(MathAtan)
+UNIMPLEMENTED_INTRINSIC(MathAtan2)
+UNIMPLEMENTED_INTRINSIC(MathCbrt)
+UNIMPLEMENTED_INTRINSIC(MathCosh)
+UNIMPLEMENTED_INTRINSIC(MathExp)
+UNIMPLEMENTED_INTRINSIC(MathExpm1)
+UNIMPLEMENTED_INTRINSIC(MathHypot)
+UNIMPLEMENTED_INTRINSIC(MathLog)
+UNIMPLEMENTED_INTRINSIC(MathLog10)
+UNIMPLEMENTED_INTRINSIC(MathNextAfter)
+UNIMPLEMENTED_INTRINSIC(MathSinh)
+UNIMPLEMENTED_INTRINSIC(MathTan)
+UNIMPLEMENTED_INTRINSIC(MathTanh)
#undef UNIMPLEMENTED_INTRINSIC
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 962c4d5..2e5b022 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1532,6 +1532,24 @@
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
+UNIMPLEMENTED_INTRINSIC(MathCos)
+UNIMPLEMENTED_INTRINSIC(MathSin)
+UNIMPLEMENTED_INTRINSIC(MathAcos)
+UNIMPLEMENTED_INTRINSIC(MathAsin)
+UNIMPLEMENTED_INTRINSIC(MathAtan)
+UNIMPLEMENTED_INTRINSIC(MathAtan2)
+UNIMPLEMENTED_INTRINSIC(MathCbrt)
+UNIMPLEMENTED_INTRINSIC(MathCosh)
+UNIMPLEMENTED_INTRINSIC(MathExp)
+UNIMPLEMENTED_INTRINSIC(MathExpm1)
+UNIMPLEMENTED_INTRINSIC(MathHypot)
+UNIMPLEMENTED_INTRINSIC(MathLog)
+UNIMPLEMENTED_INTRINSIC(MathLog10)
+UNIMPLEMENTED_INTRINSIC(MathNextAfter)
+UNIMPLEMENTED_INTRINSIC(MathSinh)
+UNIMPLEMENTED_INTRINSIC(MathTan)
+UNIMPLEMENTED_INTRINSIC(MathTanh)
+
#undef UNIMPLEMENTED_INTRINSIC
#undef __
diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h
index 8f1d5e1..96f43a0 100644
--- a/compiler/optimizing/intrinsics_list.h
+++ b/compiler/optimizing/intrinsics_list.h
@@ -51,6 +51,23 @@
V(MathMaxFloatFloat, kStatic, kNeedsEnvironmentOrCache) \
V(MathMaxLongLong, kStatic, kNeedsEnvironmentOrCache) \
V(MathMaxIntInt, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathCos, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathSin, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathAcos, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathAsin, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathAtan, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathAtan2, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathCbrt, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathCosh, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathExp, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathExpm1, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathHypot, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathLog, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathLog10, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathNextAfter, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathSinh, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathTan, kStatic, kNeedsEnvironmentOrCache) \
+ V(MathTanh, kStatic, kNeedsEnvironmentOrCache) \
V(MathSqrt, kStatic, kNeedsEnvironmentOrCache) \
V(MathCeil, kStatic, kNeedsEnvironmentOrCache) \
V(MathFloor, kStatic, kNeedsEnvironmentOrCache) \
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 9ecce0e..06fab61 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -989,6 +989,23 @@
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(SystemArrayCopy)
+UNIMPLEMENTED_INTRINSIC(MathCos)
+UNIMPLEMENTED_INTRINSIC(MathSin)
+UNIMPLEMENTED_INTRINSIC(MathAcos)
+UNIMPLEMENTED_INTRINSIC(MathAsin)
+UNIMPLEMENTED_INTRINSIC(MathAtan)
+UNIMPLEMENTED_INTRINSIC(MathAtan2)
+UNIMPLEMENTED_INTRINSIC(MathCbrt)
+UNIMPLEMENTED_INTRINSIC(MathCosh)
+UNIMPLEMENTED_INTRINSIC(MathExp)
+UNIMPLEMENTED_INTRINSIC(MathExpm1)
+UNIMPLEMENTED_INTRINSIC(MathHypot)
+UNIMPLEMENTED_INTRINSIC(MathLog)
+UNIMPLEMENTED_INTRINSIC(MathLog10)
+UNIMPLEMENTED_INTRINSIC(MathNextAfter)
+UNIMPLEMENTED_INTRINSIC(MathSinh)
+UNIMPLEMENTED_INTRINSIC(MathTan)
+UNIMPLEMENTED_INTRINSIC(MathTanh)
#undef UNIMPLEMENTED_INTRINSIC
#undef __
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 36e1b20..8aa7d9f 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1730,6 +1730,24 @@
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(SystemArrayCopy)
+UNIMPLEMENTED_INTRINSIC(MathCos)
+UNIMPLEMENTED_INTRINSIC(MathSin)
+UNIMPLEMENTED_INTRINSIC(MathAcos)
+UNIMPLEMENTED_INTRINSIC(MathAsin)
+UNIMPLEMENTED_INTRINSIC(MathAtan)
+UNIMPLEMENTED_INTRINSIC(MathAtan2)
+UNIMPLEMENTED_INTRINSIC(MathCbrt)
+UNIMPLEMENTED_INTRINSIC(MathCosh)
+UNIMPLEMENTED_INTRINSIC(MathExp)
+UNIMPLEMENTED_INTRINSIC(MathExpm1)
+UNIMPLEMENTED_INTRINSIC(MathHypot)
+UNIMPLEMENTED_INTRINSIC(MathLog)
+UNIMPLEMENTED_INTRINSIC(MathLog10)
+UNIMPLEMENTED_INTRINSIC(MathNextAfter)
+UNIMPLEMENTED_INTRINSIC(MathSinh)
+UNIMPLEMENTED_INTRINSIC(MathTan)
+UNIMPLEMENTED_INTRINSIC(MathTanh)
+
#undef UNIMPLEMENTED_INTRINSIC
#undef __
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 5b67cde..3370e73 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -788,6 +788,195 @@
__ Bind(&done);
}
+static void CreateFPToFPCallLocations(ArenaAllocator* arena,
+ HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kCall,
+ kIntrinsified);
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
+ locations->SetOut(Location::FpuRegisterLocation(XMM0));
+}
+
+static void GenFPToFPCall(HInvoke* invoke, CodeGeneratorX86* codegen, QuickEntrypointEnum entry) {
+ LocationSummary* locations = invoke->GetLocations();
+ DCHECK(locations->WillCall());
+ DCHECK(invoke->IsInvokeStaticOrDirect());
+ X86Assembler* assembler = codegen->GetAssembler();
+
+ // We need some place to pass the parameters.
+ __ subl(ESP, Immediate(16));
+ __ cfi().AdjustCFAOffset(16);
+
+ // Pass the parameters at the bottom of the stack.
+ __ movsd(Address(ESP, 0), XMM0);
+
+ // If we have a second parameter, pass it next.
+ if (invoke->GetNumberOfArguments() == 2) {
+ __ movsd(Address(ESP, 8), XMM1);
+ }
+
+ // Now do the actual call.
+ __ fs()->call(Address::Absolute(GetThreadOffset<kX86WordSize>(entry)));
+
+ // Extract the return value from the FP stack.
+ __ fstpl(Address(ESP, 0));
+ __ movsd(XMM0, Address(ESP, 0));
+
+ // And clean up the stack.
+ __ addl(ESP, Immediate(16));
+ __ cfi().AdjustCFAOffset(-16);
+
+ codegen->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathCos(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathCos(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickCos);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathSin(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathSin(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickSin);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathAcos(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathAcos(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickAcos);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathAsin(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathAsin(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickAsin);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathAtan(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathAtan(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickAtan);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathCbrt(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathCbrt(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickCbrt);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathCosh(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathCosh(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickCosh);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathExp(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathExp(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickExp);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathExpm1(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathExpm1(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickExpm1);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathLog(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathLog(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickLog);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathLog10(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathLog10(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickLog10);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathSinh(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathSinh(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickSinh);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathTan(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathTan(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickTan);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathTanh(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathTanh(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickTanh);
+}
+
+static void CreateFPFPToFPCallLocations(ArenaAllocator* arena,
+ HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kCall,
+ kIntrinsified);
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
+ locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
+ locations->SetOut(Location::FpuRegisterLocation(XMM0));
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathAtan2(HInvoke* invoke) {
+ CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathAtan2(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickAtan2);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathHypot(HInvoke* invoke) {
+ CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathHypot(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickHypot);
+}
+
+void IntrinsicLocationsBuilderX86::VisitMathNextAfter(HInvoke* invoke) {
+ CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitMathNextAfter(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
+}
+
void IntrinsicLocationsBuilderX86::VisitStringCharAt(HInvoke* invoke) {
// The inputs plus one temp.
LocationSummary* locations = new (arena_) LocationSummary(invoke,
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index ecd129f..e862573 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -703,6 +703,188 @@
__ Bind(&done);
}
+static void CreateFPToFPCallLocations(ArenaAllocator* arena,
+ HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kCall,
+ kIntrinsified);
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
+ locations->SetOut(Location::FpuRegisterLocation(XMM0));
+
+ // We have to ensure that the native code doesn't clobber the XMM registers which are
+ // non-volatile for ART, but volatile for Native calls. This will ensure that they are
+ // saved in the prologue and properly restored.
+ for (auto fp_reg : non_volatile_xmm_regs) {
+ locations->AddTemp(Location::FpuRegisterLocation(fp_reg));
+ }
+}
+
+static void GenFPToFPCall(HInvoke* invoke, CodeGeneratorX86_64* codegen,
+ QuickEntrypointEnum entry) {
+ LocationSummary* locations = invoke->GetLocations();
+ DCHECK(locations->WillCall());
+ DCHECK(invoke->IsInvokeStaticOrDirect());
+ X86_64Assembler* assembler = codegen->GetAssembler();
+
+ __ gs()->call(Address::Absolute(GetThreadOffset<kX86_64WordSize>(entry), true));
+ codegen->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathCos(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathCos(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickCos);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathSin(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathSin(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickSin);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAcos(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAcos(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickAcos);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAsin(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAsin(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickAsin);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAtan(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAtan(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickAtan);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathCbrt(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathCbrt(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickCbrt);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathCosh(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathCosh(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickCosh);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathExp(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathExp(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickExp);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathExpm1(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathExpm1(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickExpm1);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathLog(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathLog(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickLog);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathLog10(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathLog10(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickLog10);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathSinh(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathSinh(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickSinh);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathTan(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathTan(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickTan);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathTanh(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathTanh(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickTanh);
+}
+
+static void CreateFPFPToFPCallLocations(ArenaAllocator* arena,
+ HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kCall,
+ kIntrinsified);
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0)));
+ locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1)));
+ locations->SetOut(Location::FpuRegisterLocation(XMM0));
+
+ // We have to ensure that the native code doesn't clobber the XMM registers which are
+ // non-volatile for ART, but volatile for Native calls. This will ensure that they are
+ // saved in the prologue and properly restored.
+ for (auto fp_reg : non_volatile_xmm_regs) {
+ locations->AddTemp(Location::FpuRegisterLocation(fp_reg));
+ }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathAtan2(HInvoke* invoke) {
+ CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathAtan2(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickAtan2);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathHypot(HInvoke* invoke) {
+ CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathHypot(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickHypot);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitMathNextAfter(HInvoke* invoke) {
+ CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMathNextAfter(HInvoke* invoke) {
+ GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
+}
+
void IntrinsicLocationsBuilderX86_64::VisitStringCharAt(HInvoke* invoke) {
// The inputs plus one temp.
LocationSummary* locations = new (arena_) LocationSummary(invoke,
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 680f89f..adde004 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -335,16 +335,24 @@
return true;
}
- ReferenceInfo* GetOrCreateReferenceInfo(HInstruction* ref) {
- ReferenceInfo* ref_info = FindReferenceInfoOf(ref);
+ ReferenceInfo* GetOrCreateReferenceInfo(HInstruction* instruction) {
+ ReferenceInfo* ref_info = FindReferenceInfoOf(instruction);
if (ref_info == nullptr) {
size_t pos = ref_info_array_.size();
- ref_info = new (GetGraph()->GetArena()) ReferenceInfo(ref, pos);
+ ref_info = new (GetGraph()->GetArena()) ReferenceInfo(instruction, pos);
ref_info_array_.push_back(ref_info);
}
return ref_info;
}
+ void CreateReferenceInfoForReferenceType(HInstruction* instruction) {
+ if (instruction->GetType() != Primitive::kPrimNot) {
+ return;
+ }
+ DCHECK(FindReferenceInfoOf(instruction) == nullptr);
+ GetOrCreateReferenceInfo(instruction);
+ }
+
HeapLocation* GetOrCreateHeapLocation(HInstruction* ref,
size_t offset,
HInstruction* index,
@@ -378,6 +386,7 @@
void VisitInstanceFieldGet(HInstanceFieldGet* instruction) OVERRIDE {
VisitFieldAccess(instruction->InputAt(0), instruction->GetFieldInfo());
+ CreateReferenceInfoForReferenceType(instruction);
}
void VisitInstanceFieldSet(HInstanceFieldSet* instruction) OVERRIDE {
@@ -387,6 +396,7 @@
void VisitStaticFieldGet(HStaticFieldGet* instruction) OVERRIDE {
VisitFieldAccess(instruction->InputAt(0), instruction->GetFieldInfo());
+ CreateReferenceInfoForReferenceType(instruction);
}
void VisitStaticFieldSet(HStaticFieldSet* instruction) OVERRIDE {
@@ -399,6 +409,7 @@
void VisitArrayGet(HArrayGet* instruction) OVERRIDE {
VisitArrayAccess(instruction->InputAt(0), instruction->InputAt(1));
+ CreateReferenceInfoForReferenceType(instruction);
}
void VisitArraySet(HArraySet* instruction) OVERRIDE {
@@ -408,7 +419,23 @@
void VisitNewInstance(HNewInstance* new_instance) OVERRIDE {
// Any references appearing in the ref_info_array_ so far cannot alias with new_instance.
- GetOrCreateReferenceInfo(new_instance);
+ CreateReferenceInfoForReferenceType(new_instance);
+ }
+
+ void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* instruction) OVERRIDE {
+ CreateReferenceInfoForReferenceType(instruction);
+ }
+
+ void VisitInvokeVirtual(HInvokeVirtual* instruction) OVERRIDE {
+ CreateReferenceInfoForReferenceType(instruction);
+ }
+
+ void VisitInvokeInterface(HInvokeInterface* instruction) OVERRIDE {
+ CreateReferenceInfoForReferenceType(instruction);
+ }
+
+ void VisitParameterValue(HParameterValue* instruction) OVERRIDE {
+ CreateReferenceInfoForReferenceType(instruction);
}
void VisitDeoptimize(HDeoptimize* instruction ATTRIBUTE_UNUSED) OVERRIDE {
@@ -725,7 +752,7 @@
// instruction is a store in the loop so the loop must does write.
DCHECK(side_effects_.GetLoopEffects(loop_info->GetHeader()).DoesAnyWrite());
- if (loop_info->IsLoopInvariant(original_ref, false)) {
+ if (loop_info->IsDefinedOutOfTheLoop(original_ref)) {
DCHECK(original_ref->GetBlock()->Dominates(loop_info->GetPreHeader()));
// Keep the store since its value may be needed at the loop header.
possibly_redundant = false;
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 9b26de4..926bc15 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -587,15 +587,8 @@
return other.blocks_.IsBitSet(header_->GetBlockId());
}
-bool HLoopInformation::IsLoopInvariant(HInstruction* instruction, bool must_dominate) const {
- HLoopInformation* other_loop = instruction->GetBlock()->GetLoopInformation();
- if (other_loop != this && (other_loop == nullptr || !other_loop->IsIn(*this))) {
- if (must_dominate) {
- return instruction->GetBlock()->Dominates(GetHeader());
- }
- return true;
- }
- return false;
+bool HLoopInformation::IsDefinedOutOfTheLoop(HInstruction* instruction) const {
+ return !blocks_.IsBitSet(instruction->GetBlock()->GetBlockId());
}
size_t HLoopInformation::GetLifetimeEnd() const {
@@ -1467,6 +1460,24 @@
}
}
+// Should be called on instructions in a dead block in post order. This method
+// assumes `insn` has been removed from all users with the exception of catch
+// phis because of missing exceptional edges in the graph. It removes the
+// instruction from catch phi uses, together with inputs of other catch phis in
+// the catch block at the same index, as these must be dead too.
+static void RemoveUsesOfDeadInstruction(HInstruction* insn) {
+ DCHECK(!insn->HasEnvironmentUses());
+ while (insn->HasNonEnvironmentUses()) {
+ HUseListNode<HInstruction*>* use = insn->GetUses().GetFirst();
+ size_t use_index = use->GetIndex();
+ HBasicBlock* user_block = use->GetUser()->GetBlock();
+ DCHECK(use->GetUser()->IsPhi() && user_block->IsCatchBlock());
+ for (HInstructionIterator phi_it(user_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
+ phi_it.Current()->AsPhi()->RemoveInputAt(use_index);
+ }
+ }
+}
+
void HBasicBlock::DisconnectAndDelete() {
// Dominators must be removed after all the blocks they dominate. This way
// a loop header is removed last, a requirement for correct loop information
@@ -1569,21 +1580,13 @@
// graph will always remain consistent.
for (HBackwardInstructionIterator it(GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* insn = it.Current();
- while (insn->HasUses()) {
- DCHECK(IsTryBlock());
- HUseListNode<HInstruction*>* use = insn->GetUses().GetFirst();
- size_t use_index = use->GetIndex();
- HBasicBlock* user_block = use->GetUser()->GetBlock();
- DCHECK(use->GetUser()->IsPhi() && user_block->IsCatchBlock());
- for (HInstructionIterator phi_it(user_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
- phi_it.Current()->AsPhi()->RemoveInputAt(use_index);
- }
- }
-
+ RemoveUsesOfDeadInstruction(insn);
RemoveInstruction(insn);
}
for (HInstructionIterator it(GetPhis()); !it.Done(); it.Advance()) {
- RemovePhi(it.Current()->AsPhi());
+ HPhi* insn = it.Current()->AsPhi();
+ RemoveUsesOfDeadInstruction(insn);
+ RemovePhi(insn);
}
// Disconnect from the dominator.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 9d3c88c..accd89e 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -564,11 +564,8 @@
// Note that `other` *must* be populated before entering this function.
bool IsIn(const HLoopInformation& other) const;
- // Returns true if instruction is not defined within this loop or any loop nested inside
- // this loop. If must_dominate is set, only definitions that actually dominate the loop
- // header can be invariant. Otherwise, any definition outside the loop, including
- // definitions that appear after the loop, is invariant.
- bool IsLoopInvariant(HInstruction* instruction, bool must_dominate) const;
+ // Returns true if instruction is not defined within this loop.
+ bool IsDefinedOutOfTheLoop(HInstruction* instruction) const;
const ArenaBitVector& GetBlocks() const { return blocks_; }
@@ -4928,9 +4925,13 @@
class HLoadString : public HExpression<1> {
public:
- HLoadString(HCurrentMethod* current_method, uint32_t string_index, uint32_t dex_pc)
+ HLoadString(HCurrentMethod* current_method,
+ uint32_t string_index,
+ uint32_t dex_pc,
+ bool is_in_dex_cache)
: HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
- string_index_(string_index) {
+ string_index_(string_index),
+ is_in_dex_cache_(is_in_dex_cache) {
SetRawInputAt(0, current_method);
}
@@ -4948,6 +4949,7 @@
bool NeedsEnvironment() const OVERRIDE { return false; }
bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return true; }
bool CanBeNull() const OVERRIDE { return false; }
+ bool IsInDexCache() const { return is_in_dex_cache_; }
static SideEffects SideEffectsForArchRuntimeCalls() {
return SideEffects::CanTriggerGC();
@@ -4957,6 +4959,7 @@
private:
const uint32_t string_index_;
+ const bool is_in_dex_cache_;
DISALLOW_COPY_AND_ASSIGN(HLoadString);
};
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 4643aed..831b626 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -614,7 +614,7 @@
codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(),
codegen->GetCoreSpillMask(),
codegen->GetFpuSpillMask(),
- ArrayRef<const SrcMapElem>(codegen->GetSrcMappingTable()),
+ ArrayRef<const SrcMapElem>(),
ArrayRef<const uint8_t>(), // mapping_table.
ArrayRef<const uint8_t>(stack_map),
ArrayRef<const uint8_t>(), // native_gc_map.
@@ -648,7 +648,7 @@
codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(),
codegen->GetCoreSpillMask(),
codegen->GetFpuSpillMask(),
- ArrayRef<const SrcMapElem>(codegen->GetSrcMappingTable()),
+ ArrayRef<const SrcMapElem>(),
AlignVectorSize(mapping_table),
AlignVectorSize(vmap_table),
AlignVectorSize(gc_map),
diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc
index 39e5259..48465e6 100644
--- a/compiler/trampolines/trampoline_compiler.cc
+++ b/compiler/trampolines/trampoline_compiler.cc
@@ -57,7 +57,7 @@
__ LoadFromOffset(kLoadWord, PC, R0, offset.Int32Value());
break;
case kJniAbi: // Load via Thread* held in JNIEnv* in first argument (R0).
- __ LoadFromOffset(kLoadWord, IP, R0, JNIEnvExt::SelfOffset().Int32Value());
+ __ LoadFromOffset(kLoadWord, IP, R0, JNIEnvExt::SelfOffset(4).Int32Value());
__ LoadFromOffset(kLoadWord, PC, IP, offset.Int32Value());
break;
case kQuickAbi: // R9 holds Thread*.
@@ -91,7 +91,7 @@
case kJniAbi: // Load via Thread* held in JNIEnv* in first argument (X0).
__ LoadRawPtr(Arm64ManagedRegister::FromXRegister(IP1),
Arm64ManagedRegister::FromXRegister(X0),
- Offset(JNIEnvExt::SelfOffset().Int32Value()));
+ Offset(JNIEnvExt::SelfOffset(8).Int32Value()));
__ JumpTo(Arm64ManagedRegister::FromXRegister(IP1), Offset(offset.Int32Value()),
Arm64ManagedRegister::FromXRegister(IP0));
@@ -126,7 +126,7 @@
__ LoadFromOffset(kLoadWord, T9, A0, offset.Int32Value());
break;
case kJniAbi: // Load via Thread* held in JNIEnv* in first argument (A0).
- __ LoadFromOffset(kLoadWord, T9, A0, JNIEnvExt::SelfOffset().Int32Value());
+ __ LoadFromOffset(kLoadWord, T9, A0, JNIEnvExt::SelfOffset(4).Int32Value());
__ LoadFromOffset(kLoadWord, T9, T9, offset.Int32Value());
break;
case kQuickAbi: // S1 holds Thread*.
@@ -158,7 +158,7 @@
__ LoadFromOffset(kLoadDoubleword, T9, A0, offset.Int32Value());
break;
case kJniAbi: // Load via Thread* held in JNIEnv* in first argument (A0).
- __ LoadFromOffset(kLoadDoubleword, T9, A0, JNIEnvExt::SelfOffset().Int32Value());
+ __ LoadFromOffset(kLoadDoubleword, T9, A0, JNIEnvExt::SelfOffset(8).Int32Value());
__ LoadFromOffset(kLoadDoubleword, T9, T9, offset.Int32Value());
break;
case kQuickAbi: // Fall-through.
diff --git a/compiler/trampolines/trampoline_compiler.h b/compiler/trampolines/trampoline_compiler.h
index 9fb2245..66d5ac3 100644
--- a/compiler/trampolines/trampoline_compiler.h
+++ b/compiler/trampolines/trampoline_compiler.h
@@ -25,12 +25,12 @@
namespace art {
// Create code that will invoke the function held in thread local storage.
-const std::vector<uint8_t>* CreateTrampoline32(InstructionSet isa, EntryPointCallingConvention abi,
- ThreadOffset<4> entry_point_offset)
- SHARED_REQUIRES(Locks::mutator_lock_);
-const std::vector<uint8_t>* CreateTrampoline64(InstructionSet isa, EntryPointCallingConvention abi,
- ThreadOffset<8> entry_point_offset)
- SHARED_REQUIRES(Locks::mutator_lock_);
+const std::vector<uint8_t>* CreateTrampoline32(InstructionSet isa,
+ EntryPointCallingConvention abi,
+ ThreadOffset<4> entry_point_offset);
+const std::vector<uint8_t>* CreateTrampoline64(InstructionSet isa,
+ EntryPointCallingConvention abi,
+ ThreadOffset<8> entry_point_offset);
} // namespace art
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 42f21e6..733ad2c 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -249,6 +249,11 @@
EmitR(0, rs, rt, rd, 2, 0x18);
}
+void MipsAssembler::MuhR6(Register rd, Register rs, Register rt) {
+ CHECK(IsR6());
+ EmitR(0, rs, rt, rd, 3, 0x18);
+}
+
void MipsAssembler::MuhuR6(Register rd, Register rs, Register rt) {
CHECK(IsR6());
EmitR(0, rs, rt, rd, 3, 0x19);
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index d50b4f6..62366f6 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -119,6 +119,7 @@
void DivuR2(Register rd, Register rs, Register rt); // R2
void ModuR2(Register rd, Register rs, Register rt); // R2
void MulR6(Register rd, Register rs, Register rt); // R6
+ void MuhR6(Register rd, Register rs, Register rt); // R6
void MuhuR6(Register rd, Register rs, Register rt); // R6
void DivR6(Register rd, Register rs, Register rt); // R6
void ModR6(Register rd, Register rs, Register rt); // R6
diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk
index e252765..77f8d6c 100644
--- a/dex2oat/Android.mk
+++ b/dex2oat/Android.mk
@@ -38,9 +38,10 @@
dex2oat_target_arch := 32
endif
-# We need to explcitly give the arch, as giving 'both' will make the
-# build-art-executable rule compile dex2oat for 64bits.
ifeq ($(HOST_PREFER_32_BIT),true)
+ # We need to explicitly restrict the host arch to 32-bit only, as
+ # giving 'both' would make build-art-executable generate a build
+ # rule for a 64-bit dex2oat executable too.
dex2oat_host_arch := 32
else
dex2oat_host_arch := both
@@ -56,19 +57,23 @@
# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
ifeq ($(ART_BUILD_HOST_NDEBUG),true)
- $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler libsigchain libziparchive-host,art/compiler,host,ndebug,$(dex2oat_host_arch)))
+ $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler libsigchain libziparchive-host liblz4,art/compiler,host,ndebug,$(dex2oat_host_arch)))
ifeq ($(ART_BUILD_HOST_STATIC),true)
$(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart libart-compiler libart libziparchive-host libnativehelper libnativebridge libsigchain_dummy libvixl liblog libz \
- libbacktrace libLLVMObject libLLVMBitReader libLLVMMC libLLVMMCParser libLLVMCore libLLVMSupport libcutils libunwindbacktrace libutils libbase,art/compiler,host,ndebug,$(dex2oat_host_arch),static))
+ libbacktrace libLLVMObject libLLVMBitReader libLLVMMC libLLVMMCParser libLLVMCore libLLVMSupport libcutils libunwindbacktrace libutils libbase liblz4,art/compiler,host,ndebug,$(dex2oat_host_arch),static))
endif
endif
ifeq ($(ART_BUILD_HOST_DEBUG),true)
- $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libsigchain libziparchive-host,art/compiler,host,debug,$(dex2oat_host_arch)))
+ $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libsigchain libziparchive-host liblz4,art/compiler,host,debug,$(dex2oat_host_arch)))
ifeq ($(ART_BUILD_HOST_STATIC),true)
$(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd libartd-compiler libartd libziparchive-host libnativehelper libnativebridge libsigchain_dummy libvixld liblog libz \
- libbacktrace libLLVMObject libLLVMBitReader libLLVMMC libLLVMMCParser libLLVMCore libLLVMSupport libcutils libunwindbacktrace libutils libbase,art/compiler,host,debug,$(dex2oat_host_arch),static))
+ libbacktrace libLLVMObject libLLVMBitReader libLLVMMC libLLVMMCParser libLLVMCore libLLVMSupport libcutils libunwindbacktrace libutils libbase liblz4,art/compiler,host,debug,$(dex2oat_host_arch),static))
endif
endif
+# Clear locals now they've served their purpose.
+dex2oat_target_arch :=
+dex2oat_host_arch :=
+
endif
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 2aa4085..6fae8e4 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -71,6 +71,7 @@
#include "oat_writer.h"
#include "os.h"
#include "runtime.h"
+#include "runtime_options.h"
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change.h"
#include "utils.h"
@@ -208,6 +209,11 @@
UsageError(" --image=<file.art>: specifies the output image filename.");
UsageError(" Example: --image=/system/framework/boot.art");
UsageError("");
+ UsageError(" --image-format=(uncompressed|lz4):");
+ UsageError(" Which format to store the image.");
+ UsageError(" Example: --image-format=lz4");
+ UsageError(" Default: uncompressed");
+ UsageError("");
UsageError(" --image-classes=<classname-file>: specifies classes to include in an image.");
UsageError(" Example: --image=frameworks/base/preloaded-classes");
UsageError("");
@@ -490,6 +496,7 @@
image_base_(0U),
image_classes_zip_filename_(nullptr),
image_classes_filename_(nullptr),
+ image_storage_mode_(ImageHeader::kStorageModeUncompressed),
compiled_classes_zip_filename_(nullptr),
compiled_classes_filename_(nullptr),
compiled_methods_zip_filename_(nullptr),
@@ -621,6 +628,19 @@
}
}
+ void ParseImageFormat(const StringPiece& option) {
+ const StringPiece substr("--image-format=");
+ DCHECK(option.starts_with(substr));
+ const StringPiece format_str = option.substr(substr.length());
+ if (format_str == "lz4") {
+ image_storage_mode_ = ImageHeader::kStorageModeLZ4;
+ } else if (format_str == "uncompressed") {
+ image_storage_mode_ = ImageHeader::kStorageModeUncompressed;
+ } else {
+ Usage("Unknown image format: %s", format_str.data());
+ }
+ }
+
void ProcessOptions(ParserOptions* parser_options) {
boot_image_ = !image_filename_.empty();
app_image_ = app_image_fd_ != -1 || !app_image_file_name_.empty();
@@ -667,15 +687,14 @@
parser_options->boot_image_filename += "/framework/boot.art";
}
if (!parser_options->boot_image_filename.empty()) {
- boot_image_option_ += "-Ximage:";
- boot_image_option_ += parser_options->boot_image_filename;
+ boot_image_filename_ = parser_options->boot_image_filename;
}
if (image_classes_filename_ != nullptr && !IsBootImage()) {
Usage("--image-classes should only be used with --image");
}
- if (image_classes_filename_ != nullptr && !boot_image_option_.empty()) {
+ if (image_classes_filename_ != nullptr && !boot_image_filename_.empty()) {
Usage("--image-classes should not be used with --boot-image");
}
@@ -687,7 +706,7 @@
Usage("--compiled-classes should only be used with --image");
}
- if (compiled_classes_filename_ != nullptr && !boot_image_option_.empty()) {
+ if (compiled_classes_filename_ != nullptr && !boot_image_filename_.empty()) {
Usage("--compiled-classes should not be used with --boot-image");
}
@@ -719,7 +738,7 @@
Usage("--zip-location should be supplied with --zip-fd");
}
- if (boot_image_option_.empty()) {
+ if (boot_image_filename_.empty()) {
if (image_base_ == 0) {
Usage("Non-zero --base not specified");
}
@@ -877,6 +896,8 @@
image_classes_filename_ = option.substr(strlen("--image-classes=")).data();
} else if (option.starts_with("--image-classes-zip=")) {
image_classes_zip_filename_ = option.substr(strlen("--image-classes-zip=")).data();
+ } else if (option.starts_with("--image-format=")) {
+ ParseImageFormat(option);
} else if (option.starts_with("--compiled-classes=")) {
compiled_classes_filename_ = option.substr(strlen("--compiled-classes=")).data();
} else if (option.starts_with("--compiled-classes-zip=")) {
@@ -1014,20 +1035,10 @@
// boot class path.
bool Setup() {
TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
- RuntimeOptions runtime_options;
art::MemMap::Init(); // For ZipEntry::ExtractToMemMap.
- if (boot_image_option_.empty()) {
- std::string boot_class_path = "-Xbootclasspath:";
- boot_class_path += Join(dex_filenames_, ':');
- runtime_options.push_back(std::make_pair(boot_class_path, nullptr));
- std::string boot_class_path_locations = "-Xbootclasspath-locations:";
- boot_class_path_locations += Join(dex_locations_, ':');
- runtime_options.push_back(std::make_pair(boot_class_path_locations, nullptr));
- } else {
- runtime_options.push_back(std::make_pair(boot_image_option_, nullptr));
- }
- for (size_t i = 0; i < runtime_args_.size(); i++) {
- runtime_options.push_back(std::make_pair(runtime_args_[i], nullptr));
+
+ if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) {
+ return false;
}
verification_results_.reset(new VerificationResults(compiler_options_.get()));
@@ -1037,23 +1048,15 @@
IsBootImage() ?
CompilerCallbacks::CallbackMode::kCompileBootImage :
CompilerCallbacks::CallbackMode::kCompileApp));
- runtime_options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
- runtime_options.push_back(
- std::make_pair("imageinstructionset", GetInstructionSetString(instruction_set_)));
- // 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 (!IsBootImage()) {
- runtime_options.push_back(std::make_pair("-Xno-dex-file-fallback", nullptr));
+ RuntimeArgumentMap runtime_options;
+ if (!PrepareRuntimeOptions(&runtime_options)) {
+ return false;
}
- // Disable libsigchain. We don't don't need it during compilation and it prevents us
- // from getting a statically linked version of dex2oat (because of dlsym and RTLD_NEXT).
- runtime_options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
{
TimingLogger::ScopedTiming t_runtime("Create runtime", timings_);
- if (!CreateRuntime(runtime_options)) {
+ if (!CreateRuntime(std::move(runtime_options))) {
return false;
}
}
@@ -1068,66 +1071,8 @@
// Whilst we're in native take the opportunity to initialize well known classes.
WellKnownClasses::Init(self->GetJniEnv());
- // If --image-classes was specified, calculate the full list of classes to include in the image
- if (image_classes_filename_ != nullptr) {
- std::string error_msg;
- if (image_classes_zip_filename_ != nullptr) {
- image_classes_.reset(ReadImageClassesFromZip(image_classes_zip_filename_,
- image_classes_filename_,
- &error_msg));
- } else {
- image_classes_.reset(ReadImageClassesFromFile(image_classes_filename_));
- }
- if (image_classes_.get() == nullptr) {
- LOG(ERROR) << "Failed to create list of image classes from '" << image_classes_filename_ <<
- "': " << error_msg;
- return false;
- }
- } 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
- // image.
- if (compiled_classes_filename_ != nullptr) {
- std::string error_msg;
- if (compiled_classes_zip_filename_ != nullptr) {
- compiled_classes_.reset(ReadImageClassesFromZip(compiled_classes_zip_filename_,
- compiled_classes_filename_,
- &error_msg));
- } else {
- compiled_classes_.reset(ReadImageClassesFromFile(compiled_classes_filename_));
- }
- if (compiled_classes_.get() == nullptr) {
- LOG(ERROR) << "Failed to create list of compiled classes from '"
- << compiled_classes_filename_ << "': " << error_msg;
- return false;
- }
- } else {
- compiled_classes_.reset(nullptr); // By default compile everything.
- }
- // If --compiled-methods was specified, read the methods to compile from the given file(s).
- if (compiled_methods_filename_ != nullptr) {
- std::string error_msg;
- if (compiled_methods_zip_filename_ != nullptr) {
- compiled_methods_.reset(ReadCommentedInputFromZip(compiled_methods_zip_filename_,
- compiled_methods_filename_,
- nullptr, // No post-processing.
- &error_msg));
- } else {
- compiled_methods_.reset(ReadCommentedInputFromFile(compiled_methods_filename_,
- nullptr)); // No post-processing.
- }
- if (compiled_methods_.get() == nullptr) {
- LOG(ERROR) << "Failed to create list of compiled methods from '"
- << compiled_methods_filename_ << "': " << error_msg;
- return false;
- }
- } else {
- compiled_methods_.reset(nullptr); // By default compile everything.
- }
-
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- if (boot_image_option_.empty()) {
+ if (boot_image_filename_.empty()) {
dex_files_ = class_linker->GetBootClassPath();
} else {
TimingLogger::ScopedTiming t_dex("Opening dex files", timings_);
@@ -1164,22 +1109,7 @@
constexpr bool kSaveDexInput = false;
if (kSaveDexInput) {
- for (size_t i = 0; i < dex_files_.size(); ++i) {
- const DexFile* dex_file = dex_files_[i];
- std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex",
- getpid(), i));
- std::unique_ptr<File> tmp_file(OS::CreateEmptyFile(tmp_file_name.c_str()));
- if (tmp_file.get() == nullptr) {
- PLOG(ERROR) << "Failed to open file " << tmp_file_name
- << ". Try: adb shell chmod 777 /data/local/tmp";
- continue;
- }
- // This is just dumping files for debugging. Ignore errors, and leave remnants.
- UNUSED(tmp_file->WriteFully(dex_file->Begin(), dex_file->Size()));
- UNUSED(tmp_file->Flush());
- UNUSED(tmp_file->Close());
- LOG(INFO) << "Wrote input to " << tmp_file_name;
- }
+ SaveDexInput();
}
}
// Ensure opened dex files are writable for dex-to-dex transformations. Also ensure that
@@ -1238,16 +1168,13 @@
jobject class_path_class_loader = nullptr;
Thread* self = Thread::Current();
- if (!boot_image_option_.empty()) {
+ if (!boot_image_filename_.empty()) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
OpenClassPathFiles(runtime_->GetClassPathString(), dex_files_, &class_path_files_);
ScopedObjectAccess soa(self);
// Classpath: first the class-path given.
- std::vector<const DexFile*> class_path_files;
- for (auto& class_path_file : class_path_files_) {
- class_path_files.push_back(class_path_file.get());
- }
+ std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);
// Store the classpath we have right now.
key_value_store_->Put(OatHeader::kClassPathKey,
@@ -1432,14 +1359,9 @@
elf_writer->EndText(text);
elf_writer->SetBssSize(oat_writer->GetBssSize());
-
elf_writer->WriteDynamicSection();
-
- ArrayRef<const dwarf::MethodDebugInfo> method_infos(oat_writer->GetMethodDebugInfo());
- elf_writer->WriteDebugInfo(method_infos);
-
- ArrayRef<const uintptr_t> patch_locations(oat_writer->GetAbsolutePatchLocations());
- elf_writer->WritePatchLocations(patch_locations);
+ elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo());
+ elf_writer->WritePatchLocations(oat_writer->GetAbsolutePatchLocations());
if (!elf_writer->End()) {
LOG(ERROR) << "Failed to write ELF file " << oat_file_->GetPath();
@@ -1552,6 +1474,16 @@
}
private:
+ template <typename T>
+ static std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) {
+ std::vector<T*> result;
+ result.reserve(src.size());
+ for (const std::unique_ptr<T>& t : src) {
+ result.push_back(t.get());
+ }
+ return result;
+ }
+
static size_t OpenDexFiles(const std::vector<const char*>& dex_filenames,
const std::vector<const char*>& dex_locations,
std::vector<std::unique_ptr<const DexFile>>* dex_files) {
@@ -1612,10 +1544,138 @@
}
}
+ bool PrepareImageClasses() {
+ // If --image-classes was specified, calculate the full list of classes to include in the image.
+ if (image_classes_filename_ != nullptr) {
+ image_classes_ =
+ ReadClasses(image_classes_zip_filename_, image_classes_filename_, "image");
+ if (image_classes_ == nullptr) {
+ return false;
+ }
+ } else if (IsBootImage()) {
+ image_classes_.reset(new std::unordered_set<std::string>);
+ }
+ return true;
+ }
+
+ bool PrepareCompiledClasses() {
+ // If --compiled-classes was specified, calculate the full list of classes to compile in the
+ // image.
+ if (compiled_classes_filename_ != nullptr) {
+ compiled_classes_ =
+ ReadClasses(compiled_classes_zip_filename_, compiled_classes_filename_, "compiled");
+ if (compiled_classes_ == nullptr) {
+ return false;
+ }
+ } else {
+ compiled_classes_.reset(nullptr); // By default compile everything.
+ }
+ return true;
+ }
+
+ static std::unique_ptr<std::unordered_set<std::string>> ReadClasses(const char* zip_filename,
+ const char* classes_filename,
+ const char* tag) {
+ std::unique_ptr<std::unordered_set<std::string>> classes;
+ std::string error_msg;
+ if (zip_filename != nullptr) {
+ classes.reset(ReadImageClassesFromZip(zip_filename, classes_filename, &error_msg));
+ } else {
+ classes.reset(ReadImageClassesFromFile(classes_filename));
+ }
+ if (classes == nullptr) {
+ LOG(ERROR) << "Failed to create list of " << tag << " classes from '"
+ << classes_filename << "': " << error_msg;
+ }
+ return classes;
+ }
+
+ bool PrepareCompiledMethods() {
+ // If --compiled-methods was specified, read the methods to compile from the given file(s).
+ if (compiled_methods_filename_ != nullptr) {
+ std::string error_msg;
+ if (compiled_methods_zip_filename_ != nullptr) {
+ compiled_methods_.reset(ReadCommentedInputFromZip(compiled_methods_zip_filename_,
+ compiled_methods_filename_,
+ nullptr, // No post-processing.
+ &error_msg));
+ } else {
+ compiled_methods_.reset(ReadCommentedInputFromFile(compiled_methods_filename_,
+ nullptr)); // No post-processing.
+ }
+ if (compiled_methods_.get() == nullptr) {
+ LOG(ERROR) << "Failed to create list of compiled methods from '"
+ << compiled_methods_filename_ << "': " << error_msg;
+ return false;
+ }
+ } else {
+ compiled_methods_.reset(nullptr); // By default compile everything.
+ }
+ return true;
+ }
+
+ void SaveDexInput() {
+ for (size_t i = 0; i < dex_files_.size(); ++i) {
+ const DexFile* dex_file = dex_files_[i];
+ std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%zd.dex",
+ getpid(), i));
+ std::unique_ptr<File> tmp_file(OS::CreateEmptyFile(tmp_file_name.c_str()));
+ if (tmp_file.get() == nullptr) {
+ PLOG(ERROR) << "Failed to open file " << tmp_file_name
+ << ". Try: adb shell chmod 777 /data/local/tmp";
+ continue;
+ }
+ // This is just dumping files for debugging. Ignore errors, and leave remnants.
+ UNUSED(tmp_file->WriteFully(dex_file->Begin(), dex_file->Size()));
+ UNUSED(tmp_file->Flush());
+ UNUSED(tmp_file->Close());
+ LOG(INFO) << "Wrote input to " << tmp_file_name;
+ }
+ }
+
+ bool PrepareRuntimeOptions(RuntimeArgumentMap* runtime_options) {
+ RuntimeOptions raw_options;
+ if (boot_image_filename_.empty()) {
+ std::string boot_class_path = "-Xbootclasspath:";
+ boot_class_path += Join(dex_filenames_, ':');
+ raw_options.push_back(std::make_pair(boot_class_path, nullptr));
+ std::string boot_class_path_locations = "-Xbootclasspath-locations:";
+ boot_class_path_locations += Join(dex_locations_, ':');
+ raw_options.push_back(std::make_pair(boot_class_path_locations, nullptr));
+ } else {
+ std::string boot_image_option = "-Ximage:";
+ boot_image_option += boot_image_filename_;
+ raw_options.push_back(std::make_pair(boot_image_option, nullptr));
+ }
+ for (size_t i = 0; i < runtime_args_.size(); i++) {
+ raw_options.push_back(std::make_pair(runtime_args_[i], nullptr));
+ }
+
+ raw_options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
+ raw_options.push_back(
+ std::make_pair("imageinstructionset", GetInstructionSetString(instruction_set_)));
+
+ // 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 (!IsBootImage()) {
+ raw_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
+ // from getting a statically linked version of dex2oat (because of dlsym and RTLD_NEXT).
+ raw_options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
+
+ if (!Runtime::ParseOptions(raw_options, false, runtime_options)) {
+ LOG(ERROR) << "Failed to parse runtime options";
+ return false;
+ }
+ return true;
+ }
+
// Create a runtime necessary for compilation.
- bool CreateRuntime(const RuntimeOptions& runtime_options)
+ bool CreateRuntime(RuntimeArgumentMap&& runtime_options)
SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
- if (!Runtime::Create(runtime_options, false)) {
+ if (!Runtime::Create(std::move(runtime_options))) {
LOG(ERROR) << "Failed to create runtime";
return false;
}
@@ -1643,7 +1703,8 @@
image_writer_.reset(new ImageWriter(*driver_,
image_base,
compiler_options_->GetCompilePic(),
- IsAppImage()));
+ IsAppImage(),
+ image_storage_mode_));
}
// Let the ImageWriter write the image file. If we do not compile PIC, also fix up the oat file.
@@ -1812,12 +1873,13 @@
std::vector<const char*> dex_locations_;
int zip_fd_;
std::string zip_location_;
- std::string boot_image_option_;
+ std::string boot_image_filename_;
std::vector<const char*> runtime_args_;
std::string image_filename_;
uintptr_t image_base_;
const char* image_classes_zip_filename_;
const char* image_classes_filename_;
+ ImageHeader::StorageMode image_storage_mode_;
const char* compiled_classes_zip_filename_;
const char* compiled_classes_filename_;
const char* compiled_methods_zip_filename_;
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 52e6c02..d387726 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -27,7 +27,6 @@
* Differences between XML output and the "current.xml" file:
* - classes in same package are not all grouped together; nothing is sorted
* - no "deprecated" on fields and methods
- * - no "value" on fields
* - no parameter names
* - no generic signatures on parameters, e.g. type="java.lang.Class<?>"
* - class shows declared fields and methods; does not show inherited fields
@@ -1019,9 +1018,126 @@
}
/*
+ * Dumps a string value with some escape characters.
+ */
+static void dumpEscapedString(const char* p) {
+ for (; *p; p++) {
+ switch (*p) {
+ case '\\':
+ fputs("\\\\", gOutFile);
+ break;
+ case '\"':
+ fputs("\\\"", gOutFile);
+ break;
+ case '\t':
+ fputs("\\t", gOutFile);
+ break;
+ case '\n':
+ fputs("\\n", gOutFile);
+ break;
+ case '\r':
+ fputs("\\r", gOutFile);
+ break;
+ default:
+ putc(*p, gOutFile);
+ }
+ }
+}
+
+/*
+ * Dumps an XML attribute value between double-quotes.
+ */
+static void dumpXmlAttribute(const char* p) {
+ for (; *p; p++) {
+ switch (*p) {
+ case '&':
+ fputs("&", gOutFile);
+ break;
+ case '<':
+ fputs("<", gOutFile);
+ break;
+ case '"':
+ fputs(""", gOutFile);
+ break;
+ case '\t':
+ fputs("	", gOutFile);
+ break;
+ case '\n':
+ fputs("
", gOutFile);
+ break;
+ case '\r':
+ fputs("
", gOutFile);
+ break;
+ default:
+ putc(*p, gOutFile);
+ }
+ }
+}
+
+/*
+ * Dumps a value of static (class) field.
+ */
+static void dumpSFieldValue(const DexFile* pDexFile,
+ EncodedStaticFieldValueIterator::ValueType valueType,
+ const jvalue* pValue) {
+ switch (valueType) {
+ case EncodedStaticFieldValueIterator::kByte:
+ fprintf(gOutFile, "%" PRIu8, pValue->b);
+ break;
+ case EncodedStaticFieldValueIterator::kShort:
+ fprintf(gOutFile, "%" PRId16, pValue->s);
+ break;
+ case EncodedStaticFieldValueIterator::kChar:
+ fprintf(gOutFile, "%" PRIu16, pValue->c);
+ break;
+ case EncodedStaticFieldValueIterator::kInt:
+ fprintf(gOutFile, "%" PRId32, pValue->i);
+ break;
+ case EncodedStaticFieldValueIterator::kLong:
+ fprintf(gOutFile, "%" PRId64, pValue->j);
+ break;
+ case EncodedStaticFieldValueIterator::kFloat:
+ fprintf(gOutFile, "%f", pValue->f);
+ break;
+ case EncodedStaticFieldValueIterator::kDouble:
+ fprintf(gOutFile, "%f", pValue->d);
+ break;
+ case EncodedStaticFieldValueIterator::kString: {
+ const char* str =
+ pDexFile->GetStringData(pDexFile->GetStringId(pValue->i));
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ fputs("\"", gOutFile);
+ dumpEscapedString(str);
+ fputs("\"", gOutFile);
+ } else {
+ dumpXmlAttribute(str);
+ }
+ break;
+ }
+ case EncodedStaticFieldValueIterator::kNull:
+ fputs("null", gOutFile);
+ break;
+ case EncodedStaticFieldValueIterator::kBoolean:
+ fputs(pValue->z ? "true" : "false", gOutFile);
+ break;
+
+ case EncodedStaticFieldValueIterator::kAnnotation:
+ case EncodedStaticFieldValueIterator::kArray:
+ case EncodedStaticFieldValueIterator::kEnum:
+ case EncodedStaticFieldValueIterator::kField:
+ case EncodedStaticFieldValueIterator::kMethod:
+ case EncodedStaticFieldValueIterator::kType:
+ default:
+ fprintf(gOutFile, "Unexpected static field type: %d", valueType);
+ }
+}
+
+/*
* Dumps a static (class) field.
*/
-static void dumpSField(const DexFile* pDexFile, u4 idx, u4 flags, int i) {
+static void dumpSField(const DexFile* pDexFile, u4 idx, u4 flags, int i,
+ EncodedStaticFieldValueIterator::ValueType valueType,
+ const jvalue* pValue) {
// Bail for anything private if export only requested.
if (gOptions.exportsOnly && (flags & (kAccPublic | kAccProtected)) == 0) {
return;
@@ -1038,6 +1154,11 @@
fprintf(gOutFile, " name : '%s'\n", name);
fprintf(gOutFile, " type : '%s'\n", typeDescriptor);
fprintf(gOutFile, " access : 0x%04x (%s)\n", flags, accessStr);
+ if (pValue != nullptr) {
+ fputs(" value : ", gOutFile);
+ dumpSFieldValue(pDexFile, valueType, pValue);
+ fputs("\n", gOutFile);
+ }
} else if (gOptions.outputFormat == OUTPUT_XML) {
fprintf(gOutFile, "<field name=\"%s\"\n", name);
char *tmp = descriptorToDot(typeDescriptor);
@@ -1050,7 +1171,12 @@
fprintf(gOutFile, " final=%s\n", quotedBool((flags & kAccFinal) != 0));
// The "deprecated=" is not knowable w/o parsing annotations.
fprintf(gOutFile, " visibility=%s\n", quotedVisibility(flags));
- fprintf(gOutFile, ">\n</field>\n");
+ if (pValue != nullptr) {
+ fputs(" value=\"", gOutFile);
+ dumpSFieldValue(pDexFile, valueType, pValue);
+ fputs("\"\n", gOutFile);
+ }
+ fputs(">\n</field>\n", gOutFile);
}
free(accessStr);
@@ -1060,7 +1186,8 @@
* Dumps an instance field.
*/
static void dumpIField(const DexFile* pDexFile, u4 idx, u4 flags, int i) {
- dumpSField(pDexFile, idx, flags, i);
+ dumpSField(pDexFile, idx, flags, i,
+ EncodedStaticFieldValueIterator::kByte, nullptr);
}
/*
@@ -1222,10 +1349,23 @@
if (gOptions.outputFormat == OUTPUT_PLAIN) {
fprintf(gOutFile, " Static fields -\n");
}
+ EncodedStaticFieldValueIterator staticFieldValues(*pDexFile, pClassDef);
for (int i = 0; pClassData.HasNextStaticField(); i++, pClassData.Next()) {
+ EncodedStaticFieldValueIterator::ValueType valueType =
+ EncodedStaticFieldValueIterator::kByte;
+ const jvalue* pValue = nullptr;
+ if (staticFieldValues.HasNext()) {
+ valueType = staticFieldValues.GetValueType();
+ pValue = &staticFieldValues.GetJavaValue();
+ }
dumpSField(pDexFile, pClassData.GetMemberIndex(),
- pClassData.GetRawMemberAccessFlags(), i);
+ pClassData.GetRawMemberAccessFlags(), i,
+ valueType, pValue);
+ if (staticFieldValues.HasNext()) {
+ staticFieldValues.Next();
+ }
} // for
+ DCHECK(!staticFieldValues.HasNext());
if (gOptions.outputFormat == OUTPUT_PLAIN) {
fprintf(gOutFile, " Instance fields -\n");
}
diff --git a/imgdiag/Android.mk b/imgdiag/Android.mk
index d5d7c22..83315be 100644
--- a/imgdiag/Android.mk
+++ b/imgdiag/Android.mk
@@ -25,4 +25,8 @@
# that the image it's analyzing be the same ISA as the runtime ISA.
# Build variants {target,host} x {debug,ndebug} x {32,64}
-$(eval $(call build-art-multi-executable,imgdiag,$(IMGDIAG_SRC_FILES),libart-compiler libbacktrace,libcutils,libziparchive-host,art/compiler,both))
+#
+# Honor HOST_PREFER_32_BIT, as building a 64-bit imgdiag executable
+# when HOST_PREFER_32_BIT is true would require an unmet dependency on
+# 64-bit libbacktrace.
+$(eval $(call build-art-multi-executable,imgdiag,$(IMGDIAG_SRC_FILES),libart-compiler libbacktrace,libcutils,libziparchive-host,art/compiler,both,$(HOST_PREFER_32_BIT)))
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 723bb17..46ab34b 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -153,6 +153,12 @@
return false;
}
+ if (image_header.GetStorageMode() != ImageHeader::kStorageModeUncompressed) {
+ LOG(ERROR) << "Patchoat is not supported with compressed image files "
+ << input_image->GetPath();
+ return false;
+ }
+
/*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath());
// Nothing special to do right now since the image always needs to get patched.
// Perhaps in some far-off future we may have images with relative addresses that are true-PIC.
diff --git a/runtime/Android.mk b/runtime/Android.mk
index f0bb10d..f2d7f17 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -501,9 +501,9 @@
LOCAL_C_INCLUDES += art
ifeq ($$(art_static_or_shared),static)
- LOCAL_STATIC_LIBRARIES := libnativehelper libnativebridge libsigchain_dummy libbacktrace
+ LOCAL_STATIC_LIBRARIES := libnativehelper libnativebridge libsigchain_dummy libbacktrace liblz4
else
- LOCAL_SHARED_LIBRARIES := libnativehelper libnativebridge libsigchain libbacktrace
+ LOCAL_SHARED_LIBRARIES := libnativehelper libnativebridge libsigchain libbacktrace liblz4
endif
ifeq ($$(art_target_or_host),target)
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index e200018..aca49cc 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -93,6 +93,25 @@
qpoints->pLockObject = art_quick_lock_object;
qpoints->pUnlockObject = art_quick_unlock_object;
+ // More math.
+ qpoints->pCos = cos;
+ qpoints->pSin = sin;
+ qpoints->pAcos = acos;
+ qpoints->pAsin = asin;
+ qpoints->pAtan = atan;
+ qpoints->pAtan2 = atan2;
+ qpoints->pCbrt = cbrt;
+ qpoints->pCosh = cosh;
+ qpoints->pExp = exp;
+ qpoints->pExpm1 = expm1;
+ qpoints->pHypot = hypot;
+ qpoints->pLog = log;
+ qpoints->pLog10 = log10;
+ qpoints->pNextAfter = nextafter;
+ qpoints->pSinh = sinh;
+ qpoints->pTan = tan;
+ qpoints->pTanh = tanh;
+
// Math
qpoints->pD2l = art_quick_d2l;
qpoints->pF2l = art_quick_f2l;
diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
index 2b38c9d..ebe6d40 100644
--- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc
+++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
@@ -98,6 +98,25 @@
qpoints->pLockObject = art_quick_lock_object;
qpoints->pUnlockObject = art_quick_unlock_object;
+ // More math.
+ qpoints->pCos = cos;
+ qpoints->pSin = sin;
+ qpoints->pAcos = acos;
+ qpoints->pAsin = asin;
+ qpoints->pAtan = atan;
+ qpoints->pAtan2 = atan2;
+ qpoints->pCbrt = cbrt;
+ qpoints->pCosh = cosh;
+ qpoints->pExp = exp;
+ qpoints->pExpm1 = expm1;
+ qpoints->pHypot = hypot;
+ qpoints->pLog = log;
+ qpoints->pLog10 = log10;
+ qpoints->pNextAfter = nextafter;
+ qpoints->pSinh = sinh;
+ qpoints->pTan = tan;
+ qpoints->pTanh = tanh;
+
// Math
qpoints->pD2l = art_d2l;
qpoints->pF2l = art_f2l;
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index b548dfb..29c8232 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -122,7 +122,7 @@
art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.thread_local_pos.
-#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 151 * __SIZEOF_POINTER__)
+#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 168 * __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET,
art::Thread::ThreadLocalPosOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.thread_local_end.
diff --git a/runtime/base/dchecked_vector.h b/runtime/base/dchecked_vector.h
index 2bd12df..51dfba8 100644
--- a/runtime/base/dchecked_vector.h
+++ b/runtime/base/dchecked_vector.h
@@ -33,7 +33,7 @@
// but we do not use exceptions, so this accessor is deliberately hidden.
// Note: The common pattern &v[0] used to retrieve pointer to the data is not
// valid for an empty dchecked_vector<>. Use data() to avoid checking empty().
-template <typename T, typename Alloc>
+template <typename T, typename Alloc = std::allocator<T>>
class dchecked_vector : private std::vector<T, Alloc> {
private:
// std::vector<> has a slightly different specialization for bool. We don't provide that.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0a37f26..f5085ed 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3056,10 +3056,12 @@
verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure;
std::string error_msg;
if (!preverified) {
+ Runtime* runtime = Runtime::Current();
verifier_failure = verifier::MethodVerifier::VerifyClass(self,
klass.Get(),
- Runtime::Current()->IsAotCompiler(),
- Runtime::Current()->IsAotCompiler(),
+ runtime->GetCompilerCallbacks(),
+ runtime->IsAotCompiler(),
+ runtime->IsAotCompiler(),
&error_msg);
}
if (preverified || verifier_failure != verifier::MethodVerifier::kHardFailure) {
diff --git a/runtime/compiler_callbacks.h b/runtime/compiler_callbacks.h
index af7b04f..a39d682 100644
--- a/runtime/compiler_callbacks.h
+++ b/runtime/compiler_callbacks.h
@@ -37,8 +37,8 @@
virtual ~CompilerCallbacks() { }
- virtual bool MethodVerified(verifier::MethodVerifier* verifier)
- SHARED_REQUIRES(Locks::mutator_lock_) = 0;
+ virtual void MethodVerified(verifier::MethodVerifier* verifier)
+ SHARED_REQUIRES(Locks::mutator_lock_) = 0;
virtual void ClassRejected(ClassReference ref) = 0;
// Return true if we should attempt to relocate to a random base address if we have not already
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 30d921a..4e15e80 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -2208,14 +2208,24 @@
}
EncodedStaticFieldValueIterator::EncodedStaticFieldValueIterator(
+ const DexFile& dex_file,
+ const DexFile::ClassDef& class_def)
+ : EncodedStaticFieldValueIterator(dex_file, nullptr, nullptr,
+ nullptr, class_def) {
+}
+
+EncodedStaticFieldValueIterator::EncodedStaticFieldValueIterator(
const DexFile& dex_file, Handle<mirror::DexCache>* dex_cache,
Handle<mirror::ClassLoader>* class_loader, ClassLinker* linker,
const DexFile::ClassDef& class_def)
- : dex_file_(dex_file), dex_cache_(dex_cache), class_loader_(class_loader), linker_(linker),
- array_size_(), pos_(-1), type_(kByte) {
- DCHECK(dex_cache != nullptr);
- DCHECK(class_loader != nullptr);
- ptr_ = dex_file.GetEncodedStaticFieldValuesArray(class_def);
+ : dex_file_(dex_file),
+ dex_cache_(dex_cache),
+ class_loader_(class_loader),
+ linker_(linker),
+ array_size_(),
+ pos_(-1),
+ type_(kByte) {
+ ptr_ = dex_file_.GetEncodedStaticFieldValuesArray(class_def);
if (ptr_ == nullptr) {
array_size_ = 0;
} else {
@@ -2288,6 +2298,8 @@
template<bool kTransactionActive>
void EncodedStaticFieldValueIterator::ReadValueToField(ArtField* field) const {
+ DCHECK(dex_cache_ != nullptr);
+ DCHECK(class_loader_ != nullptr);
switch (type_) {
case kBoolean: field->SetBoolean<kTransactionActive>(field->GetDeclaringClass(), jval_.z);
break;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 1e44f50..6b019f1 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -1510,6 +1510,12 @@
class EncodedStaticFieldValueIterator {
public:
+ // A constructor for static tools. You cannot call
+ // ReadValueToField() for an object created by this.
+ EncodedStaticFieldValueIterator(const DexFile& dex_file,
+ const DexFile::ClassDef& class_def);
+
+ // A constructor meant to be called from runtime code.
EncodedStaticFieldValueIterator(const DexFile& dex_file, Handle<mirror::DexCache>* dex_cache,
Handle<mirror::ClassLoader>* class_loader,
ClassLinker* linker, const DexFile::ClassDef& class_def)
@@ -1541,6 +1547,9 @@
kBoolean = 0x1f
};
+ ValueType GetValueType() const { return type_; }
+ const jvalue& GetJavaValue() const { return jval_; }
+
private:
static constexpr uint8_t kEncodedValueTypeMask = 0x1f; // 0b11111
static constexpr uint8_t kEncodedValueArgShift = 5;
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index ee7b986..3eea723 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -86,6 +86,23 @@
V(CmpgFloat, int32_t, float, float) \
V(CmplDouble, int32_t, double, double) \
V(CmplFloat, int32_t, float, float) \
+ V(Cos, double, double) \
+ V(Sin, double, double) \
+ V(Acos, double, double) \
+ V(Asin, double, double) \
+ V(Atan, double, double) \
+ V(Atan2, double, double, double) \
+ V(Cbrt, double, double) \
+ V(Cosh, double, double) \
+ V(Exp, double, double) \
+ V(Expm1, double, double) \
+ V(Hypot, double, double, double) \
+ V(Log, double, double) \
+ V(Log10, double, double) \
+ V(NextAfter, double, double, double) \
+ V(Sinh, double, double) \
+ V(Tan, double, double) \
+ V(Tanh, double, double) \
V(Fmod, double, double, double) \
V(L2d, double, int64_t) \
V(Fmodf, float, float, float) \
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 8587ede..391eb72 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -223,7 +223,24 @@
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmpgDouble, pCmpgFloat, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmpgFloat, pCmplDouble, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmplDouble, pCmplFloat, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmplFloat, pFmod, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmplFloat, pCos, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCos, pSin, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSin, pAcos, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAcos, pAsin, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAsin, pAtan, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAtan, pAtan2, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAtan2, pCbrt, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCbrt, pCosh, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCosh, pExp, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pExp, pExpm1, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pExpm1, pHypot, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pHypot, pLog, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pLog, pLog10, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pLog10, pNextAfter, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNextAfter, pSinh, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSinh, pTan, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pTan, pTanh, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pTanh, pFmod, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pFmod, pL2d, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pL2d, pFmodf, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pFmodf, pL2f, sizeof(void*));
diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc
index f741117..4884e66 100644
--- a/runtime/gc/collector/immune_spaces_test.cc
+++ b/runtime/gc/collector/immune_spaces_test.cc
@@ -113,7 +113,9 @@
/*oat_data_end*/PointerToLowMemUInt32(map->End() + oat_size),
/*oat_file_end*/PointerToLowMemUInt32(map->End() + oat_size),
/*pointer_size*/sizeof(void*),
- /*compile_pic*/false);
+ /*compile_pic*/false,
+ ImageHeader::kStorageModeUncompressed,
+ /*storage_size*/0u);
return new DummyImageSpace(map.release(), live_bitmap.release());
}
};
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index e2b2431..8f67c21 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -17,12 +17,12 @@
#include "image_space.h"
#include <dirent.h>
+#include <lz4.h>
+#include <random>
#include <sys/statvfs.h>
#include <sys/types.h>
#include <unistd.h>
-#include <random>
-
#include "art_method.h"
#include "base/macros.h"
#include "base/stl_util.h"
@@ -677,11 +677,12 @@
*error_msg = StringPrintf("Invalid image header in '%s'", image_filename);
return nullptr;
}
- // Check that the file is large enough.
- uint64_t image_file_size = static_cast<uint64_t>(file->GetLength());
- if (image_header.GetImageSize() > image_file_size) {
- *error_msg = StringPrintf("Image file too small for image heap: %" PRIu64 " vs. %zu.",
- image_file_size, image_header.GetImageSize());
+ // Check that the file is larger or equal to the header size + data size.
+ const uint64_t image_file_size = static_cast<uint64_t>(file->GetLength());
+ if (image_file_size < sizeof(ImageHeader) + image_header.GetDataSize()) {
+ *error_msg = StringPrintf("Image file truncated: %" PRIu64 " vs. %" PRIu64 ".",
+ image_file_size,
+ image_header.GetDataSize());
return nullptr;
}
@@ -697,7 +698,11 @@
}
const auto& bitmap_section = image_header.GetImageSection(ImageHeader::kSectionImageBitmap);
- auto end_of_bitmap = static_cast<size_t>(bitmap_section.End());
+ // The location we want to map from is the first aligned page after the end of the stored
+ // (possibly compressed) data.
+ const size_t image_bitmap_offset = RoundUp(sizeof(image_header) + image_header.GetDataSize(),
+ kPageSize);
+ const size_t end_of_bitmap = image_bitmap_offset + bitmap_section.Size();
if (end_of_bitmap != image_file_size) {
*error_msg = StringPrintf(
"Image file size does not equal end of bitmap: size=%" PRIu64 " vs. %zu.", image_file_size,
@@ -706,16 +711,60 @@
}
// Note: The image header is part of the image due to mmap page alignment required of offset.
- std::unique_ptr<MemMap> map(MemMap::MapFileAtAddress(image_header.GetImageBegin(),
- image_header.GetImageSize(),
- PROT_READ | PROT_WRITE,
+ std::unique_ptr<MemMap> map;
+ if (image_header.GetStorageMode() == ImageHeader::kStorageModeUncompressed) {
+ map.reset(MemMap::MapFileAtAddress(image_header.GetImageBegin(),
+ image_header.GetImageSize(),
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE,
+ file->Fd(),
+ 0,
+ /*low_4gb*/false,
+ /*reuse*/false,
+ image_filename,
+ error_msg));
+ } else {
+ // Reserve output and decompress into it.
+ map.reset(MemMap::MapAnonymous(image_location,
+ image_header.GetImageBegin(),
+ image_header.GetImageSize(),
+ PROT_READ | PROT_WRITE,
+ /*low_4gb*/false,
+ /*reuse*/false,
+ error_msg));
+ if (map != nullptr) {
+ const size_t stored_size = image_header.GetDataSize();
+ const size_t write_offset = sizeof(image_header); // Skip the header.
+ std::unique_ptr<MemMap> temp_map(MemMap::MapFile(sizeof(ImageHeader) + stored_size,
+ PROT_READ,
MAP_PRIVATE,
file->Fd(),
- 0,
+ /*offset*/0,
/*low_4gb*/false,
- /*reuse*/false,
image_filename,
error_msg));
+ if (temp_map == nullptr) {
+ DCHECK(!error_msg->empty());
+ return nullptr;
+ }
+ memcpy(map->Begin(), &image_header, sizeof(image_header));
+ const uint64_t start = NanoTime();
+ const size_t decompressed_size = LZ4_decompress_safe(
+ reinterpret_cast<char*>(temp_map->Begin()) + sizeof(ImageHeader),
+ reinterpret_cast<char*>(map->Begin()) + write_offset,
+ stored_size,
+ map->Size());
+ // TODO: VLOG(image)
+ VLOG(class_linker) << "Decompressing image took " << PrettyDuration(NanoTime() - start);
+ if (decompressed_size + sizeof(ImageHeader) != image_header.GetImageSize()) {
+ *error_msg = StringPrintf("Decompressed size does not match expected image size %zu vs %zu",
+ decompressed_size + sizeof(ImageHeader),
+ image_header.GetImageSize());
+ return nullptr;
+ }
+ }
+ }
+
if (map == nullptr) {
DCHECK(!error_msg->empty());
return nullptr;
@@ -723,16 +772,16 @@
CHECK_EQ(image_header.GetImageBegin(), map->Begin());
DCHECK_EQ(0, memcmp(&image_header, map->Begin(), sizeof(ImageHeader)));
- std::unique_ptr<MemMap> image_map(MemMap::MapFileAtAddress(nullptr,
- bitmap_section.Size(),
- PROT_READ, MAP_PRIVATE,
- file->Fd(),
- bitmap_section.Offset(),
- /*low_4gb*/false,
- /*reuse*/false,
- image_filename,
- error_msg));
- if (image_map.get() == nullptr) {
+ std::unique_ptr<MemMap> image_bitmap_map(MemMap::MapFileAtAddress(nullptr,
+ bitmap_section.Size(),
+ PROT_READ, MAP_PRIVATE,
+ file->Fd(),
+ image_bitmap_offset,
+ /*low_4gb*/false,
+ /*reuse*/false,
+ image_filename,
+ error_msg));
+ if (image_bitmap_map == nullptr) {
*error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str());
return nullptr;
}
@@ -741,9 +790,11 @@
bitmap_index));
std::unique_ptr<accounting::ContinuousSpaceBitmap> bitmap(
accounting::ContinuousSpaceBitmap::CreateFromMemMap(
- bitmap_name, image_map.release(), reinterpret_cast<uint8_t*>(map->Begin()),
+ bitmap_name,
+ image_bitmap_map.release(),
+ reinterpret_cast<uint8_t*>(map->Begin()),
accounting::ContinuousSpaceBitmap::ComputeHeapSize(bitmap_section.Size())));
- if (bitmap.get() == nullptr) {
+ if (bitmap == nullptr) {
*error_msg = StringPrintf("Could not create bitmap '%s'", bitmap_name.c_str());
return nullptr;
}
diff --git a/runtime/image.cc b/runtime/image.cc
index 2eac3fb..7d2ef75 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -24,7 +24,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '3', '\0' };
+const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '4', '\0' };
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
@@ -36,7 +36,9 @@
uint32_t oat_data_end,
uint32_t oat_file_end,
uint32_t pointer_size,
- bool compile_pic)
+ bool compile_pic,
+ StorageMode storage_mode,
+ size_t data_size)
: image_begin_(image_begin),
image_size_(image_size),
oat_checksum_(oat_checksum),
@@ -47,7 +49,9 @@
patch_delta_(0),
image_roots_(image_roots),
pointer_size_(pointer_size),
- compile_pic_(compile_pic) {
+ compile_pic_(compile_pic),
+ storage_mode_(storage_mode),
+ data_size_(data_size) {
CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize));
CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize));
diff --git a/runtime/image.h b/runtime/image.h
index a16f3c9..3032beb 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -78,10 +78,27 @@
// header of image files written by ImageWriter, read and validated by Space.
class PACKED(4) ImageHeader {
public:
+ enum StorageMode : uint32_t {
+ kStorageModeUncompressed,
+ kStorageModeLZ4,
+ kStorageModeCount, // Number of elements in enum.
+ };
+ static constexpr StorageMode kDefaultStorageMode = kStorageModeUncompressed;
+
ImageHeader()
- : image_begin_(0U), image_size_(0U), oat_checksum_(0U), oat_file_begin_(0U),
- oat_data_begin_(0U), oat_data_end_(0U), oat_file_end_(0U), patch_delta_(0),
- image_roots_(0U), pointer_size_(0U), compile_pic_(0) {}
+ : image_begin_(0U),
+ image_size_(0U),
+ oat_checksum_(0U),
+ oat_file_begin_(0U),
+ oat_data_begin_(0U),
+ oat_data_end_(0U),
+ oat_file_end_(0U),
+ patch_delta_(0),
+ image_roots_(0U),
+ pointer_size_(0U),
+ compile_pic_(0),
+ storage_mode_(kDefaultStorageMode),
+ data_size_(0) {}
ImageHeader(uint32_t image_begin,
uint32_t image_size,
@@ -93,7 +110,9 @@
uint32_t oat_data_end,
uint32_t oat_file_end,
uint32_t pointer_size,
- bool compile_pic);
+ bool compile_pic,
+ StorageMode storage_mode,
+ size_t data_size);
bool IsValid() const;
const char* GetMagic() const;
@@ -194,6 +213,14 @@
return compile_pic_ != 0;
}
+ StorageMode GetStorageMode() const {
+ return storage_mode_;
+ }
+
+ uint64_t GetDataSize() const {
+ return data_size_;
+ }
+
private:
static const uint8_t kImageMagic[4];
static const uint8_t kImageVersion[4];
@@ -241,6 +268,13 @@
// Image methods.
uint64_t image_methods_[kImageMethodsCount];
+ // Storage method for the image, the image may be compressed.
+ StorageMode storage_mode_;
+
+ // Data size for the image data excluding the bitmap and the header. For compressed images, this
+ // is the compressed size in the file.
+ uint32_t data_size_;
+
friend class ImageWriter;
};
@@ -248,6 +282,7 @@
std::ostream& operator<<(std::ostream& os, const ImageHeader::ImageRoot& policy);
std::ostream& operator<<(std::ostream& os, const ImageHeader::ImageSections& section);
std::ostream& operator<<(std::ostream& os, const ImageSection& section);
+std::ostream& operator<<(std::ostream& os, const ImageHeader::StorageMode& mode);
} // namespace art
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index d13526b..2d0ae63 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -344,8 +344,11 @@
segment_state_.all = new_state;
}
- static Offset SegmentStateOffset() {
- return Offset(OFFSETOF_MEMBER(IndirectReferenceTable, segment_state_));
+ static Offset SegmentStateOffset(size_t pointer_size ATTRIBUTE_UNUSED) {
+ // Note: Currently segment_state_ is at offset 0. We're testing the expected value in
+ // jni_internal_test to make sure it stays correct. It is not OFFSETOF_MEMBER, as that
+ // is not pointer-size-safe.
+ return Offset(0);
}
// Release pages past the end of the table that may have previously held references.
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 92aa86e..a653440 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -188,7 +188,7 @@
uint64_t last_update_ns = code_cache_->GetLastUpdateTimeNs();
if (offline_profile_info_->NeedsSaving(last_update_ns)) {
- VLOG(profiler) << "Iniate save profiling information to: " << filename;
+ VLOG(profiler) << "Initiate save profiling information to: " << filename;
std::set<ArtMethod*> methods;
{
ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index 4450653..7615870 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -68,7 +68,6 @@
}
}
-
void OfflineProfilingInfo::AddMethodInfo(ArtMethod* method, DexFileToMethodsMap* info) {
DCHECK(method != nullptr);
const DexFile* dex_file = method->GetDexFile();
@@ -80,11 +79,25 @@
info_it->second.insert(method->GetDexMethodIndex());
}
-static int OpenOrCreateFile(const std::string& filename) {
- // TODO(calin) allow the shared uid of the app to access the file.
- int fd = open(filename.c_str(),
- O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
- S_IRUSR | S_IWUSR);
+enum OpenMode {
+ READ,
+ READ_WRITE
+};
+
+static int OpenFile(const std::string& filename, OpenMode open_mode) {
+ int fd = -1;
+ switch (open_mode) {
+ case READ:
+ fd = open(filename.c_str(), O_RDONLY);
+ 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);
+ break;
+ }
+
if (fd < 0) {
PLOG(WARNING) << "Failed to open profile file " << filename;
return -1;
@@ -96,7 +109,6 @@
PLOG(WARNING) << "Failed to lock profile file " << filename;
return -1;
}
-
return fd;
}
@@ -129,8 +141,8 @@
} while (length > 0);
}
-static constexpr char kFieldSeparator = ',';
-static constexpr char kLineSeparator = '\n';
+static constexpr const char kFieldSeparator = ',';
+static constexpr const char kLineSeparator = '\n';
/**
* Serialization format:
@@ -142,7 +154,7 @@
**/
bool OfflineProfilingInfo::Serialize(const std::string& filename,
const DexFileToMethodsMap& info) const {
- int fd = OpenOrCreateFile(filename);
+ int fd = OpenFile(filename, READ_WRITE);
if (fd == -1) {
return false;
}
@@ -168,4 +180,212 @@
return CloseDescriptorForFile(fd, filename);
}
+
+// TODO(calin): This a duplicate of Utils::Split fixing the case where the first character
+// is the separator. Merge the fix into Utils::Split once verified that it doesn't break its users.
+static void SplitString(const std::string& s, char separator, std::vector<std::string>* result) {
+ const char* p = s.data();
+ const char* end = p + s.size();
+ // Check if the first character is the separator.
+ if (p != end && *p ==separator) {
+ result->push_back("");
+ ++p;
+ }
+ // Process the rest of the characters.
+ while (p != end) {
+ if (*p == separator) {
+ ++p;
+ } else {
+ const char* start = p;
+ while (++p != end && *p != separator) {
+ // Skip to the next occurrence of the separator.
+ }
+ result->push_back(std::string(start, p - start));
+ }
+ }
+}
+
+bool ProfileCompilationInfo::ProcessLine(const std::string& line,
+ const std::vector<const DexFile*>& dex_files) {
+ std::vector<std::string> parts;
+ SplitString(line, kFieldSeparator, &parts);
+ if (parts.size() < 3) {
+ LOG(WARNING) << "Invalid line: " << line;
+ return false;
+ }
+
+ const std::string& multidex_suffix = parts[0];
+ uint32_t checksum;
+ if (!ParseInt(parts[1].c_str(), &checksum)) {
+ return false;
+ }
+
+ const DexFile* current_dex_file = nullptr;
+ for (auto dex_file : dex_files) {
+ if (DexFile::GetMultiDexSuffix(dex_file->GetLocation()) == multidex_suffix) {
+ 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);
+ }
+ return true;
+}
+
+// Parses the buffer (of length n) starting from start_from and identify new lines
+// based on kLineSeparator marker.
+// Returns the first position after kLineSeparator in the buffer (starting from start_from),
+// or -1 if the marker doesn't appear.
+// The processed characters are appended to the given line.
+static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& line) {
+ if (start_from >= n) {
+ return -1;
+ }
+ int new_line_pos = -1;
+ for (int i = start_from; i < n; i++) {
+ if (buffer[i] == kLineSeparator) {
+ new_line_pos = i;
+ break;
+ }
+ }
+ int append_limit = new_line_pos == -1 ? n : new_line_pos;
+ line.append(buffer + start_from, append_limit - start_from);
+ // Jump over kLineSeparator and return the position of the next character.
+ 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 multidex suffixes are unique.
+ std::set<std::string> suffixes;
+ for (auto dex_file : dex_files) {
+ std::string multidex_suffix = DexFile::GetMultiDexSuffix(dex_file->GetLocation());
+ DCHECK(suffixes.find(multidex_suffix) == suffixes.end())
+ << "DexFiles appear to belong to different apks."
+ << " There are multiple dex files with the same multidex suffix: "
+ << multidex_suffix;
+ suffixes.insert(multidex_suffix);
+ }
+ }
+ info_.clear();
+
+ int fd = OpenFile(filename_, READ);
+ if (fd == -1) {
+ return false;
+ }
+
+ std::string current_line;
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
+ bool success = true;
+
+ while (success) {
+ int n = read(fd, buffer, kBufferSize);
+ if (n < 0) {
+ PLOG(WARNING) << "Error when reading profile file " << filename_;
+ success = false;
+ break;
+ } else if (n == 0) {
+ break;
+ }
+ // Detect the new lines from the buffer. If we manage to complete a line,
+ // process it. Otherwise append to the current line.
+ int current_start_pos = 0;
+ while (current_start_pos < n) {
+ current_start_pos = GetLineFromBuffer(buffer, n, current_start_pos, current_line);
+ if (current_start_pos == -1) {
+ break;
+ }
+ if (!ProcessLine(current_line, dex_files)) {
+ success = false;
+ break;
+ }
+ // Reset the current line (we just processed it).
+ current_line.clear();
+ }
+ }
+ if (!success) {
+ info_.clear();
+ }
+ return CloseDescriptorForFile(fd, filename_) && success;
+}
+
+bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
+ auto info_it = info_.find(method_ref.dex_file);
+ 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();
+ }
+ return false;
+ }
+ return false;
+}
+
+std::string ProfileCompilationInfo::DumpInfo(bool print_full_dex_location) const {
+ std::ostringstream os;
+ if (info_.empty()) {
+ return "ProfileInfo: empty";
+ }
+
+ 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) {
+ os << "\n";
+ const std::string& location = dex_file_it.first;
+ const DexFile* dex_file = dex_file_it.second;
+ if (print_full_dex_location) {
+ os << location;
+ } else {
+ // Replace the (empty) multidex suffix of the first key with a substitute for easier reading.
+ 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);
+ }
+ }
+ }
+ return os.str();
+}
+
} // namespace art
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index e3117eb..90bda60 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -21,6 +21,7 @@
#include "atomic.h"
#include "dex_file.h"
+#include "method_reference.h"
#include "safe_map.h"
namespace art {
@@ -50,10 +51,47 @@
bool Serialize(const std::string& filename, const DexFileToMethodsMap& info) const;
// TODO(calin): Verify if Atomic is really needed (are we sure to be called from a
- // singe thread?)
+ // single thread?)
Atomic<uint64_t> last_update_time_ns_;
};
+/**
+ * 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) {}
+
+ // 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);
+
+ // 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.
+ // This is intended for testing and debugging.
+ std::string DumpInfo(bool print_full_dex_location = true) const;
+
+ private:
+ bool ProcessLine(const std::string& line,
+ const std::vector<const DexFile*>& dex_files);
+
+ 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>;
+
+ const std::string filename_;
+ DexFileToProfileInfoMap info_;
+};
+
} // namespace art
#endif // ART_RUNTIME_JIT_OFFLINE_PROFILING_INFO_H_
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index dab1040..aa25f67 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -105,9 +105,32 @@
stacked_local_ref_cookies.pop_back();
}
-Offset JNIEnvExt::SegmentStateOffset() {
- return Offset(OFFSETOF_MEMBER(JNIEnvExt, locals) +
- IndirectReferenceTable::SegmentStateOffset().Int32Value());
+// Note: the offset code is brittle, as we can't use OFFSETOF_MEMBER or offsetof easily. Thus, there
+// are tests in jni_internal_test to match the results against the actual values.
+
+// This is encoding the knowledge of the structure and layout of JNIEnv fields.
+static size_t JNIEnvSize(size_t pointer_size) {
+ // A single pointer.
+ return pointer_size;
+}
+
+Offset JNIEnvExt::SegmentStateOffset(size_t pointer_size) {
+ size_t locals_offset = JNIEnvSize(pointer_size) +
+ 2 * pointer_size + // Thread* self + JavaVMExt* vm.
+ 4 + // local_ref_cookie.
+ (pointer_size - 4); // Padding.
+ size_t irt_segment_state_offset =
+ IndirectReferenceTable::SegmentStateOffset(pointer_size).Int32Value();
+ return Offset(locals_offset + irt_segment_state_offset);
+}
+
+Offset JNIEnvExt::LocalRefCookieOffset(size_t pointer_size) {
+ return Offset(JNIEnvSize(pointer_size) +
+ 2 * pointer_size); // Thread* self + JavaVMExt* vm
+}
+
+Offset JNIEnvExt::SelfOffset(size_t pointer_size) {
+ return Offset(JNIEnvSize(pointer_size));
}
// Use some defining part of the caller's frame as the identifying mark for the JNI segment.
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index 3828ff0..2f8decf 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -50,15 +50,9 @@
T AddLocalReference(mirror::Object* obj)
SHARED_REQUIRES(Locks::mutator_lock_);
- static Offset SegmentStateOffset();
-
- static Offset LocalRefCookieOffset() {
- return Offset(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie));
- }
-
- static Offset SelfOffset() {
- return Offset(OFFSETOF_MEMBER(JNIEnvExt, self));
- }
+ static Offset SegmentStateOffset(size_t pointer_size);
+ static Offset LocalRefCookieOffset(size_t pointer_size);
+ static Offset SelfOffset(size_t pointer_size);
jobject NewLocalRef(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
void DeleteLocalRef(jobject obj) SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index d1687d7..b41d16b 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -18,7 +18,9 @@
#include "art_method-inl.h"
#include "common_compiler_test.h"
+#include "indirect_reference_table.h"
#include "java_vm_ext.h"
+#include "jni_env_ext.h"
#include "mirror/string-inl.h"
#include "scoped_thread_state_change.h"
#include "ScopedLocalRef.h"
@@ -2261,4 +2263,41 @@
env_->DeleteGlobalRef(global_ref);
}
+// Test the offset computation of IndirectReferenceTable offsets. b/26071368.
+TEST_F(JniInternalTest, IndirectReferenceTableOffsets) {
+ // The segment_state_ field is private, and we want to avoid friend declaration. So we'll check
+ // by modifying memory.
+ // The parameters don't really matter here.
+ IndirectReferenceTable irt(5, 5, IndirectRefKind::kGlobal, true);
+ uint32_t old_state = irt.GetSegmentState();
+
+ // Write some new state directly. We invert parts of old_state to ensure a new value.
+ uint32_t new_state = old_state ^ 0x07705005;
+ ASSERT_NE(old_state, new_state);
+
+ uint8_t* base = reinterpret_cast<uint8_t*>(&irt);
+ int32_t segment_state_offset =
+ IndirectReferenceTable::SegmentStateOffset(sizeof(void*)).Int32Value();
+ *reinterpret_cast<uint32_t*>(base + segment_state_offset) = new_state;
+
+ // Read and compare.
+ EXPECT_EQ(new_state, irt.GetSegmentState());
+}
+
+// Test the offset computation of JNIEnvExt offsets. b/26071368.
+TEST_F(JniInternalTest, JNIEnvExtOffsets) {
+ EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie),
+ JNIEnvExt::LocalRefCookieOffset(sizeof(void*)).Int32Value());
+
+ EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, self), JNIEnvExt::SelfOffset(sizeof(void*)).Int32Value());
+
+ // segment_state_ is private in the IndirectReferenceTable. So this test isn't as good as we'd
+ // hope it to be.
+ int32_t segment_state_now =
+ OFFSETOF_MEMBER(JNIEnvExt, locals) +
+ IndirectReferenceTable::SegmentStateOffset(sizeof(void*)).Int32Value();
+ int32_t segment_state_computed = JNIEnvExt::SegmentStateOffset(sizeof(void*)).Int32Value();
+ EXPECT_EQ(segment_state_now, segment_state_computed);
+}
+
} // namespace art
diff --git a/runtime/noop_compiler_callbacks.h b/runtime/noop_compiler_callbacks.h
index 1cbf2bb..02081cb 100644
--- a/runtime/noop_compiler_callbacks.h
+++ b/runtime/noop_compiler_callbacks.h
@@ -26,8 +26,7 @@
NoopCompilerCallbacks() : CompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileApp) {}
~NoopCompilerCallbacks() {}
- bool MethodVerified(verifier::MethodVerifier* verifier ATTRIBUTE_UNUSED) OVERRIDE {
- return true;
+ void MethodVerified(verifier::MethodVerifier* verifier ATTRIBUTE_UNUSED) OVERRIDE {
}
void ClassRejected(ClassReference ref ATTRIBUTE_UNUSED) OVERRIDE {}
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 40aca0d..c787b9a 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -45,9 +45,7 @@
OatHeader* OatHeader::Create(InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
- const std::vector<const DexFile*>* dex_files,
- uint32_t image_file_location_oat_checksum,
- uint32_t image_file_location_oat_data_begin,
+ uint32_t dex_file_count,
const SafeMap<std::string, std::string>* variable_data) {
// Estimate size of optional data.
size_t needed_size = ComputeOatHeaderSize(variable_data);
@@ -58,18 +56,29 @@
// Create the OatHeader in-place.
return new (memory) OatHeader(instruction_set,
instruction_set_features,
- dex_files,
- image_file_location_oat_checksum,
- image_file_location_oat_data_begin,
+ dex_file_count,
variable_data);
}
OatHeader::OatHeader(InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
- const std::vector<const DexFile*>* dex_files,
- uint32_t image_file_location_oat_checksum,
- uint32_t image_file_location_oat_data_begin,
- const SafeMap<std::string, std::string>* variable_data) {
+ uint32_t dex_file_count,
+ const SafeMap<std::string, std::string>* variable_data)
+ : adler32_checksum_(adler32(0L, Z_NULL, 0)),
+ instruction_set_(instruction_set),
+ instruction_set_features_bitmap_(instruction_set_features->AsBitmap()),
+ dex_file_count_(dex_file_count),
+ executable_offset_(0),
+ interpreter_to_interpreter_bridge_offset_(0),
+ interpreter_to_compiled_code_bridge_offset_(0),
+ jni_dlsym_lookup_offset_(0),
+ quick_generic_jni_trampoline_offset_(0),
+ quick_imt_conflict_trampoline_offset_(0),
+ quick_resolution_trampoline_offset_(0),
+ quick_to_interpreter_bridge_offset_(0),
+ image_patch_delta_(0),
+ image_file_location_oat_checksum_(0),
+ image_file_location_oat_data_begin_(0) {
// Don't want asserts in header as they would be checked in each file that includes it. But the
// fields are private, so we check inside a method.
static_assert(sizeof(magic_) == sizeof(kOatMagic),
@@ -79,46 +88,11 @@
memcpy(magic_, kOatMagic, sizeof(kOatMagic));
memcpy(version_, kOatVersion, sizeof(kOatVersion));
- executable_offset_ = 0;
- image_patch_delta_ = 0;
-
- adler32_checksum_ = adler32(0L, Z_NULL, 0);
CHECK_NE(instruction_set, kNone);
- instruction_set_ = instruction_set;
- UpdateChecksum(&instruction_set_, sizeof(instruction_set_));
-
- instruction_set_features_bitmap_ = instruction_set_features->AsBitmap();
- UpdateChecksum(&instruction_set_features_bitmap_, sizeof(instruction_set_features_bitmap_));
-
- dex_file_count_ = dex_files->size();
- UpdateChecksum(&dex_file_count_, sizeof(dex_file_count_));
-
- image_file_location_oat_checksum_ = image_file_location_oat_checksum;
- UpdateChecksum(&image_file_location_oat_checksum_, sizeof(image_file_location_oat_checksum_));
-
- CHECK_ALIGNED(image_file_location_oat_data_begin, kPageSize);
- image_file_location_oat_data_begin_ = image_file_location_oat_data_begin;
- UpdateChecksum(&image_file_location_oat_data_begin_, sizeof(image_file_location_oat_data_begin_));
// Flatten the map. Will also update variable_size_data_size_.
Flatten(variable_data);
-
- // Update checksum for variable data size.
- UpdateChecksum(&key_value_store_size_, sizeof(key_value_store_size_));
-
- // Update for data, if existing.
- if (key_value_store_size_ > 0U) {
- UpdateChecksum(&key_value_store_, key_value_store_size_);
- }
-
- interpreter_to_interpreter_bridge_offset_ = 0;
- interpreter_to_compiled_code_bridge_offset_ = 0;
- jni_dlsym_lookup_offset_ = 0;
- quick_generic_jni_trampoline_offset_ = 0;
- quick_imt_conflict_trampoline_offset_ = 0;
- quick_resolution_trampoline_offset_ = 0;
- quick_to_interpreter_bridge_offset_ = 0;
}
bool OatHeader::IsValid() const {
@@ -175,6 +149,37 @@
return adler32_checksum_;
}
+void OatHeader::UpdateChecksumWithHeaderData() {
+ UpdateChecksum(&instruction_set_, sizeof(instruction_set_));
+ UpdateChecksum(&instruction_set_features_bitmap_, sizeof(instruction_set_features_bitmap_));
+ UpdateChecksum(&dex_file_count_, sizeof(dex_file_count_));
+ UpdateChecksum(&image_file_location_oat_checksum_, sizeof(image_file_location_oat_checksum_));
+ UpdateChecksum(&image_file_location_oat_data_begin_, sizeof(image_file_location_oat_data_begin_));
+
+ // Update checksum for variable data size.
+ UpdateChecksum(&key_value_store_size_, sizeof(key_value_store_size_));
+
+ // Update for data, if existing.
+ if (key_value_store_size_ > 0U) {
+ UpdateChecksum(&key_value_store_, key_value_store_size_);
+ }
+
+ UpdateChecksum(&executable_offset_, sizeof(executable_offset_));
+ UpdateChecksum(&interpreter_to_interpreter_bridge_offset_,
+ sizeof(interpreter_to_interpreter_bridge_offset_));
+ UpdateChecksum(&interpreter_to_compiled_code_bridge_offset_,
+ sizeof(interpreter_to_compiled_code_bridge_offset_));
+ UpdateChecksum(&jni_dlsym_lookup_offset_, sizeof(jni_dlsym_lookup_offset_));
+ UpdateChecksum(&quick_generic_jni_trampoline_offset_,
+ sizeof(quick_generic_jni_trampoline_offset_));
+ UpdateChecksum(&quick_imt_conflict_trampoline_offset_,
+ sizeof(quick_imt_conflict_trampoline_offset_));
+ UpdateChecksum(&quick_resolution_trampoline_offset_,
+ sizeof(quick_resolution_trampoline_offset_));
+ UpdateChecksum(&quick_to_interpreter_bridge_offset_,
+ sizeof(quick_to_interpreter_bridge_offset_));
+}
+
void OatHeader::UpdateChecksum(const void* data, size_t length) {
DCHECK(IsValid());
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data);
@@ -205,7 +210,6 @@
DCHECK_EQ(executable_offset_, 0U);
executable_offset_ = executable_offset;
- UpdateChecksum(&executable_offset_, sizeof(executable_offset));
}
const void* OatHeader::GetInterpreterToInterpreterBridge() const {
@@ -225,7 +229,6 @@
DCHECK_EQ(interpreter_to_interpreter_bridge_offset_, 0U) << offset;
interpreter_to_interpreter_bridge_offset_ = offset;
- UpdateChecksum(&interpreter_to_interpreter_bridge_offset_, sizeof(offset));
}
const void* OatHeader::GetInterpreterToCompiledCodeBridge() const {
@@ -244,7 +247,6 @@
DCHECK_EQ(interpreter_to_compiled_code_bridge_offset_, 0U) << offset;
interpreter_to_compiled_code_bridge_offset_ = offset;
- UpdateChecksum(&interpreter_to_compiled_code_bridge_offset_, sizeof(offset));
}
const void* OatHeader::GetJniDlsymLookup() const {
@@ -263,7 +265,6 @@
DCHECK_EQ(jni_dlsym_lookup_offset_, 0U) << offset;
jni_dlsym_lookup_offset_ = offset;
- UpdateChecksum(&jni_dlsym_lookup_offset_, sizeof(offset));
}
const void* OatHeader::GetQuickGenericJniTrampoline() const {
@@ -282,7 +283,6 @@
DCHECK_EQ(quick_generic_jni_trampoline_offset_, 0U) << offset;
quick_generic_jni_trampoline_offset_ = offset;
- UpdateChecksum(&quick_generic_jni_trampoline_offset_, sizeof(offset));
}
const void* OatHeader::GetQuickImtConflictTrampoline() const {
@@ -301,7 +301,6 @@
DCHECK_EQ(quick_imt_conflict_trampoline_offset_, 0U) << offset;
quick_imt_conflict_trampoline_offset_ = offset;
- UpdateChecksum(&quick_imt_conflict_trampoline_offset_, sizeof(offset));
}
const void* OatHeader::GetQuickResolutionTrampoline() const {
@@ -320,7 +319,6 @@
DCHECK_EQ(quick_resolution_trampoline_offset_, 0U) << offset;
quick_resolution_trampoline_offset_ = offset;
- UpdateChecksum(&quick_resolution_trampoline_offset_, sizeof(offset));
}
const void* OatHeader::GetQuickToInterpreterBridge() const {
@@ -339,7 +337,6 @@
DCHECK_EQ(quick_to_interpreter_bridge_offset_, 0U) << offset;
quick_to_interpreter_bridge_offset_ = offset;
- UpdateChecksum(&quick_to_interpreter_bridge_offset_, sizeof(offset));
}
int32_t OatHeader::GetImagePatchDelta() const {
@@ -367,11 +364,22 @@
return image_file_location_oat_checksum_;
}
+void OatHeader::SetImageFileLocationOatChecksum(uint32_t image_file_location_oat_checksum) {
+ CHECK(IsValid());
+ image_file_location_oat_checksum_ = image_file_location_oat_checksum;
+}
+
uint32_t OatHeader::GetImageFileLocationOatDataBegin() const {
CHECK(IsValid());
return image_file_location_oat_data_begin_;
}
+void OatHeader::SetImageFileLocationOatDataBegin(uint32_t image_file_location_oat_data_begin) {
+ CHECK(IsValid());
+ CHECK_ALIGNED(image_file_location_oat_data_begin, kPageSize);
+ image_file_location_oat_data_begin_ = image_file_location_oat_data_begin;
+}
+
uint32_t OatHeader::GetKeyValueStoreSize() const {
CHECK(IsValid());
return key_value_store_size_;
diff --git a/runtime/oat.h b/runtime/oat.h
index 5b780c3..5ed1977 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -31,7 +31,7 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '0', '7', '3', '\0' };
+ static constexpr uint8_t kOatVersion[] = { '0', '7', '4', '\0' };
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
@@ -45,15 +45,14 @@
static OatHeader* Create(InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
- const std::vector<const DexFile*>* dex_files,
- uint32_t image_file_location_oat_checksum,
- uint32_t image_file_location_oat_data_begin,
+ uint32_t dex_file_count,
const SafeMap<std::string, std::string>* variable_data);
bool IsValid() const;
std::string GetValidationErrorMessage() const;
const char* GetMagic() const;
uint32_t GetChecksum() const;
+ void UpdateChecksumWithHeaderData();
void UpdateChecksum(const void* data, size_t length);
uint32_t GetDexFileCount() const {
DCHECK(IsValid());
@@ -92,8 +91,11 @@
InstructionSet GetInstructionSet() const;
uint32_t GetInstructionSetFeaturesBitmap() const;
+
uint32_t GetImageFileLocationOatChecksum() const;
+ void SetImageFileLocationOatChecksum(uint32_t image_file_location_oat_checksum);
uint32_t GetImageFileLocationOatDataBegin() const;
+ void SetImageFileLocationOatDataBegin(uint32_t image_file_location_oat_data_begin);
uint32_t GetKeyValueStoreSize() const;
const uint8_t* GetKeyValueStore() const;
@@ -107,9 +109,7 @@
private:
OatHeader(InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
- const std::vector<const DexFile*>* dex_files,
- uint32_t image_file_location_oat_checksum,
- uint32_t image_file_location_oat_data_begin,
+ uint32_t dex_file_count,
const SafeMap<std::string, std::string>* variable_data);
// Returns true if the value of the given key is "true", false otherwise.
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
index 837662d..6cea902 100644
--- a/runtime/quick/inline_method_analyser.h
+++ b/runtime/quick/inline_method_analyser.h
@@ -51,6 +51,23 @@
kIntrinsicMinMaxLong,
kIntrinsicMinMaxFloat,
kIntrinsicMinMaxDouble,
+ kIntrinsicCos,
+ kIntrinsicSin,
+ kIntrinsicAcos,
+ kIntrinsicAsin,
+ kIntrinsicAtan,
+ kIntrinsicAtan2,
+ kIntrinsicCbrt,
+ kIntrinsicCosh,
+ kIntrinsicExp,
+ kIntrinsicExpm1,
+ kIntrinsicHypot,
+ kIntrinsicLog,
+ kIntrinsicLog10,
+ kIntrinsicNextAfter,
+ kIntrinsicSinh,
+ kIntrinsicTan,
+ kIntrinsicTanh,
kIntrinsicSqrt,
kIntrinsicCeil,
kIntrinsicFloor,
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index dedc110..bfd9ffe 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -219,8 +219,6 @@
UnloadNativeBridge();
}
- MaybeSaveJitProfilingInfo();
-
if (dump_gc_performance_on_shutdown_) {
// This can't be called from the Heap destructor below because it
// could call RosAlloc::InspectAll() which needs the thread_list
@@ -1293,6 +1291,11 @@
}
void Runtime::DumpForSigQuit(std::ostream& os) {
+ // Dumping for SIGQIT may cause deadlocks if the the debugger is active. b/26118154
+ if (Dbg::IsDebuggerActive()) {
+ LOG(INFO) << "Skipping DumpForSigQuit due to active debugger";
+ return;
+ }
GetClassLinker()->DumpForSigQuit(os);
GetInternTable()->DumpForSigQuit(os);
GetJavaVM()->DumpForSigQuit(os);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 90539b4..8a8c02f 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2478,6 +2478,23 @@
QUICK_ENTRY_POINT_INFO(pCmpgFloat)
QUICK_ENTRY_POINT_INFO(pCmplDouble)
QUICK_ENTRY_POINT_INFO(pCmplFloat)
+ QUICK_ENTRY_POINT_INFO(pCos)
+ QUICK_ENTRY_POINT_INFO(pSin)
+ QUICK_ENTRY_POINT_INFO(pAcos)
+ QUICK_ENTRY_POINT_INFO(pAsin)
+ QUICK_ENTRY_POINT_INFO(pAtan)
+ QUICK_ENTRY_POINT_INFO(pAtan2)
+ QUICK_ENTRY_POINT_INFO(pCbrt)
+ QUICK_ENTRY_POINT_INFO(pCosh)
+ QUICK_ENTRY_POINT_INFO(pExp)
+ QUICK_ENTRY_POINT_INFO(pExpm1)
+ QUICK_ENTRY_POINT_INFO(pHypot)
+ QUICK_ENTRY_POINT_INFO(pLog)
+ QUICK_ENTRY_POINT_INFO(pLog10)
+ QUICK_ENTRY_POINT_INFO(pNextAfter)
+ QUICK_ENTRY_POINT_INFO(pSinh)
+ QUICK_ENTRY_POINT_INFO(pTan)
+ QUICK_ENTRY_POINT_INFO(pTanh)
QUICK_ENTRY_POINT_INFO(pFmod)
QUICK_ENTRY_POINT_INFO(pL2d)
QUICK_ENTRY_POINT_INFO(pFmodf)
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index cf27ff2..d75587b 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -116,6 +116,7 @@
MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self,
mirror::Class* klass,
+ CompilerCallbacks* callbacks,
bool allow_soft_failures,
bool log_hard_failures,
std::string* error) {
@@ -140,9 +141,9 @@
}
if (early_failure) {
*error = "Verifier rejected class " + PrettyDescriptor(klass) + failure_message;
- if (Runtime::Current()->IsAotCompiler()) {
+ if (callbacks != nullptr) {
ClassReference ref(&dex_file, klass->GetDexClassDefIndex());
- Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref);
+ callbacks->ClassRejected(ref);
}
return kHardFailure;
}
@@ -154,6 +155,7 @@
dex_cache,
class_loader,
class_def,
+ callbacks,
allow_soft_failures,
log_hard_failures,
error);
@@ -172,6 +174,7 @@
ClassDataItemIterator* it,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
+ CompilerCallbacks* callbacks,
bool allow_soft_failures,
bool log_hard_failures,
bool need_precise_constants,
@@ -212,6 +215,7 @@
it->GetMethodCodeItem(),
method,
it->GetMethodAccessFlags(),
+ callbacks,
allow_soft_failures,
log_hard_failures,
need_precise_constants,
@@ -241,6 +245,7 @@
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
const DexFile::ClassDef* class_def,
+ CompilerCallbacks* callbacks,
bool allow_soft_failures,
bool log_hard_failures,
std::string* error) {
@@ -274,6 +279,7 @@
&it,
dex_cache,
class_loader,
+ callbacks,
allow_soft_failures,
log_hard_failures,
false /* need precise constants */,
@@ -288,6 +294,7 @@
&it,
dex_cache,
class_loader,
+ callbacks,
allow_soft_failures,
log_hard_failures,
false /* need precise constants */,
@@ -322,6 +329,7 @@
const DexFile::CodeItem* code_item,
ArtMethod* method,
uint32_t method_access_flags,
+ CompilerCallbacks* callbacks,
bool allow_soft_failures,
bool log_hard_failures,
bool need_precise_constants,
@@ -336,6 +344,12 @@
// Verification completed, however failures may be pending that didn't cause the verification
// to hard fail.
CHECK(!verifier.have_pending_hard_failure_);
+
+ if (code_item != nullptr && callbacks != nullptr) {
+ // Let the interested party know that the method was verified.
+ callbacks->MethodVerified(&verifier);
+ }
+
if (verifier.failures_.size() != 0) {
if (VLOG_IS_ON(verifier)) {
verifier.DumpFailures(VLOG_STREAM(verifier) << "Soft verification failures in "
@@ -363,8 +377,14 @@
verifier.failure_messages_[verifier.failure_messages_.size() - 1]->str();
}
result = kHardFailure;
+
+ if (callbacks != nullptr) {
+ // Let the interested party know that we failed the class.
+ ClassReference ref(dex_file, dex_file->GetIndexForClassDef(*class_def));
+ callbacks->ClassRejected(ref);
+ }
}
- if (kDebugVerify) {
+ if (VLOG_IS_ON(verifier)) {
std::cout << "\n" << verifier.info_messages_.str();
verifier.Dump(std::cout);
}
@@ -408,13 +428,18 @@
}
MethodVerifier::MethodVerifier(Thread* self,
- const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
+ const DexFile* dex_file,
+ Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
const DexFile::ClassDef* class_def,
- const DexFile::CodeItem* code_item, uint32_t dex_method_idx,
- ArtMethod* method, uint32_t method_access_flags,
- bool can_load_classes, bool allow_soft_failures,
- bool need_precise_constants, bool verify_to_dump,
+ const DexFile::CodeItem* code_item,
+ uint32_t dex_method_idx,
+ ArtMethod* method,
+ uint32_t method_access_flags,
+ bool can_load_classes,
+ bool allow_soft_failures,
+ bool need_precise_constants,
+ bool verify_to_dump,
bool allow_thread_suspension)
: self_(self),
arena_stack_(Runtime::Current()->GetArenaPool()),
@@ -739,10 +764,7 @@
result = result && VerifyInstructions();
// Perform code-flow analysis and return.
result = result && VerifyCodeFlow();
- // Compute information for compiler.
- if (result && runtime->IsCompiler()) {
- result = runtime->GetCompilerCallbacks()->MethodVerified(this);
- }
+
return result;
}
@@ -802,10 +824,6 @@
// Hard verification failures at compile time will still fail at runtime, so the class is
// marked as rejected to prevent it from being compiled.
case VERIFY_ERROR_BAD_CLASS_HARD: {
- if (Runtime::Current()->IsAotCompiler()) {
- ClassReference ref(dex_file_, dex_file_->GetIndexForClassDef(*class_def_));
- Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref);
- }
have_pending_hard_failure_ = true;
if (VLOG_IS_ON(verifier) && kDumpRegLinesOnHardFailureIfVLOG) {
ScopedObjectAccess soa(Thread::Current());
@@ -3639,30 +3657,8 @@
auto* cl = Runtime::Current()->GetClassLinker();
auto pointer_size = cl->GetImagePointerSize();
- // Check that interface methods are static or match interface classes.
- // We only allow statics if we don't have default methods enabled.
- if (klass->IsInterface()) {
- Runtime* runtime = Runtime::Current();
- const bool default_methods_supported =
- runtime == nullptr ||
- runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods);
- if (method_type != METHOD_INTERFACE &&
- (!default_methods_supported || method_type != METHOD_STATIC)) {
- Fail(VERIFY_ERROR_CLASS_CHANGE)
- << "non-interface method " << PrettyMethod(dex_method_idx, *dex_file_)
- << " is in an interface class " << PrettyClass(klass);
- return nullptr;
- }
- } else {
- if (method_type == METHOD_INTERFACE) {
- Fail(VERIFY_ERROR_CLASS_CHANGE)
- << "interface method " << PrettyMethod(dex_method_idx, *dex_file_)
- << " is in a non-interface class " << PrettyClass(klass);
- return nullptr;
- }
- }
-
ArtMethod* res_method = dex_cache_->GetResolvedMethod(dex_method_idx, pointer_size);
+ bool stash_method = false;
if (res_method == nullptr) {
const char* name = dex_file_->GetMethodName(method_id);
const Signature signature = dex_file_->GetMethodSignature(method_id);
@@ -3675,7 +3671,7 @@
res_method = klass->FindVirtualMethod(name, signature, pointer_size);
}
if (res_method != nullptr) {
- dex_cache_->SetResolvedMethod(dex_method_idx, res_method, pointer_size);
+ stash_method = true;
} else {
// If a virtual or interface method wasn't found with the expected type, look in
// the direct methods. This can happen when the wrong invoke type is used or when
@@ -3704,6 +3700,38 @@
<< PrettyMethod(res_method);
return nullptr;
}
+
+ // Check that interface methods are static or match interface classes.
+ // We only allow statics if we don't have default methods enabled.
+ //
+ // Note: this check must be after the initializer check, as those are required to fail a class,
+ // while this check implies an IncompatibleClassChangeError.
+ if (klass->IsInterface()) {
+ Runtime* runtime = Runtime::Current();
+ const bool default_methods_supported =
+ runtime == nullptr ||
+ runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods);
+ if (method_type != METHOD_INTERFACE &&
+ (!default_methods_supported || method_type != METHOD_STATIC)) {
+ Fail(VERIFY_ERROR_CLASS_CHANGE)
+ << "non-interface method " << PrettyMethod(dex_method_idx, *dex_file_)
+ << " is in an interface class " << PrettyClass(klass);
+ return nullptr;
+ }
+ } else {
+ if (method_type == METHOD_INTERFACE) {
+ Fail(VERIFY_ERROR_CLASS_CHANGE)
+ << "interface method " << PrettyMethod(dex_method_idx, *dex_file_)
+ << " is in a non-interface class " << PrettyClass(klass);
+ return nullptr;
+ }
+ }
+
+ // Only stash after the above passed. Otherwise the method wasn't guaranteed to be correct.
+ if (stash_method) {
+ dex_cache_->SetResolvedMethod(dex_method_idx, res_method, pointer_size);
+ }
+
// Check if access is allowed.
if (!referrer.CanAccessMember(res_method->GetDeclaringClass(), res_method->GetAccessFlags())) {
Fail(VERIFY_ERROR_ACCESS_METHOD) << "illegal method access (call " << PrettyMethod(res_method)
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 719f0d7..79db576 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -33,6 +33,7 @@
namespace art {
+class CompilerCallbacks;
class Instruction;
struct ReferenceMap2Visitor;
class Thread;
@@ -141,6 +142,7 @@
/* Verify a class. Returns "kNoFailure" on success. */
static FailureKind VerifyClass(Thread* self,
mirror::Class* klass,
+ CompilerCallbacks* callbacks,
bool allow_soft_failures,
bool log_hard_failures,
std::string* error)
@@ -150,6 +152,7 @@
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
const DexFile::ClassDef* class_def,
+ CompilerCallbacks* callbacks,
bool allow_soft_failures,
bool log_hard_failures,
std::string* error)
@@ -216,16 +219,34 @@
return can_load_classes_;
}
- MethodVerifier(Thread* self, const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, const DexFile::ClassDef* class_def,
- const DexFile::CodeItem* code_item, uint32_t method_idx,
+ MethodVerifier(Thread* self,
+ const DexFile* dex_file,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ const DexFile::ClassDef* class_def,
+ const DexFile::CodeItem* code_item,
+ uint32_t method_idx,
ArtMethod* method,
- uint32_t access_flags, bool can_load_classes, bool allow_soft_failures,
- bool need_precise_constants, bool allow_thread_suspension)
+ uint32_t access_flags,
+ bool can_load_classes,
+ bool allow_soft_failures,
+ bool need_precise_constants,
+ bool allow_thread_suspension)
SHARED_REQUIRES(Locks::mutator_lock_)
- : MethodVerifier(self, dex_file, dex_cache, class_loader, class_def, code_item, method_idx,
- method, access_flags, can_load_classes, allow_soft_failures,
- need_precise_constants, false, allow_thread_suspension) {}
+ : MethodVerifier(self,
+ dex_file,
+ dex_cache,
+ class_loader,
+ class_def,
+ code_item,
+ method_idx,
+ method,
+ access_flags,
+ can_load_classes,
+ allow_soft_failures,
+ need_precise_constants,
+ false,
+ allow_thread_suspension) {}
~MethodVerifier();
@@ -299,12 +320,20 @@
}
// Private constructor for dumping.
- MethodVerifier(Thread* self, const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, const DexFile::ClassDef* class_def,
- const DexFile::CodeItem* code_item, uint32_t method_idx,
- ArtMethod* method, uint32_t access_flags,
- bool can_load_classes, bool allow_soft_failures, bool need_precise_constants,
- bool verify_to_dump, bool allow_thread_suspension)
+ MethodVerifier(Thread* self,
+ const DexFile* dex_file,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ const DexFile::ClassDef* class_def,
+ const DexFile::CodeItem* code_item,
+ uint32_t method_idx,
+ ArtMethod* method,
+ uint32_t access_flags,
+ bool can_load_classes,
+ bool allow_soft_failures,
+ bool need_precise_constants,
+ bool verify_to_dump,
+ bool allow_thread_suspension)
SHARED_REQUIRES(Locks::mutator_lock_);
// Adds the given string to the beginning of the last failure message.
@@ -323,6 +352,7 @@
ClassDataItemIterator* it,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
+ CompilerCallbacks* callbacks,
bool allow_soft_failures,
bool log_hard_failures,
bool need_precise_constants,
@@ -350,6 +380,7 @@
const DexFile::CodeItem* code_item,
ArtMethod* method,
uint32_t method_access_flags,
+ CompilerCallbacks* callbacks,
bool allow_soft_failures,
bool log_hard_failures,
bool need_precise_constants,
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index c4123d5..946f842 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -37,8 +37,8 @@
// Verify the class
std::string error_msg;
- ASSERT_TRUE(MethodVerifier::VerifyClass(self, klass, true, true, &error_msg) == MethodVerifier::kNoFailure)
- << error_msg;
+ ASSERT_TRUE(MethodVerifier::VerifyClass(self, klass, nullptr, true, true, &error_msg)
+ == MethodVerifier::kNoFailure) << error_msg;
}
void VerifyDexFile(const DexFile& dex)
diff --git a/test/123-inline-execute2/expected.txt b/test/123-inline-execute2/expected.txt
new file mode 100644
index 0000000..aa74fa3
--- /dev/null
+++ b/test/123-inline-execute2/expected.txt
@@ -0,0 +1,299 @@
+Math.sin(0.0) = 0.000000000000
+Math.sinh(0.0) = 0.000000000000
+Math.asin(0.0) = 0.000000000000
+Math.cos(0.0) = 1.000000000000
+Math.cosh(0.0) = 1.000000000000
+Math.acos(0.0) = 1.570796326795
+Math.tan(0.0) = 0.000000000000
+Math.tanh(0.0) = 0.000000000000
+Math.atan(0.0) = 0.000000000000
+Math.atan2(0.0, 1.0) = 0.000000000000
+Math.sin(0.7853981633974483) = 0.707106781187
+Math.sinh(0.7853981633974483) = 0.868670961486
+Math.asin(0.7853981633974483) = 0.903339110767
+Math.cos(0.7853981633974483) = 0.707106781187
+Math.cosh(0.7853981633974483) = 1.324609089252
+Math.acos(0.7853981633974483) = 0.667457216028
+Math.tan(0.7853981633974483) = 1.000000000000
+Math.tanh(0.7853981633974483) = 0.655794202633
+Math.atan(0.7853981633974483) = 0.665773750028
+Math.atan2(0.7853981633974483, 1.7853981633974483) = 0.414423800577
+Math.sin(1.5707963267948966) = 1.000000000000
+Math.sinh(1.5707963267948966) = 2.301298902307
+Math.asin(1.5707963267948966) = NaN
+Math.cos(1.5707963267948966) = 0.000000000000
+Math.cosh(1.5707963267948966) = 2.509178478658
+Math.acos(1.5707963267948966) = NaN
+Math.tanh(1.5707963267948966) = 0.917152335667
+Math.atan(1.5707963267948966) = 1.003884821854
+Math.atan2(1.5707963267948966, 2.5707963267948966) = 0.548479764417
+Math.sin(2.356194490192345) = 0.707106781187
+Math.sinh(2.356194490192345) = 5.227971924678
+Math.asin(2.356194490192345) = NaN
+Math.cos(2.356194490192345) = -0.707106781187
+Math.cosh(2.356194490192345) = 5.322752149520
+Math.acos(2.356194490192345) = NaN
+Math.tan(2.356194490192345) = -1.000000000000
+Math.tanh(2.356194490192345) = 0.982193380007
+Math.atan(2.356194490192345) = 1.169422824816
+Math.atan2(2.356194490192345, 3.356194490192345) = 0.612096117380
+Math.sin(3.141592653589793) = 0.000000000000
+Math.sinh(3.141592653589793) = 11.548739357258
+Math.asin(3.141592653589793) = NaN
+Math.cos(3.141592653589793) = -1.000000000000
+Math.cosh(3.141592653589793) = 11.591953275522
+Math.acos(3.141592653589793) = NaN
+Math.tan(3.141592653589793) = -0.000000000000
+Math.tanh(3.141592653589793) = 0.996272076221
+Math.atan(3.141592653589793) = 1.262627255679
+Math.atan2(3.141592653589793, 4.141592653589793) = 0.648948780815
+Math.sin(3.9269908169872414) = -0.707106781187
+Math.sinh(3.9269908169872414) = 25.367158319374
+Math.asin(3.9269908169872414) = NaN
+Math.cos(3.9269908169872414) = -0.707106781187
+Math.cosh(3.9269908169872414) = 25.386861192361
+Math.acos(3.9269908169872414) = NaN
+Math.tan(3.9269908169872414) = 1.000000000000
+Math.tanh(3.9269908169872414) = 0.999223894879
+Math.atan(3.9269908169872414) = 1.321447967784
+Math.atan2(3.9269908169872414, 4.926990816987241) = 0.672931229191
+Math.sin(4.71238898038469) = -1.000000000000
+Math.sinh(4.71238898038469) = 55.654397599418
+Math.asin(4.71238898038469) = NaN
+Math.cos(4.71238898038469) = -0.000000000000
+Math.cosh(4.71238898038469) = 55.663380890439
+Math.acos(4.71238898038469) = NaN
+Math.tanh(4.71238898038469) = 0.999838613989
+Math.atan(4.71238898038469) = 1.361691682971
+Math.atan2(4.71238898038469, 5.71238898038469) = 0.689765469251
+Math.sin(5.497787143782138) = -0.707106781187
+Math.sinh(5.497787143782138) = 122.073483514693
+Math.asin(5.497787143782138) = NaN
+Math.cos(5.497787143782138) = 0.707106781187
+Math.cosh(5.497787143782138) = 122.077579339582
+Math.acos(5.497787143782138) = NaN
+Math.tan(5.497787143782138) = -1.000000000000
+Math.tanh(5.497787143782138) = 0.999966449000
+Math.atan(5.497787143782138) = 1.390871988014
+Math.atan2(5.497787143782138, 6.497787143782138) = 0.702226398171
+Math.sin(6.283185307179586) = -0.000000000000
+Math.sinh(6.283185307179586) = 267.744894041016
+Math.asin(6.283185307179586) = NaN
+Math.cos(6.283185307179586) = 1.000000000000
+Math.cosh(6.283185307179586) = 267.746761483748
+Math.acos(6.283185307179586) = NaN
+Math.tan(6.283185307179586) = -0.000000000000
+Math.tanh(6.283185307179586) = 0.999993025340
+Math.atan(6.283185307179586) = 1.412965136507
+Math.atan2(6.283185307179586, 7.283185307179586) = 0.711819549590
+Math.cbrt(-3.0) = -1.442249570307
+Math.log(-3.0) = NaN
+Math.log10(-3.0) = NaN
+Math.log1p(-3.0) = NaN
+Math.exp(-3.0) = 0.049787068368
+Math.expm1(-3.0) = -0.950212931632
+Math.pow(-3.0, -2.0) = 0.111111111111
+Math.hypot(-3.0, -2.0) = 3.605551275464
+Math.cbrt(-2.0) = -1.259921049895
+Math.log(-2.0) = NaN
+Math.log10(-2.0) = NaN
+Math.log1p(-2.0) = NaN
+Math.exp(-2.0) = 0.135335283237
+Math.expm1(-2.0) = -0.864664716763
+Math.pow(-2.0, -1.0) = -0.500000000000
+Math.hypot(-2.0, -1.0) = 2.236067977500
+Math.cbrt(-1.0) = -1.000000000000
+Math.log(-1.0) = NaN
+Math.log10(-1.0) = NaN
+Math.log1p(-1.0) = -Infinity
+Math.exp(-1.0) = 0.367879441171
+Math.expm1(-1.0) = -0.632120558829
+Math.pow(-1.0, 0.0) = 1.000000000000
+Math.hypot(-1.0, 0.0) = 1.000000000000
+Math.cbrt(0.0) = 0.000000000000
+Math.log(0.0) = -Infinity
+Math.log10(0.0) = -Infinity
+Math.log1p(0.0) = 0.000000000000
+Math.exp(0.0) = 1.000000000000
+Math.expm1(0.0) = 0.000000000000
+Math.pow(0.0, 1.0) = 0.000000000000
+Math.hypot(0.0, 1.0) = 1.000000000000
+Math.cbrt(1.0) = 1.000000000000
+Math.log(1.0) = 0.000000000000
+Math.log10(1.0) = 0.000000000000
+Math.log1p(1.0) = 0.693147180560
+Math.exp(1.0) = 2.718281828459
+Math.expm1(1.0) = 1.718281828459
+Math.pow(1.0, 2.0) = 1.000000000000
+Math.hypot(1.0, 2.0) = 2.236067977500
+Math.cbrt(2.0) = 1.259921049895
+Math.log(2.0) = 0.693147180560
+Math.log10(2.0) = 0.301029995664
+Math.log1p(2.0) = 1.098612288668
+Math.exp(2.0) = 7.389056098931
+Math.expm1(2.0) = 6.389056098931
+Math.pow(2.0, 3.0) = 8.000000000000
+Math.hypot(2.0, 3.0) = 3.605551275464
+Math.cbrt(3.0) = 1.442249570307
+Math.log(3.0) = 1.098612288668
+Math.log10(3.0) = 0.477121254720
+Math.log1p(3.0) = 1.386294361120
+Math.exp(3.0) = 20.085536923188
+Math.expm1(3.0) = 19.085536923188
+Math.pow(3.0, 4.0) = 81.000000000000
+Math.hypot(3.0, 4.0) = 5.000000000000
+Math.ceil(0.0001) = 1.000000000000
+Math.floor(0.0001) = 0.000000000000
+Math.nextAfter(1.0, 2.0) = 1.000000000000
+Math.nextAfter(2.0, 1.0) = 2.000000000000
+Math.rint(0.5000001) = 1.000000000000
+StrictMath.sin(0.0) = 0.0
+StrictMath.sinh(0.0) = 0.0
+StrictMath.asin(0.0) = 0.0
+StrictMath.cos(0.0) = 1.0
+StrictMath.cosh(0.0) = 1.0
+StrictMath.acos(0.0) = 1.5707963267948966
+StrictMath.tan(0.0) = 0.0
+StrictMath.tanh(0.0) = 0.0
+StrictMath.atan(0.0) = 0.0
+StrictMath.atan2(0.0, 1.0) = 0.0
+StrictMath.sin(0.7853981633974483) = 0.7071067811865475
+StrictMath.sinh(0.7853981633974483) = 0.8686709614860095
+StrictMath.asin(0.7853981633974483) = 0.9033391107665127
+StrictMath.cos(0.7853981633974483) = 0.7071067811865476
+StrictMath.cosh(0.7853981633974483) = 1.3246090892520057
+StrictMath.acos(0.7853981633974483) = 0.6674572160283838
+StrictMath.tan(0.7853981633974483) = 0.9999999999999999
+StrictMath.tanh(0.7853981633974483) = 0.6557942026326724
+StrictMath.atan(0.7853981633974483) = 0.6657737500283538
+StrictMath.atan2(0.7853981633974483, 1.7853981633974483) = 0.41442380057704103
+StrictMath.sin(1.5707963267948966) = 1.0
+StrictMath.sinh(1.5707963267948966) = 2.3012989023072947
+StrictMath.asin(1.5707963267948966) = NaN
+StrictMath.cos(1.5707963267948966) = 6.123233995736766E-17
+StrictMath.cosh(1.5707963267948966) = 2.5091784786580567
+StrictMath.acos(1.5707963267948966) = NaN
+StrictMath.tan(1.5707963267948966) = 1.633123935319537E16
+StrictMath.tanh(1.5707963267948966) = 0.9171523356672744
+StrictMath.atan(1.5707963267948966) = 1.0038848218538872
+StrictMath.atan2(1.5707963267948966, 2.5707963267948966) = 0.5484797644174059
+StrictMath.sin(2.356194490192345) = 0.7071067811865476
+StrictMath.sinh(2.356194490192345) = 5.227971924677803
+StrictMath.asin(2.356194490192345) = NaN
+StrictMath.cos(2.356194490192345) = -0.7071067811865475
+StrictMath.cosh(2.356194490192345) = 5.322752149519959
+StrictMath.acos(2.356194490192345) = NaN
+StrictMath.tan(2.356194490192345) = -1.0000000000000002
+StrictMath.tanh(2.356194490192345) = 0.9821933800072388
+StrictMath.atan(2.356194490192345) = 1.1694228248157563
+StrictMath.atan2(2.356194490192345, 3.356194490192345) = 0.6120961173796371
+StrictMath.sin(3.141592653589793) = 1.2246467991473532E-16
+StrictMath.sinh(3.141592653589793) = 11.548739357257748
+StrictMath.asin(3.141592653589793) = NaN
+StrictMath.cos(3.141592653589793) = -1.0
+StrictMath.cosh(3.141592653589793) = 11.591953275521519
+StrictMath.acos(3.141592653589793) = NaN
+StrictMath.tan(3.141592653589793) = -1.2246467991473532E-16
+StrictMath.tanh(3.141592653589793) = 0.99627207622075
+StrictMath.atan(3.141592653589793) = 1.2626272556789115
+StrictMath.atan2(3.141592653589793, 4.141592653589793) = 0.6489487808147751
+StrictMath.sin(3.9269908169872414) = -0.7071067811865475
+StrictMath.sinh(3.9269908169872414) = 25.367158319374152
+StrictMath.asin(3.9269908169872414) = NaN
+StrictMath.cos(3.9269908169872414) = -0.7071067811865477
+StrictMath.cosh(3.9269908169872414) = 25.386861192360772
+StrictMath.acos(3.9269908169872414) = NaN
+StrictMath.tan(3.9269908169872414) = 0.9999999999999997
+StrictMath.tanh(3.9269908169872414) = 0.9992238948786412
+StrictMath.atan(3.9269908169872414) = 1.3214479677837223
+StrictMath.atan2(3.9269908169872414, 4.926990816987241) = 0.6729312291908799
+StrictMath.sin(4.71238898038469) = -1.0
+StrictMath.sinh(4.71238898038469) = 55.65439759941754
+StrictMath.asin(4.71238898038469) = NaN
+StrictMath.cos(4.71238898038469) = -1.8369701987210297E-16
+StrictMath.cosh(4.71238898038469) = 55.66338089043867
+StrictMath.acos(4.71238898038469) = NaN
+StrictMath.tan(4.71238898038469) = 5.443746451065123E15
+StrictMath.tanh(4.71238898038469) = 0.9998386139886326
+StrictMath.atan(4.71238898038469) = 1.3616916829711636
+StrictMath.atan2(4.71238898038469, 5.71238898038469) = 0.6897654692509959
+StrictMath.sin(5.497787143782138) = -0.7071067811865477
+StrictMath.sinh(5.497787143782138) = 122.07348351469281
+StrictMath.asin(5.497787143782138) = NaN
+StrictMath.cos(5.497787143782138) = 0.7071067811865474
+StrictMath.cosh(5.497787143782138) = 122.07757933958217
+StrictMath.acos(5.497787143782138) = NaN
+StrictMath.tan(5.497787143782138) = -1.0000000000000004
+StrictMath.tanh(5.497787143782138) = 0.9999664489997958
+StrictMath.atan(5.497787143782138) = 1.390871988014422
+StrictMath.atan2(5.497787143782138, 6.497787143782138) = 0.7022263981709682
+StrictMath.sin(6.283185307179586) = -2.4492935982947064E-16
+StrictMath.sinh(6.283185307179586) = 267.74489404101644
+StrictMath.asin(6.283185307179586) = NaN
+StrictMath.cos(6.283185307179586) = 1.0
+StrictMath.cosh(6.283185307179586) = 267.7467614837482
+StrictMath.acos(6.283185307179586) = NaN
+StrictMath.tan(6.283185307179586) = -2.4492935982947064E-16
+StrictMath.tanh(6.283185307179586) = 0.9999930253396107
+StrictMath.atan(6.283185307179586) = 1.4129651365067377
+StrictMath.atan2(6.283185307179586, 7.283185307179586) = 0.7118195495895945
+StrictMath.cbrt(-3.0) = -1.4422495703074083
+StrictMath.log(-3.0) = NaN
+StrictMath.log10(-3.0) = NaN
+StrictMath.log1p(-3.0) = NaN
+StrictMath.exp(-3.0) = 0.049787068367863944
+StrictMath.expm1(-3.0) = -0.950212931632136
+StrictMath.pow(-3.0, -2.0) = 0.1111111111111111
+StrictMath.hypot(-3.0, -2.0) = 3.605551275463989
+StrictMath.cbrt(-2.0) = -1.2599210498948732
+StrictMath.log(-2.0) = NaN
+StrictMath.log10(-2.0) = NaN
+StrictMath.log1p(-2.0) = NaN
+StrictMath.exp(-2.0) = 0.1353352832366127
+StrictMath.expm1(-2.0) = -0.8646647167633873
+StrictMath.pow(-2.0, -1.0) = -0.5
+StrictMath.hypot(-2.0, -1.0) = 2.23606797749979
+StrictMath.cbrt(-1.0) = -1.0
+StrictMath.log(-1.0) = NaN
+StrictMath.log10(-1.0) = NaN
+StrictMath.log1p(-1.0) = -Infinity
+StrictMath.exp(-1.0) = 0.36787944117144233
+StrictMath.expm1(-1.0) = -0.6321205588285577
+StrictMath.pow(-1.0, 0.0) = 1.0
+StrictMath.hypot(-1.0, 0.0) = 1.0
+StrictMath.cbrt(0.0) = 0.0
+StrictMath.log(0.0) = -Infinity
+StrictMath.log10(0.0) = -Infinity
+StrictMath.log1p(0.0) = 0.0
+StrictMath.exp(0.0) = 1.0
+StrictMath.expm1(0.0) = 0.0
+StrictMath.pow(0.0, 1.0) = 0.0
+StrictMath.hypot(0.0, 1.0) = 1.0
+StrictMath.cbrt(1.0) = 1.0
+StrictMath.log(1.0) = 0.0
+StrictMath.log10(1.0) = 0.0
+StrictMath.log1p(1.0) = 0.6931471805599453
+StrictMath.exp(1.0) = 2.7182818284590455
+StrictMath.expm1(1.0) = 1.718281828459045
+StrictMath.pow(1.0, 2.0) = 1.0
+StrictMath.hypot(1.0, 2.0) = 2.23606797749979
+StrictMath.cbrt(2.0) = 1.2599210498948732
+StrictMath.log(2.0) = 0.6931471805599453
+StrictMath.log10(2.0) = 0.3010299956639812
+StrictMath.log1p(2.0) = 1.0986122886681096
+StrictMath.exp(2.0) = 7.38905609893065
+StrictMath.expm1(2.0) = 6.38905609893065
+StrictMath.pow(2.0, 3.0) = 8.0
+StrictMath.hypot(2.0, 3.0) = 3.605551275463989
+StrictMath.cbrt(3.0) = 1.4422495703074083
+StrictMath.log(3.0) = 1.0986122886681096
+StrictMath.log10(3.0) = 0.47712125471966244
+StrictMath.log1p(3.0) = 1.3862943611198906
+StrictMath.exp(3.0) = 20.085536923187668
+StrictMath.expm1(3.0) = 19.085536923187668
+StrictMath.pow(3.0, 4.0) = 81.0
+StrictMath.hypot(3.0, 4.0) = 5.0
+StrictMath.ceil(0.0001) = 1.0
+StrictMath.floor(0.0001) = 0.0
+StrictMath.nextAfter(1.0, 2.0) = 1.0000000000000002
+StrictMath.rint(0.5000001) = 1.0
diff --git a/test/123-inline-execute2/info.txt b/test/123-inline-execute2/info.txt
new file mode 100644
index 0000000..4a728a7
--- /dev/null
+++ b/test/123-inline-execute2/info.txt
@@ -0,0 +1 @@
+Sanity checks for added InlineNative methods.
diff --git a/test/123-inline-execute2/src/Main.java b/test/123-inline-execute2/src/Main.java
new file mode 100644
index 0000000..9fadcfd
--- /dev/null
+++ b/test/123-inline-execute2/src/Main.java
@@ -0,0 +1,114 @@
+/*
+ * 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.util.Locale;
+
+public class Main {
+ public static void main(String args[]) {
+ for (int i = 0; i <= 360; i += 45) {
+ double d = i * (Math.PI / 180.0);
+ System.out.println("Math.sin(" + d + ") = "
+ + String.format(Locale.US, "%.12f", Math.sin(d)));
+
+ System.out.println("Math.sinh(" + d + ") = "
+ + String.format(Locale.US, "%.12f", Math.sinh(d)));
+ System.out.println("Math.asin(" + d + ") = "
+ + String.format(Locale.US, "%.12f", Math.asin(d)));
+ System.out.println("Math.cos(" + d + ") = "
+ + String.format(Locale.US, "%.12f", Math.cos(d)));
+ System.out.println("Math.cosh(" + d + ") = "
+ + String.format(Locale.US, "%.12f", Math.cosh(d)));
+ System.out.println("Math.acos(" + d + ") = "
+ + String.format(Locale.US, "%.12f", Math.acos(d)));
+ if ((i + 90) % 180 != 0) {
+ System.out.println("Math.tan(" + d + ") = "
+ + String.format(Locale.US, "%.12f", Math.tan(d)));
+ }
+ System.out.println("Math.tanh(" + d + ") = "
+ + String.format(Locale.US, "%.12f", Math.tanh(d)));
+ System.out.println("Math.atan(" + d + ") = "
+ + String.format(Locale.US, "%.12f", Math.atan(d)));
+ System.out.println("Math.atan2(" + d + ", " + (d + 1.0) + ") = "
+ + String.format(Locale.US, "%.12f", Math.atan2(d, d + 1.0)));
+ }
+
+ for (int j = -3; j <= 3; j++) {
+ double e = (double) j;
+ System.out.println("Math.cbrt(" + e + ") = "
+ + String.format(Locale.US, "%.12f", Math.cbrt(e)));
+ System.out.println("Math.log(" + e + ") = "
+ + String.format(Locale.US, "%.12f", Math.log(e)));
+ System.out.println("Math.log10(" + e + ") = "
+ + String.format(Locale.US, "%.12f", Math.log10(e)));
+ System.out.println("Math.log1p(" + e + ") = "
+ + String.format(Locale.US, "%.12f", Math.log1p(e)));
+ System.out.println("Math.exp(" + e + ") = "
+ + String.format(Locale.US, "%.12f", Math.exp(e)));
+ System.out.println("Math.expm1(" + e + ") = "
+ + String.format(Locale.US, "%.12f", Math.expm1(e)));
+ System.out.println("Math.pow(" + e + ", " + (e + 1.0) + ") = "
+ + String.format(Locale.US, "%.12f", Math.pow(e, e + 1.0)));
+ System.out.println("Math.hypot(" + e + ", " + (e + 1.0) + ") = "
+ + String.format(Locale.US, "%.12f", Math.hypot(e, e + 1.0)));
+ }
+
+ System.out.println("Math.ceil(0.0001) = "
+ + String.format(Locale.US, "%.12f", Math.ceil(0.0001)));
+ System.out.println("Math.floor(0.0001) = "
+ + String.format(Locale.US, "%.12f", Math.floor(0.0001)));
+ System.out.println("Math.nextAfter(1.0, 2.0) = "
+ + String.format(Locale.US, "%.12f", Math.nextAfter(1.0, 2.0)));
+ System.out.println("Math.nextAfter(2.0, 1.0) = "
+ + String.format(Locale.US, "%.12f", Math.nextAfter(2.0, 1.0)));
+ System.out.println("Math.rint(0.5000001) = "
+ + String.format(Locale.US, "%.12f", Math.rint(0.5000001)));
+
+ for (int i = 0; i <= 360; i += 45) {
+ double d = i * (StrictMath.PI / 180.0);
+ System.out.println("StrictMath.sin(" + d + ") = " + StrictMath.sin(d));
+ System.out.println("StrictMath.sinh(" + d + ") = " + StrictMath.sinh(d));
+ System.out.println("StrictMath.asin(" + d + ") = " + StrictMath.asin(d));
+ System.out.println("StrictMath.cos(" + d + ") = " + StrictMath.cos(d));
+ System.out.println("StrictMath.cosh(" + d + ") = " + StrictMath.cosh(d));
+ System.out.println("StrictMath.acos(" + d + ") = " + StrictMath.acos(d));
+ System.out.println("StrictMath.tan(" + d + ") = " + StrictMath.tan(d));
+ System.out.println("StrictMath.tanh(" + d + ") = " + StrictMath.tanh(d));
+ System.out.println("StrictMath.atan(" + d + ") = " + StrictMath.atan(d));
+ System.out.println("StrictMath.atan2(" + d + ", " + (d + 1.0) + ") = "
+ + StrictMath.atan2(d, d + 1.0));
+ }
+
+ for (int j = -3; j <= 3; j++) {
+ double e = (double) j;
+ System.out.println("StrictMath.cbrt(" + e + ") = " + StrictMath.cbrt(e));
+ System.out.println("StrictMath.log(" + e + ") = " + StrictMath.log(e));
+ System.out.println("StrictMath.log10(" + e + ") = " + StrictMath.log10(e));
+ System.out.println("StrictMath.log1p(" + e + ") = " + StrictMath.log1p(e));
+ System.out.println("StrictMath.exp(" + e + ") = " + StrictMath.exp(e));
+ System.out.println("StrictMath.expm1(" + e + ") = " + StrictMath.expm1(e));
+ System.out.println("StrictMath.pow(" + e + ", " + (e + 1.0) + ") = "
+ + StrictMath.pow(e, e + 1.0));
+ System.out.println("StrictMath.hypot(" + e + ", " + (e + 1.0) + ") = "
+ + StrictMath.hypot(e, e + 1.0));
+ }
+
+ System.out.println("StrictMath.ceil(0.0001) = " + StrictMath.ceil(0.0001));
+ System.out.println("StrictMath.floor(0.0001) = " + StrictMath.floor(0.0001));
+ System.out.println("StrictMath.nextAfter(1.0, 2.0) = " + StrictMath.nextAfter(1.0, 2.0));
+ System.out.println("StrictMath.rint(0.5000001) = " + StrictMath.rint(0.5000001));
+ }
+
+}
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index 17e88ce..98251e4 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -25,6 +25,9 @@
}
class TestClass {
+ static {
+ sTestClassObj = new TestClass(-1, -2);
+ }
TestClass() {
}
TestClass(int i, int j) {
@@ -37,6 +40,7 @@
TestClass next;
String str;
static int si;
+ static TestClass sTestClassObj;
}
class SubTestClass extends TestClass {
@@ -115,10 +119,11 @@
}
/// CHECK-START: int Main.test3(TestClass) load_store_elimination (before)
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldGet
- /// CHECK: InstanceFieldSet
/// CHECK: NewInstance
+ /// CHECK: StaticFieldGet
+ /// CHECK: NewInstance
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
/// CHECK: InstanceFieldSet
/// CHECK: InstanceFieldSet
/// CHECK: InstanceFieldGet
@@ -127,24 +132,31 @@
/// CHECK: InstanceFieldGet
/// CHECK-START: int Main.test3(TestClass) load_store_elimination (after)
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldGet
- /// CHECK: InstanceFieldSet
/// CHECK: NewInstance
- /// CHECK-NOT: InstanceFieldSet
+ /// CHECK: StaticFieldGet
+ /// CHECK: NewInstance
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
/// CHECK-NOT: InstanceFieldGet
+ /// CHECK-NOT: StaticFieldGet
- // A new allocation shouldn't alias with pre-existing values.
+ // A new allocation (even non-singleton) shouldn't alias with pre-existing values.
static int test3(TestClass obj) {
// Do an allocation here to avoid the HLoadClass and HClinitCheck
// at the second allocation.
new TestClass();
+ TestClass obj1 = TestClass.sTestClassObj;
+ TestClass obj2 = new TestClass(); // Cannot alias with obj or obj1 which pre-exist.
+ obj.next = obj2; // Make obj2 a non-singleton.
+ // All stores below need to stay since obj/obj1/obj2 are not singletons.
obj.i = 1;
- obj.next.j = 2;
- TestClass obj2 = new TestClass();
+ obj1.j = 2;
+ // Following stores won't kill values of obj.i and obj1.j.
obj2.i = 3;
obj2.j = 4;
- return obj.i + obj.next.j + obj2.i + obj2.j;
+ return obj.i + obj1.j + obj2.i + obj2.j;
}
/// CHECK-START: int Main.test4(TestClass, boolean) load_store_elimination (before)
diff --git a/test/543-checker-dce-trycatch/smali/TestCase.smali b/test/543-checker-dce-trycatch/smali/TestCase.smali
index 44e907d..1756fa4 100644
--- a/test/543-checker-dce-trycatch/smali/TestCase.smali
+++ b/test/543-checker-dce-trycatch/smali/TestCase.smali
@@ -202,27 +202,35 @@
# Test that DCE removes catch phi uses of instructions defined in dead try blocks.
## CHECK-START: int TestCase.testCatchPhiInputs_DefinedInTryBlock(int, int, int, int) dead_code_elimination_final (before)
-## CHECK-DAG: <<Arg0:i\d+>> ParameterValue
-## CHECK-DAG: <<Arg1:i\d+>> ParameterValue
-## CHECK-DAG: <<Const0xa:i\d+>> IntConstant 10
-## CHECK-DAG: <<Const0xb:i\d+>> IntConstant 11
-## CHECK-DAG: <<Const0xc:i\d+>> IntConstant 12
-## CHECK-DAG: <<Const0xd:i\d+>> IntConstant 13
-## CHECK-DAG: <<Const0xe:i\d+>> IntConstant 14
-## CHECK-DAG: <<Add:i\d+>> Add [<<Arg0>>,<<Arg1>>]
-## CHECK-DAG: Phi [<<Const0xa>>,<<Const0xb>>,<<Const0xd>>] reg:1 is_catch_phi:true
-## CHECK-DAG: Phi [<<Add>>,<<Const0xc>>,<<Const0xe>>] reg:2 is_catch_phi:true
+## CHECK-DAG: <<Arg0:i\d+>> ParameterValue
+## CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+## CHECK-DAG: <<Const0xa:i\d+>> IntConstant 10
+## CHECK-DAG: <<Const0xb:i\d+>> IntConstant 11
+## CHECK-DAG: <<Const0xc:i\d+>> IntConstant 12
+## CHECK-DAG: <<Const0xd:i\d+>> IntConstant 13
+## CHECK-DAG: <<Const0xe:i\d+>> IntConstant 14
+## CHECK-DAG: <<Const0xf:i\d+>> IntConstant 15
+## CHECK-DAG: <<Const0x10:i\d+>> IntConstant 16
+## CHECK-DAG: <<Const0x11:i\d+>> IntConstant 17
+## CHECK-DAG: <<Add:i\d+>> Add [<<Arg0>>,<<Arg1>>]
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Const0xf>>] reg:3 is_catch_phi:false
+## CHECK-DAG: Phi [<<Const0xa>>,<<Const0xb>>,<<Const0xd>>] reg:1 is_catch_phi:true
+## CHECK-DAG: Phi [<<Add>>,<<Const0xc>>,<<Const0xe>>] reg:2 is_catch_phi:true
+## CHECK-DAG: Phi [<<Phi>>,<<Const0x10>>,<<Const0x11>>] reg:3 is_catch_phi:true
## CHECK-START: int TestCase.testCatchPhiInputs_DefinedInTryBlock(int, int, int, int) dead_code_elimination_final (after)
-## CHECK-DAG: <<Const0xb:i\d+>> IntConstant 11
-## CHECK-DAG: <<Const0xc:i\d+>> IntConstant 12
-## CHECK-DAG: <<Const0xd:i\d+>> IntConstant 13
-## CHECK-DAG: <<Const0xe:i\d+>> IntConstant 14
-## CHECK-DAG: Phi [<<Const0xb>>,<<Const0xd>>] reg:1 is_catch_phi:true
-## CHECK-DAG: Phi [<<Const0xc>>,<<Const0xe>>] reg:2 is_catch_phi:true
+## CHECK-DAG: <<Const0xb:i\d+>> IntConstant 11
+## CHECK-DAG: <<Const0xc:i\d+>> IntConstant 12
+## CHECK-DAG: <<Const0xd:i\d+>> IntConstant 13
+## CHECK-DAG: <<Const0xe:i\d+>> IntConstant 14
+## CHECK-DAG: <<Const0x10:i\d+>> IntConstant 16
+## CHECK-DAG: <<Const0x11:i\d+>> IntConstant 17
+## CHECK-DAG: Phi [<<Const0xb>>,<<Const0xd>>] reg:1 is_catch_phi:true
+## CHECK-DAG: Phi [<<Const0xc>>,<<Const0xe>>] reg:2 is_catch_phi:true
+## CHECK-DAG: Phi [<<Const0x10>>,<<Const0x11>>] reg:3 is_catch_phi:true
.method public static testCatchPhiInputs_DefinedInTryBlock(IIII)I
- .registers 7
+ .registers 8
invoke-static {}, LTestCase;->$inline$False()Z
move-result v0
@@ -232,17 +240,24 @@
shr-int/2addr p2, p3
:try_start
- const v1, 0xa # dead catch phi input, defined in entry block
- add-int v2, p0, p1 # dead catch phi input, defined in the dead block
+ const v1, 0xa # dead catch phi input, defined in entry block (HInstruction)
+ add-int v2, p0, p1 # dead catch phi input, defined in the dead block (HInstruction)
+ move v3, v2
+ if-eqz v3, :define_phi
+ const v3, 0xf
+ :define_phi
+ # v3 = Phi [Add, 0xf] # dead catch phi input, defined in the dead block (HPhi)
div-int/2addr p0, v2
:else
const v1, 0xb # live catch phi input
const v2, 0xc # live catch phi input
+ const v3, 0x10 # live catch phi input
div-int/2addr p0, p3
const v1, 0xd # live catch phi input
const v2, 0xe # live catch phi input
+ const v3, 0x11 # live catch phi input
div-int/2addr p0, p1
:try_end
.catchall {:try_start .. :try_end} :catch_all
@@ -252,6 +267,7 @@
:catch_all
sub-int p0, v1, v2 # use catch phi values
+ sub-int p0, p0, v3 # use catch phi values
goto :return
.end method
@@ -260,8 +276,6 @@
# dead try blocks.
## CHECK-START: int TestCase.testCatchPhiInputs_DefinedOutsideTryBlock(int, int, int, int) dead_code_elimination_final (before)
-## CHECK-DAG: <<Arg0:i\d+>> ParameterValue
-## CHECK-DAG: <<Arg1:i\d+>> ParameterValue
## CHECK-DAG: <<Const0xa:i\d+>> IntConstant 10
## CHECK-DAG: <<Const0xb:i\d+>> IntConstant 11
## CHECK-DAG: <<Const0xc:i\d+>> IntConstant 12
diff --git a/test/554-jit-profile-file/expected.txt b/test/554-jit-profile-file/expected.txt
new file mode 100644
index 0000000..cde211e
--- /dev/null
+++ b/test/554-jit-profile-file/expected.txt
@@ -0,0 +1,7 @@
+JNI_OnLoad called
+ProfileInfo:
+:classes.dex
+ java.lang.String Main.hotMethod()
+ void Main.main(java.lang.String[])
+:classes2.dex
+ java.lang.String OtherDex.hotMethod()
diff --git a/test/554-jit-profile-file/info.txt b/test/554-jit-profile-file/info.txt
new file mode 100644
index 0000000..b1bfe81
--- /dev/null
+++ b/test/554-jit-profile-file/info.txt
@@ -0,0 +1 @@
+Check that saving and restoring profile files works correctly in a JIT environment.
diff --git a/test/554-jit-profile-file/offline_profile.cc b/test/554-jit-profile-file/offline_profile.cc
new file mode 100644
index 0000000..75e441f
--- /dev/null
+++ b/test/554-jit-profile-file/offline_profile.cc
@@ -0,0 +1,59 @@
+/*
+ * 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 "dex_file.h"
+
+#include "jit/offline_profiling_info.h"
+#include "jni.h"
+#include "mirror/class-inl.h"
+#include "oat_file_assistant.h"
+#include "oat_file_manager.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+
+namespace art {
+namespace {
+
+extern "C" JNIEXPORT jstring JNICALL Java_Main_getProfileInfoDump(
+ JNIEnv* env, jclass cls, jstring filename) {
+ std::string dex_location;
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ dex_location = soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetDexFile()->GetLocation();
+ }
+ const OatFile* oat_file = Runtime::Current()->GetOatFileManager().GetPrimaryOatFile();
+ std::vector<std::unique_ptr<const DexFile>> dex_files =
+ OatFileAssistant::LoadDexFiles(*oat_file, dex_location.c_str());
+ const char* filename_chars = env->GetStringUTFChars(filename, nullptr);
+
+ std::vector<const DexFile*> dex_files_raw;
+ for (size_t i = 0; i < dex_files.size(); i++) {
+ dex_files_raw.push_back(dex_files[i].get());
+ }
+
+ ProfileCompilationInfo info(filename_chars);
+
+ std::string result = info.Load(dex_files_raw)
+ ? info.DumpInfo(/*print_full_dex_location*/false)
+ : "Could not load profile info";
+
+ env->ReleaseStringUTFChars(filename, filename_chars);
+ // Return the dump of the profile info. It will be compared against a golden value.
+ return env->NewStringUTF(result.c_str());
+}
+
+} // namespace
+} // namespace art
diff --git a/test/554-jit-profile-file/run b/test/554-jit-profile-file/run
new file mode 100644
index 0000000..08dcb38
--- /dev/null
+++ b/test/554-jit-profile-file/run
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+exec ${RUN} \
+ -Xcompiler-option --compiler-filter=interpret-only \
+ --runtime-option -Xjitsaveprofilinginfo \
+ --runtime-option -Xusejit:true \
+ --runtime-option -Xjitthreshold:100 \
+ "${@}"
diff --git a/test/554-jit-profile-file/src-multidex/OtherDex.java b/test/554-jit-profile-file/src-multidex/OtherDex.java
new file mode 100644
index 0000000..51644db
--- /dev/null
+++ b/test/554-jit-profile-file/src-multidex/OtherDex.java
@@ -0,0 +1,31 @@
+/*
+ * 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.util.HashMap;
+
+public class OtherDex {
+ public void coldMethod() {
+ hotMethod();
+ }
+
+ public String hotMethod() {
+ HashMap<String, String> map = new HashMap<String, String>();
+ for (int i = 0; i < 10; i++) {
+ map.put("" + i, "" + i + 1);
+ }
+ return map.get("1");
+ }
+}
diff --git a/test/554-jit-profile-file/src/Main.java b/test/554-jit-profile-file/src/Main.java
new file mode 100644
index 0000000..b581a67
--- /dev/null
+++ b/test/554-jit-profile-file/src/Main.java
@@ -0,0 +1,143 @@
+/*
+ * 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.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+public class Main {
+
+ public void coldMethod() {
+ hotMethod();
+ }
+
+ public String hotMethod() {
+ HashMap<String, String> map = new HashMap<String, String>();
+ for (int i = 0; i < 10; i++) {
+ map.put("" + i, "" + i + 1);
+ }
+ return map.get("1");
+ }
+
+ private static final String PKG_NAME = "test.package";
+ private static final String APP_DIR_PREFIX = "app_dir_";
+ private static final String CODE_CACHE = "code_cache";
+ private static final String PROFILE_FILE = PKG_NAME + ".prof";
+ private static final String TEMP_FILE_NAME_PREFIX = "dummy";
+ private static final String TEMP_FILE_NAME_SUFFIX = "-file";
+ private static final int JIT_INVOCATION_COUNT = 101;
+
+ /* needs to match Runtime:: kProfileBackground */
+ private static final int PROFILE_BACKGROUND = 1;
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
+ File file = null;
+ File appDir = null;
+ File profileDir = null;
+ File profileFile = null;
+ try {
+ // We don't know where we have rights to create the code_cache. So create
+ // a dummy temporary file and get its parent directory. That will serve as
+ // the app directory.
+ file = createTempFile();
+ appDir = new File(file.getParent(), APP_DIR_PREFIX + file.getName());
+ appDir.mkdir();
+ profileDir = new File(appDir, CODE_CACHE);
+ profileDir.mkdir();
+
+ // Registering the app info will set the profile file name.
+ VMRuntime.registerAppInfo(PKG_NAME, appDir.getPath());
+
+ // Make sure the hot methods are jitted.
+ Main m = new Main();
+ OtherDex o = new OtherDex();
+ for (int i = 0; i < JIT_INVOCATION_COUNT; i++) {
+ m.hotMethod();
+ o.hotMethod();
+ }
+
+ // Updating the process state to BACKGROUND will trigger profile saving.
+ VMRuntime.updateProcessState(PROFILE_BACKGROUND);
+
+ // Check that the profile file exists.
+ profileFile = new File(profileDir, PROFILE_FILE);
+ if (!profileFile.exists()) {
+ throw new RuntimeException("No profile file found");
+ }
+ // Dump the profile file.
+ // We know what methods are hot and we compare with the golden `expected` output.
+ System.out.println(getProfileInfoDump(profileFile.getPath()));
+ } finally {
+ if (file != null) {
+ file.delete();
+ }
+ if (profileFile != null) {
+ profileFile.delete();
+ }
+ if (profileDir != null) {
+ profileDir.delete();
+ }
+ if (appDir != null) {
+ appDir.delete();
+ }
+ }
+ }
+
+ private static class VMRuntime {
+ private static final Method registerAppInfoMethod;
+ private static final Method updateProcessStateMethod;
+ private static final Method getRuntimeMethod;
+ static {
+ try {
+ Class c = Class.forName("dalvik.system.VMRuntime");
+ registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo",
+ String.class, String.class, String.class);
+ updateProcessStateMethod = c.getDeclaredMethod("updateProcessState", Integer.TYPE);
+ getRuntimeMethod = c.getDeclaredMethod("getRuntime");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void registerAppInfo(String pkgName, String appDir) throws Exception {
+ registerAppInfoMethod.invoke(null, pkgName, appDir, null);
+ }
+ public static void updateProcessState(int state) throws Exception {
+ Object runtime = getRuntimeMethod.invoke(null);
+ updateProcessStateMethod.invoke(runtime, state);
+ }
+ }
+
+ static native String getProfileInfoDump(
+ String filename);
+
+ private static File createTempFile() throws Exception {
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ }
+ }
+ }
+}
diff --git a/test/556-invoke-super/expected.txt b/test/556-invoke-super/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/556-invoke-super/expected.txt
diff --git a/test/556-invoke-super/info.txt b/test/556-invoke-super/info.txt
new file mode 100644
index 0000000..7de2a4f
--- /dev/null
+++ b/test/556-invoke-super/info.txt
@@ -0,0 +1 @@
+Tests the invoke-super opcode with multidex.
diff --git a/test/556-invoke-super/smali/invokesuper.smali b/test/556-invoke-super/smali/invokesuper.smali
new file mode 100644
index 0000000..ef55000
--- /dev/null
+++ b/test/556-invoke-super/smali/invokesuper.smali
@@ -0,0 +1,40 @@
+#
+# 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.
+
+
+.class public LInvokeSuper;
+.super LSuperClass;
+
+.method public constructor <init>()V
+.registers 1
+ invoke-direct {v0}, LSuperClass;-><init>()V
+ return-void
+.end method
+
+
+.method public run()I
+.registers 2
+ # Do an invoke super on this class, to confuse runtime/compiler.
+ invoke-super {p0}, LInvokeSuper;->returnInt()I
+ move-result v0
+ return v0
+.end method
+
+
+.method public returnInt()I
+.registers 2
+ const v0, 777
+ return v0
+.end method
diff --git a/test/556-invoke-super/src-multidex/SuperClass.java b/test/556-invoke-super/src-multidex/SuperClass.java
new file mode 100644
index 0000000..faf16c4
--- /dev/null
+++ b/test/556-invoke-super/src-multidex/SuperClass.java
@@ -0,0 +1,21 @@
+/*
+ * 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 SuperClass {
+ public int returnInt() {
+ return 42;
+ }
+}
diff --git a/test/556-invoke-super/src/Main.java b/test/556-invoke-super/src/Main.java
new file mode 100644
index 0000000..07289f7
--- /dev/null
+++ b/test/556-invoke-super/src/Main.java
@@ -0,0 +1,35 @@
+/*
+ * 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.Method;
+
+public class Main {
+
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ static void assertEquals(int expected, int value) {
+ if (expected != value) {
+ throw new Error("Expected " + expected + ", got " + value);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("InvokeSuper");
+ Method m = c.getMethod("run");
+ assertEquals(42, ((Integer)m.invoke(c.newInstance(), new Object[0])).intValue());
+ }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index f74a516..f84dfe6 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -38,7 +38,8 @@
461-get-reference-vreg/get_reference_vreg_jni.cc \
466-get-live-vreg/get_live_vreg_jni.cc \
497-inlining-and-class-loader/clear_dex_cache.cc \
- 543-env-long-ref/env_long_ref.cc
+ 543-env-long-ref/env_long_ref.cc \
+ 554-jit-profile-file/offline_profile.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
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 0925d36..72b0dcb 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -276,7 +276,8 @@
TEST_ART_BROKEN_NO_RELOCATE_TESTS := \
117-nopatchoat \
118-noimage-dex2oat \
- 119-noimage-patchoat
+ 119-noimage-patchoat \
+ 554-jit-profile-file
ifneq (,$(filter no-relocate,$(RELOCATE_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -298,6 +299,7 @@
412-new-array \
471-uninitialized-locals \
506-verify-aput \
+ 554-jit-profile-file \
800-smali
ifneq (,$(filter interp-ac,$(COMPILER_TYPES)))
@@ -356,13 +358,15 @@
# All these tests check that we have sane behavior if we don't have a patchoat or dex2oat.
# Therefore we shouldn't run them in situations where we actually don't have these since they
# explicitly test for them. These all also assume we have an image.
+# 554-jit-profile-file is disabled because it needs a primary oat file to know what it should save.
TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \
116-nodex2oat \
117-nopatchoat \
118-noimage-dex2oat \
119-noimage-patchoat \
137-cfi \
- 138-duplicate-classes-check2
+ 138-duplicate-classes-check2 \
+ 554-jit-profile-file
# This test fails without an image.
TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \
@@ -413,7 +417,8 @@
# Known broken tests for the interpreter.
# CFI unwinding expects managed frames.
TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := \
- 137-cfi
+ 137-cfi \
+ 554-jit-profile-file
ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -520,7 +525,8 @@
# Tests that should fail in the heap poisoning configuration with the interpreter.
# 137: Cannot run this with the interpreter.
TEST_ART_BROKEN_INTERPRETER_HEAP_POISONING_RUN_TESTS := \
- 137-cfi
+ 137-cfi \
+ 554-jit-profile-file
ifeq ($(ART_HEAP_POISONING),true)
ifneq (,$(filter default,$(COMPILER_TYPES)))
diff --git a/test/dexdump/bytecodes.txt b/test/dexdump/bytecodes.txt
index d14c47c..4c8b79b 100755
--- a/test/dexdump/bytecodes.txt
+++ b/test/dexdump/bytecodes.txt
@@ -196,6 +196,7 @@
name : 'icon'
type : 'I'
access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 2130837504
Instance fields -
Direct methods -
#0 : (in Lcom/google/android/test/R$drawable;)
diff --git a/test/dexdump/bytecodes.xml b/test/dexdump/bytecodes.xml
index 0581677..8e54dd3 100755
--- a/test/dexdump/bytecodes.xml
+++ b/test/dexdump/bytecodes.xml
@@ -97,6 +97,7 @@
static="true"
final="true"
visibility="public"
+ value="2130837504"
>
</field>
<constructor name="R.drawable"
diff --git a/test/dexdump/staticfields.dex b/test/dexdump/staticfields.dex
new file mode 100644
index 0000000..a07c46e
--- /dev/null
+++ b/test/dexdump/staticfields.dex
Binary files differ
diff --git a/test/dexdump/staticfields.lst b/test/dexdump/staticfields.lst
new file mode 100644
index 0000000..5375b8e
--- /dev/null
+++ b/test/dexdump/staticfields.lst
@@ -0,0 +1,2 @@
+#staticfields.dex
+0x000001bc 8 StaticFields <init> ()V StaticFields.java 24
diff --git a/test/dexdump/staticfields.txt b/test/dexdump/staticfields.txt
new file mode 100644
index 0000000..022605f
--- /dev/null
+++ b/test/dexdump/staticfields.txt
@@ -0,0 +1,126 @@
+Processing 'staticfields.dex'...
+Opened 'staticfields.dex', DEX version '035'
+DEX file header:
+magic : 'dex\n035\0'
+checksum : 52d4fc6d
+signature : 6e82...2f27
+file_size : 1264
+header_size : 112
+link_size : 0
+link_off : 0 (0x000000)
+string_ids_size : 28
+string_ids_off : 112 (0x000070)
+type_ids_size : 12
+type_ids_off : 224 (0x0000e0)
+proto_ids_size : 1
+proto_ids_off : 272 (0x000110)
+field_ids_size : 12
+field_ids_off : 284 (0x00011c)
+method_ids_size : 2
+method_ids_off : 380 (0x00017c)
+class_defs_size : 1
+class_defs_off : 396 (0x00018c)
+data_size : 836
+data_off : 428 (0x0001ac)
+
+Class #0 header:
+class_idx : 6
+access_flags : 1 (0x0001)
+superclass_idx : 7
+interfaces_off : 0 (0x000000)
+source_file_idx : 11
+annotations_off : 0 (0x000000)
+class_data_off : 1067 (0x00042b)
+static_fields_size : 12
+instance_fields_size: 0
+direct_methods_size : 1
+virtual_methods_size: 0
+
+Class #0 -
+ Class descriptor : 'LStaticFields;'
+ Access flags : 0x0001 (PUBLIC)
+ Superclass : 'Ljava/lang/Object;'
+ Interfaces -
+ Static fields -
+ #0 : (in LStaticFields;)
+ name : 'test00_public_static_final_byte_42'
+ type : 'B'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 42
+ #1 : (in LStaticFields;)
+ name : 'test01_public_static_final_short_43'
+ type : 'S'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 43
+ #2 : (in LStaticFields;)
+ name : 'test02_public_static_final_char_X'
+ type : 'C'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 88
+ #3 : (in LStaticFields;)
+ name : 'test03_public_static_final_int_44'
+ type : 'I'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 44
+ #4 : (in LStaticFields;)
+ name : 'test04_public_static_final_long_45'
+ type : 'J'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 45
+ #5 : (in LStaticFields;)
+ name : 'test05_public_static_final_float_46_47'
+ type : 'F'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 46.470001
+ #6 : (in LStaticFields;)
+ name : 'test06_public_static_final_double_48_49'
+ type : 'D'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 48.490000
+ #7 : (in LStaticFields;)
+ name : 'test07_public_static_final_string'
+ type : 'Ljava/lang/String;'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : "abc \\><\"'&\t\r\n"
+ #8 : (in LStaticFields;)
+ name : 'test08_public_static_final_object_null'
+ type : 'Ljava/lang/Object;'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : null
+ #9 : (in LStaticFields;)
+ name : 'test09_public_static_final_boolean_true'
+ type : 'Z'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : true
+ #10 : (in LStaticFields;)
+ name : 'test10_private_static_final_int_50'
+ type : 'I'
+ access : 0x001a (PRIVATE STATIC FINAL)
+ value : 50
+ #11 : (in LStaticFields;)
+ name : 'test99_empty_value'
+ type : 'I'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ Instance fields -
+ Direct methods -
+ #0 : (in LStaticFields;)
+ name : '<init>'
+ type : '()V'
+ access : 0x10001 (PUBLIC CONSTRUCTOR)
+ code -
+ registers : 1
+ ins : 1
+ outs : 1
+ insns size : 4 16-bit code units
+0001ac: |[0001ac] StaticFields.<init>:()V
+0001bc: 7010 0100 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0001
+0001c2: 0e00 |0003: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=24
+ locals :
+ 0x0000 - 0x0004 reg=0 this LStaticFields;
+
+ Virtual methods -
+ source_file_idx : 11 (StaticFields.java)
+
diff --git a/test/dexdump/staticfields.xml b/test/dexdump/staticfields.xml
new file mode 100644
index 0000000..6cff71b
--- /dev/null
+++ b/test/dexdump/staticfields.xml
@@ -0,0 +1,129 @@
+<api>
+<package name=""
+>
+<class name="StaticFields"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ visibility="public"
+>
+<field name="test00_public_static_final_byte_42"
+ type="byte"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="42"
+>
+</field>
+<field name="test01_public_static_final_short_43"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="43"
+>
+</field>
+<field name="test02_public_static_final_char_X"
+ type="char"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="88"
+>
+</field>
+<field name="test03_public_static_final_int_44"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="44"
+>
+</field>
+<field name="test04_public_static_final_long_45"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="45"
+>
+</field>
+<field name="test05_public_static_final_float_46_47"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="46.470001"
+>
+</field>
+<field name="test06_public_static_final_double_48_49"
+ type="double"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="48.490000"
+>
+</field>
+<field name="test07_public_static_final_string"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="abc \><"'&	
"
+>
+</field>
+<field name="test08_public_static_final_object_null"
+ type="java.lang.Object"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="null"
+>
+</field>
+<field name="test09_public_static_final_boolean_true"
+ type="boolean"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="true"
+>
+</field>
+<field name="test99_empty_value"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+>
+</field>
+<constructor name="StaticFields"
+ type="StaticFields"
+ static="false"
+ final="false"
+ visibility="public"
+>
+</constructor>
+</class>
+</package>
+</api>