summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/common_compiler_test.h12
-rw-r--r--compiler/compiler.h2
-rw-r--r--compiler/dex/mir_field_info.h4
-rw-r--r--compiler/dex/mir_graph.h12
-rw-r--r--compiler/dex/mir_method_info.h2
-rw-r--r--compiler/dex/mir_optimization.cc3
-rw-r--r--compiler/dex/mir_optimization_test.cc11
-rw-r--r--compiler/dex/quick/dex_file_method_inliner.cc9
-rw-r--r--compiler/dex/quick/dex_file_method_inliner.h23
-rw-r--r--compiler/dex/quick/quick_cfi_test.cc6
-rw-r--r--compiler/dex/quick/quick_compiler.h2
-rw-r--r--compiler/dex/quick/x86/quick_assemble_x86_test.cc2
-rw-r--r--compiler/dex/quick_compiler_callbacks.h2
-rw-r--r--compiler/dex/verification_results.h12
-rw-r--r--compiler/dex/verified_method.h8
-rw-r--r--compiler/driver/compiler_driver.cc1050
-rw-r--r--compiler/driver/compiler_driver.h135
-rw-r--r--compiler/driver/compiler_driver_test.cc4
-rw-r--r--compiler/driver/compiler_options.cc6
-rw-r--r--compiler/driver/compiler_options.h14
-rw-r--r--compiler/dwarf/dwarf_test.cc4
-rw-r--r--compiler/elf_writer.h2
-rw-r--r--compiler/elf_writer_quick.h4
-rw-r--r--compiler/image_writer.cc81
-rw-r--r--compiler/image_writer.h97
-rw-r--r--compiler/jit/jit_compiler.cc4
-rw-r--r--compiler/jit/jit_compiler.h6
-rw-r--r--compiler/jni/jni_cfi_test.cc4
-rw-r--r--compiler/oat_test.cc11
-rw-r--r--compiler/oat_writer.cc26
-rw-r--r--compiler/oat_writer.h4
-rw-r--r--compiler/optimizing/builder.cc6
-rw-r--r--compiler/optimizing/code_generator_arm.cc16
-rw-r--r--compiler/optimizing/code_generator_arm64.cc16
-rw-r--r--compiler/optimizing/code_generator_mips64.cc15
-rw-r--r--compiler/optimizing/code_generator_x86.cc46
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc77
-rw-r--r--compiler/optimizing/graph_checker.cc102
-rw-r--r--compiler/optimizing/graph_checker_test.cc4
-rw-r--r--compiler/optimizing/graph_visualizer.cc29
-rw-r--r--compiler/optimizing/inliner.cc50
-rw-r--r--compiler/optimizing/instruction_simplifier.cc1
-rw-r--r--compiler/optimizing/intrinsics.cc12
-rw-r--r--compiler/optimizing/intrinsics_arm.cc42
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc27
-rw-r--r--compiler/optimizing/intrinsics_list.h2
-rw-r--r--compiler/optimizing/intrinsics_x86.cc2
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc10
-rw-r--r--compiler/optimizing/licm_test.cc195
-rw-r--r--compiler/optimizing/nodes.cc26
-rw-r--r--compiler/optimizing/nodes.h64
-rw-r--r--compiler/optimizing/optimizing_cfi_test.cc4
-rw-r--r--compiler/optimizing/optimizing_compiler.cc91
-rw-r--r--compiler/optimizing/parallel_move_resolver.cc14
-rw-r--r--compiler/optimizing/reference_type_propagation.cc14
-rw-r--r--compiler/optimizing/reference_type_propagation.h6
-rw-r--r--compiler/optimizing/ssa_builder.cc4
-rw-r--r--compiler/optimizing/ssa_liveness_analysis.cc2
-rw-r--r--compiler/optimizing/ssa_liveness_analysis.h8
-rw-r--r--compiler/trampolines/trampoline_compiler.h4
-rw-r--r--compiler/utils/arm/assembler_thumb2.cc29
-rw-r--r--compiler/utils/arm/assembler_thumb2.h4
-rw-r--r--compiler/utils/arm/assembler_thumb2_test.cc69
-rw-r--r--compiler/utils/assembler_thumb_test.cc6
-rw-r--r--compiler/utils/swap_space.cc1
-rw-r--r--compiler/utils/swap_space.h6
-rw-r--r--compiler/utils/x86/assembler_x86.cc45
-rw-r--r--compiler/utils/x86/assembler_x86.h5
-rw-r--r--compiler/utils/x86/assembler_x86_test.cc22
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.cc51
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.h7
-rw-r--r--compiler/utils/x86_64/assembler_x86_64_test.cc60
72 files changed, 1886 insertions, 870 deletions
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index d215662645..dc2bc5c3f4 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -46,12 +46,12 @@ class CommonCompilerTest : public CommonRuntimeTest {
// Create an OatMethod based on pointers (for unit tests).
OatFile::OatMethod CreateOatMethod(const void* code);
- void MakeExecutable(ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void MakeExecutable(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
static void MakeExecutable(const void* code_start, size_t code_length);
void MakeExecutable(mirror::ClassLoader* class_loader, const char* class_name)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
protected:
virtual void SetUp();
@@ -76,17 +76,17 @@ class CommonCompilerTest : public CommonRuntimeTest {
virtual void TearDown();
void CompileClass(mirror::ClassLoader* class_loader, const char* class_name)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
- void CompileMethod(ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void CompileMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
void CompileDirectMethod(Handle<mirror::ClassLoader> class_loader, const char* class_name,
const char* method_name, const char* signature)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
void CompileVirtualMethod(Handle<mirror::ClassLoader> class_loader, const char* class_name,
const char* method_name, const char* signature)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
void ReserveImageSpace();
diff --git a/compiler/compiler.h b/compiler/compiler.h
index e5d1aff08c..fcd3434e68 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -58,7 +58,7 @@ class Compiler {
const DexFile& dex_file) const = 0;
virtual uintptr_t GetEntryPointOf(ArtMethod* method) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
+ SHARED_REQUIRES(Locks::mutator_lock_) = 0;
uint64_t GetMaximumCompilationTimeBeforeWarning() const {
return maximum_compilation_time_before_warning_;
diff --git a/compiler/dex/mir_field_info.h b/compiler/dex/mir_field_info.h
index e4570fd8d3..04c58aca6b 100644
--- a/compiler/dex/mir_field_info.h
+++ b/compiler/dex/mir_field_info.h
@@ -135,7 +135,7 @@ class MirIFieldLoweringInfo : public MirFieldInfo {
// with IGET/IPUT. For fast path fields, retrieve the field offset.
static void Resolve(CompilerDriver* compiler_driver, const DexCompilationUnit* mUnit,
MirIFieldLoweringInfo* field_infos, size_t count)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_);
// Construct an unresolved instance field lowering info.
explicit MirIFieldLoweringInfo(uint16_t field_idx, DexMemAccessType type, bool is_quickened)
@@ -192,7 +192,7 @@ class MirSFieldLoweringInfo : public MirFieldInfo {
// and the type index of the declaring class in the compiled method's dex file.
static void Resolve(CompilerDriver* compiler_driver, const DexCompilationUnit* mUnit,
MirSFieldLoweringInfo* field_infos, size_t count)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_);
// Construct an unresolved static field lowering info.
explicit MirSFieldLoweringInfo(uint16_t field_idx, DexMemAccessType type)
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index dbe906280f..23b7c4292b 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -173,7 +173,17 @@ enum OatMethodAttributes {
typedef uint16_t BasicBlockId;
static const BasicBlockId NullBasicBlockId = 0;
-static constexpr bool kLeafOptimization = false;
+
+// Leaf optimization is basically the removal of suspend checks from leaf methods.
+// This is incompatible with SuspendCheckElimination (SCE) which eliminates suspend
+// checks from loops that call any non-intrinsic method, since a loop that calls
+// only a leaf method would end up without any suspend checks at all. So turning
+// this on automatically disables the SCE in MIRGraph::EliminateSuspendChecksGate().
+//
+// Since the Optimizing compiler is actually applying the same optimization, Quick
+// must not run SCE anyway, so we enable this optimization as a way to disable SCE
+// while keeping a consistent behavior across the backends, b/22657404.
+static constexpr bool kLeafOptimization = true;
/*
* In general, vreg/sreg describe Dalvik registers that originated with dx. However,
diff --git a/compiler/dex/mir_method_info.h b/compiler/dex/mir_method_info.h
index 946c74becf..4512f35a99 100644
--- a/compiler/dex/mir_method_info.h
+++ b/compiler/dex/mir_method_info.h
@@ -99,7 +99,7 @@ class MirMethodLoweringInfo : public MirMethodInfo {
// path methods, retrieve the method's vtable index and direct code and method when applicable.
static void Resolve(CompilerDriver* compiler_driver, const DexCompilationUnit* mUnit,
MirMethodLoweringInfo* method_infos, size_t count)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_);
MirMethodLoweringInfo(uint16_t method_idx, InvokeType type, bool is_quickened)
: MirMethodInfo(method_idx,
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 5bb0ce3ba5..80b7ac1e5b 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -1724,7 +1724,8 @@ void MIRGraph::StringChange() {
bool MIRGraph::EliminateSuspendChecksGate() {
- if ((cu_->disable_opt & (1 << kSuspendCheckElimination)) != 0 || // Disabled.
+ if (kLeafOptimization || // Incompatible (could create loops without suspend checks).
+ (cu_->disable_opt & (1 << kSuspendCheckElimination)) != 0 || // Disabled.
GetMaxNestedLoops() == 0u || // Nothing to do.
GetMaxNestedLoops() >= 32u || // Only 32 bits in suspend_checks_in_loops_[.].
// Exclude 32 as well to keep bit shifts well-defined.
diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc
index 10a4337cf5..47123ba28c 100644
--- a/compiler/dex/mir_optimization_test.cc
+++ b/compiler/dex/mir_optimization_test.cc
@@ -467,8 +467,17 @@ class SuspendCheckEliminationTest : public MirOptimizationTest {
cu_.mir_graph->ComputeDominators();
cu_.mir_graph->ComputeTopologicalSortOrder();
cu_.mir_graph->SSATransformationEnd();
+
bool gate_result = cu_.mir_graph->EliminateSuspendChecksGate();
- ASSERT_TRUE(gate_result);
+ ASSERT_NE(gate_result, kLeafOptimization);
+ if (kLeafOptimization) {
+ // Even with kLeafOptimization on and Gate() refusing to allow SCE, we want
+ // to run the SCE test to avoid bitrot, so we need to initialize explicitly.
+ cu_.mir_graph->suspend_checks_in_loops_ =
+ cu_.mir_graph->arena_->AllocArray<uint32_t>(cu_.mir_graph->GetNumBlocks(),
+ kArenaAllocMisc);
+ }
+
TopologicalSortIterator iterator(cu_.mir_graph.get());
bool change = false;
for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index 2568ee3064..7fc6fa29c7 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -38,6 +38,7 @@ static constexpr bool kIntrinsicIsStatic[] = {
true, // kIntrinsicFloatCvt
true, // kIntrinsicReverseBits
true, // kIntrinsicReverseBytes
+ true, // kIntrinsicNumberOfLeadingZeros
true, // kIntrinsicAbsInt
true, // kIntrinsicAbsLong
true, // kIntrinsicAbsFloat
@@ -75,6 +76,8 @@ static_assert(kIntrinsicIsStatic[kIntrinsicDoubleCvt], "DoubleCvt must be static
static_assert(kIntrinsicIsStatic[kIntrinsicFloatCvt], "FloatCvt must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicReverseBits], "ReverseBits must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicReverseBytes], "ReverseBytes must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicNumberOfLeadingZeros],
+ "NumberOfLeadingZeros must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicAbsInt], "AbsInt must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicAbsLong], "AbsLong must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicAbsFloat], "AbsFloat must be static");
@@ -225,6 +228,7 @@ const char* const DexFileMethodInliner::kNameCacheNames[] = {
"putObjectVolatile", // kNameCachePutObjectVolatile
"putOrderedObject", // kNameCachePutOrderedObject
"arraycopy", // kNameCacheArrayCopy
+ "numberOfLeadingZeros", // kNameCacheNumberOfLeadingZeros
};
const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = {
@@ -368,6 +372,9 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods
INTRINSIC(JavaLangInteger, Reverse, I_I, kIntrinsicReverseBits, k32),
INTRINSIC(JavaLangLong, Reverse, J_J, kIntrinsicReverseBits, k64),
+ INTRINSIC(JavaLangInteger, NumberOfLeadingZeros, I_I, kIntrinsicNumberOfLeadingZeros, k32),
+ INTRINSIC(JavaLangLong, NumberOfLeadingZeros, J_I, kIntrinsicNumberOfLeadingZeros, k64),
+
INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0),
INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0),
INTRINSIC(JavaLangMath, Abs, J_J, kIntrinsicAbsLong, 0),
@@ -614,6 +621,8 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) {
intrinsic.d.data & kIntrinsicFlagIsOrdered);
case kIntrinsicSystemArrayCopyCharArray:
return backend->GenInlinedArrayCopyCharArray(info);
+ case kIntrinsicNumberOfLeadingZeros:
+ return false; // not implemented in quick
default:
LOG(FATAL) << "Unexpected intrinsic opcode: " << intrinsic.opcode;
return false; // avoid warning "control reaches end of non-void function"
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index 26b41bf54d..bcb9ee5b53 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -62,49 +62,49 @@ class DexFileMethodInliner {
* @return true if the method is a candidate for inlining, false otherwise.
*/
bool AnalyseMethodCode(verifier::MethodVerifier* verifier)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_);
/**
* Check whether a particular method index corresponds to an intrinsic or special function.
*/
- InlineMethodFlags IsIntrinsicOrSpecial(uint32_t method_index) LOCKS_EXCLUDED(lock_);
+ InlineMethodFlags IsIntrinsicOrSpecial(uint32_t method_index) REQUIRES(!lock_);
/**
* Check whether a particular method index corresponds to an intrinsic function.
*/
- bool IsIntrinsic(uint32_t method_index, InlineMethod* intrinsic) LOCKS_EXCLUDED(lock_);
+ bool IsIntrinsic(uint32_t method_index, InlineMethod* intrinsic) REQUIRES(!lock_);
/**
* Generate code for an intrinsic function invocation.
*/
- bool GenIntrinsic(Mir2Lir* backend, CallInfo* info) LOCKS_EXCLUDED(lock_);
+ bool GenIntrinsic(Mir2Lir* backend, CallInfo* info) REQUIRES(!lock_);
/**
* Check whether a particular method index corresponds to a special function.
*/
- bool IsSpecial(uint32_t method_index) LOCKS_EXCLUDED(lock_);
+ bool IsSpecial(uint32_t method_index) REQUIRES(!lock_);
/**
* Generate code for a special function.
*/
- bool GenSpecial(Mir2Lir* backend, uint32_t method_idx) LOCKS_EXCLUDED(lock_);
+ bool GenSpecial(Mir2Lir* backend, uint32_t method_idx) REQUIRES(!lock_);
/**
* Try to inline an invoke.
*/
bool GenInline(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, uint32_t method_idx)
- LOCKS_EXCLUDED(lock_);
+ REQUIRES(!lock_);
/**
* Gets the thread pointer entrypoint offset for a string init method index and pointer size.
*/
uint32_t GetOffsetForStringInit(uint32_t method_index, size_t pointer_size)
- LOCKS_EXCLUDED(lock_);
+ REQUIRES(!lock_);
/**
* Check whether a particular method index is a string init.
*/
- bool IsStringInitMethodIndex(uint32_t method_index) LOCKS_EXCLUDED(lock_);
+ bool IsStringInitMethodIndex(uint32_t method_index) REQUIRES(!lock_);
/**
* To avoid multiple lookups of a class by its descriptor, we cache its
@@ -206,6 +206,7 @@ class DexFileMethodInliner {
kNameCachePutObjectVolatile,
kNameCachePutOrderedObject,
kNameCacheArrayCopy,
+ kNameCacheNumberOfLeadingZeros,
kNameCacheLast
};
@@ -351,11 +352,11 @@ class DexFileMethodInliner {
*
* Only DexFileToMethodInlinerMap may call this function to initialize the inliner.
*/
- void FindIntrinsics(const DexFile* dex_file) EXCLUSIVE_LOCKS_REQUIRED(lock_);
+ void FindIntrinsics(const DexFile* dex_file) REQUIRES(lock_);
friend class DexFileToMethodInlinerMap;
- bool AddInlineMethod(int32_t method_idx, const InlineMethod& method) LOCKS_EXCLUDED(lock_);
+ bool AddInlineMethod(int32_t method_idx, const InlineMethod& method) REQUIRES(!lock_);
static bool GenInlineConst(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke,
MIR* move_result, const InlineMethod& method);
diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc
index dd68dd40c6..16c161e320 100644
--- a/compiler/dex/quick/quick_cfi_test.cc
+++ b/compiler/dex/quick/quick_cfi_test.cc
@@ -36,7 +36,7 @@
namespace art {
// Run the tests only on host.
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
class QuickCFITest : public CFITest {
public:
@@ -56,6 +56,8 @@ class QuickCFITest : public CFITest {
CompilerOptions::kDefaultSmallMethodThreshold,
CompilerOptions::kDefaultTinyMethodThreshold,
CompilerOptions::kDefaultNumDexMethodsThreshold,
+ CompilerOptions::kDefaultInlineDepthLimit,
+ CompilerOptions::kDefaultInlineMaxCodeUnits,
false,
CompilerOptions::kDefaultTopKProfileThreshold,
false,
@@ -134,6 +136,6 @@ TEST_ISA(kX86_64)
TEST_ISA(kMips)
TEST_ISA(kMips64)
-#endif // HAVE_ANDROID_OS
+#endif // __ANDROID__
} // namespace art
diff --git a/compiler/dex/quick/quick_compiler.h b/compiler/dex/quick/quick_compiler.h
index 43dd5786af..4a39ab3565 100644
--- a/compiler/dex/quick/quick_compiler.h
+++ b/compiler/dex/quick/quick_compiler.h
@@ -50,7 +50,7 @@ class QuickCompiler : public Compiler {
const DexFile& dex_file) const OVERRIDE;
uintptr_t GetEntryPointOf(ArtMethod* method) const OVERRIDE
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
static Mir2Lir* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit);
diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
index 798e23fbac..98e9f38d52 100644
--- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc
+++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
@@ -39,6 +39,8 @@ class QuickAssembleX86TestBase : public testing::Test {
CompilerOptions::kDefaultSmallMethodThreshold,
CompilerOptions::kDefaultTinyMethodThreshold,
CompilerOptions::kDefaultNumDexMethodsThreshold,
+ CompilerOptions::kDefaultInlineDepthLimit,
+ CompilerOptions::kDefaultInlineMaxCodeUnits,
false,
CompilerOptions::kDefaultTopKProfileThreshold,
false,
diff --git a/compiler/dex/quick_compiler_callbacks.h b/compiler/dex/quick_compiler_callbacks.h
index d692d26229..03bf57bded 100644
--- a/compiler/dex/quick_compiler_callbacks.h
+++ b/compiler/dex/quick_compiler_callbacks.h
@@ -38,7 +38,7 @@ class QuickCompilerCallbacks FINAL : public CompilerCallbacks {
~QuickCompilerCallbacks() { }
bool MethodVerified(verifier::MethodVerifier* verifier)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE;
+ SHARED_REQUIRES(Locks::mutator_lock_) OVERRIDE;
void ClassRejected(ClassReference ref) OVERRIDE;
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 7fc2a2363d..9934f6b13b 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -43,15 +43,15 @@ class VerificationResults {
~VerificationResults();
bool ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- LOCKS_EXCLUDED(verified_methods_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!verified_methods_lock_);
const VerifiedMethod* GetVerifiedMethod(MethodReference ref)
- LOCKS_EXCLUDED(verified_methods_lock_);
- void RemoveVerifiedMethod(MethodReference ref) LOCKS_EXCLUDED(verified_methods_lock_);
+ REQUIRES(!verified_methods_lock_);
+ void RemoveVerifiedMethod(MethodReference ref) REQUIRES(!verified_methods_lock_);
- void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
- bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
+ void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_);
+ bool IsClassRejected(ClassReference ref) REQUIRES(!rejected_classes_lock_);
bool IsCandidateForCompilation(MethodReference& method_ref,
const uint32_t access_flags);
diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h
index bf11839cf0..f7d6d67368 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -44,7 +44,7 @@ class VerifiedMethod {
typedef SafeMap<uint32_t, DexFileReference> DequickenMap;
static const VerifiedMethod* Create(verifier::MethodVerifier* method_verifier, bool compile)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
~VerifiedMethod() = default;
const std::vector<uint8_t>& GetDexGcMap() const {
@@ -107,15 +107,15 @@ class VerifiedMethod {
// Generate devirtualizaion map into devirt_map_.
void GenerateDevirtMap(verifier::MethodVerifier* method_verifier)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Generate dequickening map into dequicken_map_. Returns false if there is an error.
bool GenerateDequickenMap(verifier::MethodVerifier* method_verifier)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Generate safe case set into safe_cast_set_.
void GenerateSafeCastSet(verifier::MethodVerifier* method_verifier)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
std::vector<uint8_t> dex_gc_map_;
DevirtualizationMap devirt_map_;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index a52bfaeb5b..affa52a37a 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -167,69 +167,69 @@ class CompilerDriver::AOTCompilationStats {
#define STATS_LOCK()
#endif
- void TypeInDexCache() {
+ void TypeInDexCache() REQUIRES(!stats_lock_) {
STATS_LOCK();
types_in_dex_cache_++;
}
- void TypeNotInDexCache() {
+ void TypeNotInDexCache() REQUIRES(!stats_lock_) {
STATS_LOCK();
types_not_in_dex_cache_++;
}
- void StringInDexCache() {
+ void StringInDexCache() REQUIRES(!stats_lock_) {
STATS_LOCK();
strings_in_dex_cache_++;
}
- void StringNotInDexCache() {
+ void StringNotInDexCache() REQUIRES(!stats_lock_) {
STATS_LOCK();
strings_not_in_dex_cache_++;
}
- void TypeDoesntNeedAccessCheck() {
+ void TypeDoesntNeedAccessCheck() REQUIRES(!stats_lock_) {
STATS_LOCK();
resolved_types_++;
}
- void TypeNeedsAccessCheck() {
+ void TypeNeedsAccessCheck() REQUIRES(!stats_lock_) {
STATS_LOCK();
unresolved_types_++;
}
- void ResolvedInstanceField() {
+ void ResolvedInstanceField() REQUIRES(!stats_lock_) {
STATS_LOCK();
resolved_instance_fields_++;
}
- void UnresolvedInstanceField() {
+ void UnresolvedInstanceField() REQUIRES(!stats_lock_) {
STATS_LOCK();
unresolved_instance_fields_++;
}
- void ResolvedLocalStaticField() {
+ void ResolvedLocalStaticField() REQUIRES(!stats_lock_) {
STATS_LOCK();
resolved_local_static_fields_++;
}
- void ResolvedStaticField() {
+ void ResolvedStaticField() REQUIRES(!stats_lock_) {
STATS_LOCK();
resolved_static_fields_++;
}
- void UnresolvedStaticField() {
+ void UnresolvedStaticField() REQUIRES(!stats_lock_) {
STATS_LOCK();
unresolved_static_fields_++;
}
// Indicate that type information from the verifier led to devirtualization.
- void PreciseTypeDevirtualization() {
+ void PreciseTypeDevirtualization() REQUIRES(!stats_lock_) {
STATS_LOCK();
type_based_devirtualization_++;
}
// Indicate that a method of the given type was resolved at compile time.
- void ResolvedMethod(InvokeType type) {
+ void ResolvedMethod(InvokeType type) REQUIRES(!stats_lock_) {
DCHECK_LE(type, kMaxInvokeType);
STATS_LOCK();
resolved_methods_[type]++;
@@ -237,7 +237,7 @@ class CompilerDriver::AOTCompilationStats {
// Indicate that a method of the given type was unresolved at compile time as it was in an
// unknown dex file.
- void UnresolvedMethod(InvokeType type) {
+ void UnresolvedMethod(InvokeType type) REQUIRES(!stats_lock_) {
DCHECK_LE(type, kMaxInvokeType);
STATS_LOCK();
unresolved_methods_[type]++;
@@ -245,27 +245,27 @@ class CompilerDriver::AOTCompilationStats {
// Indicate that a type of virtual method dispatch has been converted into a direct method
// dispatch.
- void VirtualMadeDirect(InvokeType type) {
+ void VirtualMadeDirect(InvokeType type) REQUIRES(!stats_lock_) {
DCHECK(type == kVirtual || type == kInterface || type == kSuper);
STATS_LOCK();
virtual_made_direct_[type]++;
}
// Indicate that a method of the given type was able to call directly into boot.
- void DirectCallsToBoot(InvokeType type) {
+ void DirectCallsToBoot(InvokeType type) REQUIRES(!stats_lock_) {
DCHECK_LE(type, kMaxInvokeType);
STATS_LOCK();
direct_calls_to_boot_[type]++;
}
// Indicate that a method of the given type was able to be resolved directly from boot.
- void DirectMethodsToBoot(InvokeType type) {
+ void DirectMethodsToBoot(InvokeType type) REQUIRES(!stats_lock_) {
DCHECK_LE(type, kMaxInvokeType);
STATS_LOCK();
direct_methods_to_boot_[type]++;
}
- void ProcessedInvoke(InvokeType type, int flags) {
+ void ProcessedInvoke(InvokeType type, int flags) REQUIRES(!stats_lock_) {
STATS_LOCK();
if (flags == 0) {
unresolved_methods_[type]++;
@@ -290,13 +290,13 @@ class CompilerDriver::AOTCompilationStats {
}
// A check-cast could be eliminated due to verifier type analysis.
- void SafeCast() {
+ void SafeCast() REQUIRES(!stats_lock_) {
STATS_LOCK();
safe_casts_++;
}
// A check-cast couldn't be eliminated due to verifier type analysis.
- void NotASafeCast() {
+ void NotASafeCast() REQUIRES(!stats_lock_) {
STATS_LOCK();
not_safe_casts_++;
}
@@ -690,70 +690,79 @@ bool CompilerDriver::IsMethodToCompile(const MethodReference& method_ref) const
return methods_to_compile_->find(tmp.c_str()) != methods_to_compile_->end();
}
-static void ResolveExceptionsForMethod(
- ArtMethod* method_handle, std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- const DexFile::CodeItem* code_item = method_handle->GetCodeItem();
- if (code_item == nullptr) {
- return; // native or abstract method
- }
- if (code_item->tries_size_ == 0) {
- return; // nothing to process
- }
- const uint8_t* encoded_catch_handler_list = DexFile::GetCatchHandlerData(*code_item, 0);
- size_t num_encoded_catch_handlers = DecodeUnsignedLeb128(&encoded_catch_handler_list);
- for (size_t i = 0; i < num_encoded_catch_handlers; i++) {
- int32_t encoded_catch_handler_size = DecodeSignedLeb128(&encoded_catch_handler_list);
- bool has_catch_all = false;
- if (encoded_catch_handler_size <= 0) {
- encoded_catch_handler_size = -encoded_catch_handler_size;
- has_catch_all = true;
- }
- for (int32_t j = 0; j < encoded_catch_handler_size; j++) {
- uint16_t encoded_catch_handler_handlers_type_idx =
- DecodeUnsignedLeb128(&encoded_catch_handler_list);
- // Add to set of types to resolve if not already in the dex cache resolved types
- if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
- exceptions_to_resolve.insert(
- std::pair<uint16_t, const DexFile*>(encoded_catch_handler_handlers_type_idx,
- method_handle->GetDexFile()));
+class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
+ public:
+ ResolveCatchBlockExceptionsClassVisitor(
+ std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve)
+ : exceptions_to_resolve_(exceptions_to_resolve) {}
+
+ void ResolveExceptionsForMethod(ArtMethod* method_handle) SHARED_REQUIRES(Locks::mutator_lock_) {
+ const DexFile::CodeItem* code_item = method_handle->GetCodeItem();
+ if (code_item == nullptr) {
+ return; // native or abstract method
+ }
+ if (code_item->tries_size_ == 0) {
+ return; // nothing to process
+ }
+ const uint8_t* encoded_catch_handler_list = DexFile::GetCatchHandlerData(*code_item, 0);
+ size_t num_encoded_catch_handlers = DecodeUnsignedLeb128(&encoded_catch_handler_list);
+ for (size_t i = 0; i < num_encoded_catch_handlers; i++) {
+ int32_t encoded_catch_handler_size = DecodeSignedLeb128(&encoded_catch_handler_list);
+ bool has_catch_all = false;
+ if (encoded_catch_handler_size <= 0) {
+ encoded_catch_handler_size = -encoded_catch_handler_size;
+ has_catch_all = true;
+ }
+ for (int32_t j = 0; j < encoded_catch_handler_size; j++) {
+ uint16_t encoded_catch_handler_handlers_type_idx =
+ DecodeUnsignedLeb128(&encoded_catch_handler_list);
+ // Add to set of types to resolve if not already in the dex cache resolved types
+ if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
+ exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx,
+ method_handle->GetDexFile());
+ }
+ // ignore address associated with catch handler
+ DecodeUnsignedLeb128(&encoded_catch_handler_list);
+ }
+ if (has_catch_all) {
+ // ignore catch all address
+ DecodeUnsignedLeb128(&encoded_catch_handler_list);
}
- // ignore address associated with catch handler
- DecodeUnsignedLeb128(&encoded_catch_handler_list);
- }
- if (has_catch_all) {
- // ignore catch all address
- DecodeUnsignedLeb128(&encoded_catch_handler_list);
}
}
-}
-static bool ResolveCatchBlockExceptionsClassVisitor(mirror::Class* c, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- auto* exceptions_to_resolve =
- reinterpret_cast<std::set<std::pair<uint16_t, const DexFile*>>*>(arg);
- const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- for (auto& m : c->GetVirtualMethods(pointer_size)) {
- ResolveExceptionsForMethod(&m, *exceptions_to_resolve);
+ virtual bool Visit(mirror::Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ for (auto& m : c->GetVirtualMethods(pointer_size)) {
+ ResolveExceptionsForMethod(&m);
+ }
+ for (auto& m : c->GetDirectMethods(pointer_size)) {
+ ResolveExceptionsForMethod(&m);
+ }
+ return true;
}
- for (auto& m : c->GetDirectMethods(pointer_size)) {
- ResolveExceptionsForMethod(&m, *exceptions_to_resolve);
+
+ private:
+ std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve_;
+};
+
+class RecordImageClassesVisitor : public ClassVisitor {
+ public:
+ explicit RecordImageClassesVisitor(std::unordered_set<std::string>* image_classes)
+ : image_classes_(image_classes) {}
+
+ bool Visit(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ std::string temp;
+ image_classes_->insert(klass->GetDescriptor(&temp));
+ return true;
}
- return true;
-}
-static bool RecordImageClassesVisitor(mirror::Class* klass, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- std::unordered_set<std::string>* image_classes =
- reinterpret_cast<std::unordered_set<std::string>*>(arg);
- std::string temp;
- image_classes->insert(klass->GetDescriptor(&temp));
- return true;
-}
+ private:
+ std::unordered_set<std::string>* const image_classes_;
+};
// Make a list of descriptors for classes to include in the image
-void CompilerDriver::LoadImageClasses(TimingLogger* timings)
- LOCKS_EXCLUDED(Locks::mutator_lock_) {
+void CompilerDriver::LoadImageClasses(TimingLogger* timings) {
CHECK(timings != nullptr);
if (!IsImage()) {
return;
@@ -788,8 +797,8 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings)
hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/lang/Throwable;")));
do {
unresolved_exception_types.clear();
- class_linker->VisitClasses(ResolveCatchBlockExceptionsClassVisitor,
- &unresolved_exception_types);
+ ResolveCatchBlockExceptionsClassVisitor visitor(unresolved_exception_types);
+ class_linker->VisitClasses(&visitor);
for (const std::pair<uint16_t, const DexFile*>& exception_type : unresolved_exception_types) {
uint16_t exception_type_idx = exception_type.first;
const DexFile* dex_file = exception_type.second;
@@ -812,14 +821,15 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings)
// We walk the roots looking for classes so that we'll pick up the
// above classes plus any classes them depend on such super
// classes, interfaces, and the required ClassLinker roots.
- class_linker->VisitClasses(RecordImageClassesVisitor, image_classes_.get());
+ RecordImageClassesVisitor visitor(image_classes_.get());
+ class_linker->VisitClasses(&visitor);
CHECK_NE(image_classes_->size(), 0U);
}
static void MaybeAddToImageClasses(Handle<mirror::Class> c,
std::unordered_set<std::string>* image_classes)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
Thread* self = Thread::Current();
StackHandleScope<1> hs(self);
// Make a copy of the handle so that we don't clobber it doing Assign.
@@ -876,7 +886,7 @@ class ClinitImageUpdate {
// Visitor for VisitReferences.
void operator()(mirror::Object* object, MemberOffset field_offset, bool /* is_static */) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
mirror::Object* ref = object->GetFieldObject<mirror::Object>(field_offset);
if (ref != nullptr) {
VisitClinitClassesObject(ref);
@@ -884,10 +894,15 @@ class ClinitImageUpdate {
}
// java.lang.Reference visitor for VisitReferences.
- void operator()(mirror::Class* /* klass */, mirror::Reference* /* ref */) const {
- }
+ void operator()(mirror::Class* klass ATTRIBUTE_UNUSED, mirror::Reference* ref ATTRIBUTE_UNUSED)
+ const {}
- void Walk() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Ignore class native roots.
+ void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED)
+ const {}
+ void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
+
+ void Walk() SHARED_REQUIRES(Locks::mutator_lock_) {
// Use the initial classes as roots for a search.
for (mirror::Class* klass_root : image_classes_) {
VisitClinitClassesObject(klass_root);
@@ -895,9 +910,32 @@ class ClinitImageUpdate {
}
private:
+ class FindImageClassesVisitor : public ClassVisitor {
+ public:
+ explicit FindImageClassesVisitor(ClinitImageUpdate* data) : data_(data) {}
+
+ bool Visit(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ std::string temp;
+ const char* name = klass->GetDescriptor(&temp);
+ if (data_->image_class_descriptors_->find(name) != data_->image_class_descriptors_->end()) {
+ data_->image_classes_.push_back(klass);
+ } else {
+ // Check whether it is initialized and has a clinit. They must be kept, too.
+ if (klass->IsInitialized() && klass->FindClassInitializer(
+ Runtime::Current()->GetClassLinker()->GetImagePointerSize()) != nullptr) {
+ data_->image_classes_.push_back(klass);
+ }
+ }
+ return true;
+ }
+
+ private:
+ ClinitImageUpdate* const data_;
+ };
+
ClinitImageUpdate(std::unordered_set<std::string>* image_class_descriptors, Thread* self,
ClassLinker* linker)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
+ SHARED_REQUIRES(Locks::mutator_lock_) :
image_class_descriptors_(image_class_descriptors), self_(self) {
CHECK(linker != nullptr);
CHECK(image_class_descriptors != nullptr);
@@ -911,29 +949,12 @@ class ClinitImageUpdate {
// Find all the already-marked classes.
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
- linker->VisitClasses(FindImageClasses, this);
- }
-
- static bool FindImageClasses(mirror::Class* klass, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ClinitImageUpdate* data = reinterpret_cast<ClinitImageUpdate*>(arg);
- std::string temp;
- const char* name = klass->GetDescriptor(&temp);
- if (data->image_class_descriptors_->find(name) != data->image_class_descriptors_->end()) {
- data->image_classes_.push_back(klass);
- } else {
- // Check whether it is initialized and has a clinit. They must be kept, too.
- if (klass->IsInitialized() && klass->FindClassInitializer(
- Runtime::Current()->GetClassLinker()->GetImagePointerSize()) != nullptr) {
- data->image_classes_.push_back(klass);
- }
- }
-
- return true;
+ FindImageClassesVisitor visitor(this);
+ linker->VisitClasses(&visitor);
}
void VisitClinitClassesObject(mirror::Object* object) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(object != nullptr);
if (marked_objects_.find(object) != marked_objects_.end()) {
// Already processed.
@@ -1569,10 +1590,14 @@ bool CompilerDriver::IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc
return result;
}
-class ParallelCompilationManager {
+class CompilationVisitor {
public:
- typedef void Callback(const ParallelCompilationManager* manager, size_t index);
+ virtual ~CompilationVisitor() {}
+ virtual void Visit(size_t index) = 0;
+};
+class ParallelCompilationManager {
+ public:
ParallelCompilationManager(ClassLinker* class_linker,
jobject class_loader,
CompilerDriver* compiler,
@@ -1610,14 +1635,15 @@ class ParallelCompilationManager {
return dex_files_;
}
- void ForAll(size_t begin, size_t end, Callback callback, size_t work_units) {
+ void ForAll(size_t begin, size_t end, CompilationVisitor* visitor, size_t work_units)
+ REQUIRES(!*Locks::mutator_lock_) {
Thread* self = Thread::Current();
self->AssertNoPendingException();
CHECK_GT(work_units, 0U);
index_.StoreRelaxed(begin);
for (size_t i = 0; i < work_units; ++i) {
- thread_pool_->AddTask(self, new ForAllClosure(this, end, callback));
+ thread_pool_->AddTask(self, new ForAllClosure(this, end, visitor));
}
thread_pool_->StartWorkers(self);
@@ -1636,10 +1662,10 @@ class ParallelCompilationManager {
private:
class ForAllClosure : public Task {
public:
- ForAllClosure(ParallelCompilationManager* manager, size_t end, Callback* callback)
+ ForAllClosure(ParallelCompilationManager* manager, size_t end, CompilationVisitor* visitor)
: manager_(manager),
end_(end),
- callback_(callback) {}
+ visitor_(visitor) {}
virtual void Run(Thread* self) {
while (true) {
@@ -1647,7 +1673,7 @@ class ParallelCompilationManager {
if (UNLIKELY(index >= end_)) {
break;
}
- callback_(manager_, index);
+ visitor_->Visit(index);
self->AssertNoPendingException();
}
}
@@ -1659,7 +1685,7 @@ class ParallelCompilationManager {
private:
ParallelCompilationManager* const manager_;
const size_t end_;
- Callback* const callback_;
+ CompilationVisitor* const visitor_;
};
AtomicInteger index_;
@@ -1676,7 +1702,7 @@ class ParallelCompilationManager {
// A fast version of SkipClass above if the class pointer is available
// that avoids the expensive FindInClassPath search.
static bool SkipClass(jobject class_loader, const DexFile& dex_file, mirror::Class* klass)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(klass != nullptr);
const DexFile& original_dex_file = *klass->GetDexCache()->GetDexFile();
if (&dex_file != &original_dex_file) {
@@ -1691,7 +1717,7 @@ static bool SkipClass(jobject class_loader, const DexFile& dex_file, mirror::Cla
}
static void CheckAndClearResolveException(Thread* self)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
CHECK(self->IsExceptionPending());
mirror::Throwable* exception = self->GetException();
std::string temp;
@@ -1717,134 +1743,148 @@ static void CheckAndClearResolveException(Thread* self)
self->ClearException();
}
-static void ResolveClassFieldsAndMethods(const ParallelCompilationManager* manager,
- size_t class_def_index)
- LOCKS_EXCLUDED(Locks::mutator_lock_) {
- ATRACE_CALL();
- Thread* self = Thread::Current();
- jobject jclass_loader = manager->GetClassLoader();
- const DexFile& dex_file = *manager->GetDexFile();
- ClassLinker* class_linker = manager->GetClassLinker();
-
- // If an instance field is final then we need to have a barrier on the return, static final
- // fields are assigned within the lock held for class initialization. Conservatively assume
- // constructor barriers are always required.
- bool requires_constructor_barrier = true;
-
- // Method and Field are the worst. We can't resolve without either
- // context from the code use (to disambiguate virtual vs direct
- // method and instance vs static field) or from class
- // definitions. While the compiler will resolve what it can as it
- // needs it, here we try to resolve fields and methods used in class
- // definitions, since many of them many never be referenced by
- // generated code.
- const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- ScopedObjectAccess soa(self);
- StackHandleScope<2> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file)));
- // Resolve the class.
- mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache,
- class_loader);
- bool resolve_fields_and_methods;
- if (klass == nullptr) {
- // Class couldn't be resolved, for example, super-class is in a different dex file. Don't
- // attempt to resolve methods and fields when there is no declaring class.
- CheckAndClearResolveException(soa.Self());
- resolve_fields_and_methods = false;
- } else {
- // We successfully resolved a class, should we skip it?
- if (SkipClass(jclass_loader, dex_file, klass)) {
- return;
- }
- // We want to resolve the methods and fields eagerly.
- resolve_fields_and_methods = true;
- }
- // Note the class_data pointer advances through the headers,
- // static fields, instance fields, direct methods, and virtual
- // methods.
- const uint8_t* class_data = dex_file.GetClassData(class_def);
- if (class_data == nullptr) {
- // Empty class such as a marker interface.
- requires_constructor_barrier = false;
- } else {
- ClassDataItemIterator it(dex_file, class_data);
- while (it.HasNextStaticField()) {
- if (resolve_fields_and_methods) {
- ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
- dex_cache, class_loader, true);
- if (field == nullptr) {
- CheckAndClearResolveException(soa.Self());
- }
- }
- it.Next();
- }
- // We require a constructor barrier if there are final instance fields.
- requires_constructor_barrier = false;
- while (it.HasNextInstanceField()) {
- if (it.MemberIsFinal()) {
- requires_constructor_barrier = true;
+class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor {
+ public:
+ explicit ResolveClassFieldsAndMethodsVisitor(const ParallelCompilationManager* manager)
+ : manager_(manager) {}
+
+ void Visit(size_t class_def_index) OVERRIDE REQUIRES(!Locks::mutator_lock_) {
+ ATRACE_CALL();
+ Thread* const self = Thread::Current();
+ jobject jclass_loader = manager_->GetClassLoader();
+ const DexFile& dex_file = *manager_->GetDexFile();
+ ClassLinker* class_linker = manager_->GetClassLinker();
+
+ // If an instance field is final then we need to have a barrier on the return, static final
+ // fields are assigned within the lock held for class initialization. Conservatively assume
+ // constructor barriers are always required.
+ bool requires_constructor_barrier = true;
+
+ // Method and Field are the worst. We can't resolve without either
+ // context from the code use (to disambiguate virtual vs direct
+ // method and instance vs static field) or from class
+ // definitions. While the compiler will resolve what it can as it
+ // needs it, here we try to resolve fields and methods used in class
+ // definitions, since many of them many never be referenced by
+ // generated code.
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+ ScopedObjectAccess soa(self);
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file)));
+ // Resolve the class.
+ mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache,
+ class_loader);
+ bool resolve_fields_and_methods;
+ if (klass == nullptr) {
+ // Class couldn't be resolved, for example, super-class is in a different dex file. Don't
+ // attempt to resolve methods and fields when there is no declaring class.
+ CheckAndClearResolveException(soa.Self());
+ resolve_fields_and_methods = false;
+ } else {
+ // We successfully resolved a class, should we skip it?
+ if (SkipClass(jclass_loader, dex_file, klass)) {
+ return;
}
- if (resolve_fields_and_methods) {
- ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
- dex_cache, class_loader, false);
- if (field == nullptr) {
- CheckAndClearResolveException(soa.Self());
+ // We want to resolve the methods and fields eagerly.
+ resolve_fields_and_methods = true;
+ }
+ // Note the class_data pointer advances through the headers,
+ // static fields, instance fields, direct methods, and virtual
+ // methods.
+ const uint8_t* class_data = dex_file.GetClassData(class_def);
+ if (class_data == nullptr) {
+ // Empty class such as a marker interface.
+ requires_constructor_barrier = false;
+ } else {
+ ClassDataItemIterator it(dex_file, class_data);
+ while (it.HasNextStaticField()) {
+ if (resolve_fields_and_methods) {
+ ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
+ dex_cache, class_loader, true);
+ if (field == nullptr) {
+ CheckAndClearResolveException(soa.Self());
+ }
}
+ it.Next();
}
- it.Next();
- }
- if (resolve_fields_and_methods) {
- while (it.HasNextDirectMethod()) {
- ArtMethod* method = class_linker->ResolveMethod(
- dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr,
- it.GetMethodInvokeType(class_def));
- if (method == nullptr) {
- CheckAndClearResolveException(soa.Self());
+ // We require a constructor barrier if there are final instance fields.
+ requires_constructor_barrier = false;
+ while (it.HasNextInstanceField()) {
+ if (it.MemberIsFinal()) {
+ requires_constructor_barrier = true;
+ }
+ if (resolve_fields_and_methods) {
+ ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
+ dex_cache, class_loader, false);
+ if (field == nullptr) {
+ CheckAndClearResolveException(soa.Self());
+ }
}
it.Next();
}
- while (it.HasNextVirtualMethod()) {
- ArtMethod* method = class_linker->ResolveMethod(
- dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr,
- it.GetMethodInvokeType(class_def));
- if (method == nullptr) {
- CheckAndClearResolveException(soa.Self());
+ if (resolve_fields_and_methods) {
+ while (it.HasNextDirectMethod()) {
+ ArtMethod* method = class_linker->ResolveMethod(
+ dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr,
+ it.GetMethodInvokeType(class_def));
+ if (method == nullptr) {
+ CheckAndClearResolveException(soa.Self());
+ }
+ it.Next();
}
- it.Next();
+ while (it.HasNextVirtualMethod()) {
+ ArtMethod* method = class_linker->ResolveMethod(
+ dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr,
+ it.GetMethodInvokeType(class_def));
+ if (method == nullptr) {
+ CheckAndClearResolveException(soa.Self());
+ }
+ it.Next();
+ }
+ DCHECK(!it.HasNext());
}
- DCHECK(!it.HasNext());
+ }
+ if (requires_constructor_barrier) {
+ manager_->GetCompiler()->AddRequiresConstructorBarrier(self, &dex_file, class_def_index);
}
}
- if (requires_constructor_barrier) {
- manager->GetCompiler()->AddRequiresConstructorBarrier(self, &dex_file, class_def_index);
- }
-}
-static void ResolveType(const ParallelCompilationManager* manager, size_t type_idx)
- LOCKS_EXCLUDED(Locks::mutator_lock_) {
- // Class derived values are more complicated, they require the linker and loader.
- ScopedObjectAccess soa(Thread::Current());
- ClassLinker* class_linker = manager->GetClassLinker();
- const DexFile& dex_file = *manager->GetDexFile();
- StackHandleScope<2> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file)));
- Handle<mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader())));
- mirror::Class* klass = class_linker->ResolveType(dex_file, type_idx, dex_cache, class_loader);
+ private:
+ const ParallelCompilationManager* const manager_;
+};
- if (klass == nullptr) {
- CHECK(soa.Self()->IsExceptionPending());
- mirror::Throwable* exception = soa.Self()->GetException();
- VLOG(compiler) << "Exception during type resolution: " << exception->Dump();
- if (exception->GetClass()->DescriptorEquals("Ljava/lang/OutOfMemoryError;")) {
- // There's little point continuing compilation if the heap is exhausted.
- LOG(FATAL) << "Out of memory during type resolution for compilation";
+class ResolveTypeVisitor : public CompilationVisitor {
+ public:
+ explicit ResolveTypeVisitor(const ParallelCompilationManager* manager) : manager_(manager) {
+ }
+ virtual void Visit(size_t type_idx) OVERRIDE REQUIRES(!Locks::mutator_lock_) {
+ // Class derived values are more complicated, they require the linker and loader.
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* class_linker = manager_->GetClassLinker();
+ const DexFile& dex_file = *manager_->GetDexFile();
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file)));
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(manager_->GetClassLoader())));
+ mirror::Class* klass = class_linker->ResolveType(dex_file, type_idx, dex_cache, class_loader);
+
+ if (klass == nullptr) {
+ soa.Self()->AssertPendingException();
+ mirror::Throwable* exception = soa.Self()->GetException();
+ VLOG(compiler) << "Exception during type resolution: " << exception->Dump();
+ if (exception->GetClass()->DescriptorEquals("Ljava/lang/OutOfMemoryError;")) {
+ // There's little point continuing compilation if the heap is exhausted.
+ LOG(FATAL) << "Out of memory during type resolution for compilation";
+ }
+ soa.Self()->ClearException();
}
- soa.Self()->ClearException();
}
-}
+
+ private:
+ const ParallelCompilationManager* const manager_;
+};
void CompilerDriver::ResolveDexFile(jobject class_loader, const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
@@ -1860,17 +1900,18 @@ void CompilerDriver::ResolveDexFile(jobject class_loader, const DexFile& dex_fil
// For images we resolve all types, such as array, whereas for applications just those with
// classdefs are resolved by ResolveClassFieldsAndMethods.
TimingLogger::ScopedTiming t("Resolve Types", timings);
- context.ForAll(0, dex_file.NumTypeIds(), ResolveType, thread_count_);
+ ResolveTypeVisitor visitor(&context);
+ context.ForAll(0, dex_file.NumTypeIds(), &visitor, thread_count_);
}
TimingLogger::ScopedTiming t("Resolve MethodsAndFields", timings);
- context.ForAll(0, dex_file.NumClassDefs(), ResolveClassFieldsAndMethods, thread_count_);
+ ResolveClassFieldsAndMethodsVisitor visitor(&context);
+ context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
}
void CompilerDriver::SetVerified(jobject class_loader, const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings) {
- for (size_t i = 0; i != dex_files.size(); ++i) {
- const DexFile* dex_file = dex_files[i];
+ for (const DexFile* dex_file : dex_files) {
CHECK(dex_file != nullptr);
SetVerifiedDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
}
@@ -1878,67 +1919,73 @@ void CompilerDriver::SetVerified(jobject class_loader, const std::vector<const D
void CompilerDriver::Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings) {
- for (size_t i = 0; i != dex_files.size(); ++i) {
- const DexFile* dex_file = dex_files[i];
+ for (const DexFile* dex_file : dex_files) {
CHECK(dex_file != nullptr);
VerifyDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
}
}
-static void VerifyClass(const ParallelCompilationManager* manager, size_t class_def_index)
- LOCKS_EXCLUDED(Locks::mutator_lock_) {
- ATRACE_CALL();
- ScopedObjectAccess soa(Thread::Current());
- const DexFile& dex_file = *manager->GetDexFile();
- const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- const char* descriptor = dex_file.GetClassDescriptor(class_def);
- ClassLinker* class_linker = manager->GetClassLinker();
- jobject jclass_loader = manager->GetClassLoader();
- StackHandleScope<3> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
- Handle<mirror::Class> klass(
- hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
- if (klass.Get() == nullptr) {
- CHECK(soa.Self()->IsExceptionPending());
- soa.Self()->ClearException();
+class VerifyClassVisitor : public CompilationVisitor {
+ public:
+ explicit VerifyClassVisitor(const ParallelCompilationManager* manager) : manager_(manager) {}
- /*
- * At compile time, we can still structurally verify the class even if FindClass fails.
- * This is to ensure the class is structurally sound for compilation. An unsound class
- * will be rejected by the verifier and later skipped during compilation in the compiler.
- */
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file)));
- std::string error_msg;
- if (verifier::MethodVerifier::VerifyClass(soa.Self(), &dex_file, dex_cache, class_loader,
- &class_def, true, &error_msg) ==
- verifier::MethodVerifier::kHardFailure) {
- LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(descriptor)
- << " because: " << error_msg;
- manager->GetCompiler()->SetHadHardVerifierFailure();
- }
- } else if (!SkipClass(jclass_loader, dex_file, klass.Get())) {
- CHECK(klass->IsResolved()) << PrettyClass(klass.Get());
- class_linker->VerifyClass(soa.Self(), klass);
-
- if (klass->IsErroneous()) {
- // ClassLinker::VerifyClass throws, which isn't useful in the compiler.
+ virtual void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE {
+ ATRACE_CALL();
+ ScopedObjectAccess soa(Thread::Current());
+ const DexFile& dex_file = *manager_->GetDexFile();
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+ const char* descriptor = dex_file.GetClassDescriptor(class_def);
+ ClassLinker* class_linker = manager_->GetClassLinker();
+ jobject jclass_loader = manager_->GetClassLoader();
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+ Handle<mirror::Class> klass(
+ hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
+ if (klass.Get() == nullptr) {
CHECK(soa.Self()->IsExceptionPending());
soa.Self()->ClearException();
- manager->GetCompiler()->SetHadHardVerifierFailure();
- }
- CHECK(klass->IsCompileTimeVerified() || klass->IsErroneous())
- << PrettyDescriptor(klass.Get()) << ": state=" << klass->GetStatus();
+ /*
+ * At compile time, we can still structurally verify the class even if FindClass fails.
+ * This is to ensure the class is structurally sound for compilation. An unsound class
+ * will be rejected by the verifier and later skipped during compilation in the compiler.
+ */
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file)));
+ std::string error_msg;
+ if (verifier::MethodVerifier::VerifyClass(soa.Self(), &dex_file, dex_cache, class_loader,
+ &class_def, true, &error_msg) ==
+ verifier::MethodVerifier::kHardFailure) {
+ LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(descriptor)
+ << " because: " << error_msg;
+ manager_->GetCompiler()->SetHadHardVerifierFailure();
+ }
+ } else if (!SkipClass(jclass_loader, dex_file, klass.Get())) {
+ CHECK(klass->IsResolved()) << PrettyClass(klass.Get());
+ class_linker->VerifyClass(soa.Self(), klass);
+
+ if (klass->IsErroneous()) {
+ // ClassLinker::VerifyClass throws, which isn't useful in the compiler.
+ CHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ manager_->GetCompiler()->SetHadHardVerifierFailure();
+ }
- // It is *very* problematic if there are verification errors in the boot classpath. For example,
- // we rely on things working OK without verification when the decryption dialog is brought up.
- // So abort in a debug build if we find this violated.
- DCHECK(!manager->GetCompiler()->IsImage() || klass->IsVerified()) << "Boot classpath class " <<
- PrettyClass(klass.Get()) << " failed to fully verify.";
+ CHECK(klass->IsCompileTimeVerified() || klass->IsErroneous())
+ << PrettyDescriptor(klass.Get()) << ": state=" << klass->GetStatus();
+
+ // It is *very* problematic if there are verification errors in the boot classpath. For example,
+ // we rely on things working OK without verification when the decryption dialog is brought up.
+ // So abort in a debug build if we find this violated.
+ DCHECK(!manager_->GetCompiler()->IsImage() || klass->IsVerified()) << "Boot classpath class "
+ << PrettyClass(klass.Get()) << " failed to fully verify.";
+ }
+ soa.Self()->AssertNoPendingException();
}
- soa.Self()->AssertNoPendingException();
-}
+
+ private:
+ const ParallelCompilationManager* const manager_;
+};
void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
@@ -1947,48 +1994,56 @@ void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, dex_files,
thread_pool);
- context.ForAll(0, dex_file.NumClassDefs(), VerifyClass, thread_count_);
+ VerifyClassVisitor visitor(&context);
+ context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
}
-static void SetVerifiedClass(const ParallelCompilationManager* manager, size_t class_def_index)
- LOCKS_EXCLUDED(Locks::mutator_lock_) {
- ATRACE_CALL();
- ScopedObjectAccess soa(Thread::Current());
- const DexFile& dex_file = *manager->GetDexFile();
- const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- const char* descriptor = dex_file.GetClassDescriptor(class_def);
- ClassLinker* class_linker = manager->GetClassLinker();
- jobject jclass_loader = manager->GetClassLoader();
- StackHandleScope<3> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
- Handle<mirror::Class> klass(
- hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
- // Class might have failed resolution. Then don't set it to verified.
- if (klass.Get() != nullptr) {
- // Only do this if the class is resolved. If even resolution fails, quickening will go very,
- // very wrong.
- if (klass->IsResolved()) {
- if (klass->GetStatus() < mirror::Class::kStatusVerified) {
- ObjectLock<mirror::Class> lock(soa.Self(), klass);
- // Set class status to verified.
- mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, soa.Self());
- // Mark methods as pre-verified. If we don't do this, the interpreter will run with
- // access checks.
- klass->SetPreverifiedFlagOnAllMethods(
- GetInstructionSetPointerSize(manager->GetCompiler()->GetInstructionSet()));
- klass->SetPreverified();
+class SetVerifiedClassVisitor : public CompilationVisitor {
+ public:
+ explicit SetVerifiedClassVisitor(const ParallelCompilationManager* manager) : manager_(manager) {}
+
+ virtual void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE {
+ ATRACE_CALL();
+ ScopedObjectAccess soa(Thread::Current());
+ const DexFile& dex_file = *manager_->GetDexFile();
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+ const char* descriptor = dex_file.GetClassDescriptor(class_def);
+ ClassLinker* class_linker = manager_->GetClassLinker();
+ jobject jclass_loader = manager_->GetClassLoader();
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+ Handle<mirror::Class> klass(
+ hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
+ // Class might have failed resolution. Then don't set it to verified.
+ if (klass.Get() != nullptr) {
+ // Only do this if the class is resolved. If even resolution fails, quickening will go very,
+ // very wrong.
+ if (klass->IsResolved()) {
+ if (klass->GetStatus() < mirror::Class::kStatusVerified) {
+ ObjectLock<mirror::Class> lock(soa.Self(), klass);
+ // Set class status to verified.
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, soa.Self());
+ // Mark methods as pre-verified. If we don't do this, the interpreter will run with
+ // access checks.
+ klass->SetPreverifiedFlagOnAllMethods(
+ GetInstructionSetPointerSize(manager_->GetCompiler()->GetInstructionSet()));
+ klass->SetPreverified();
+ }
+ // Record the final class status if necessary.
+ ClassReference ref(manager_->GetDexFile(), class_def_index);
+ manager_->GetCompiler()->RecordClassStatus(ref, klass->GetStatus());
}
- // Record the final class status if necessary.
- ClassReference ref(manager->GetDexFile(), class_def_index);
- manager->GetCompiler()->RecordClassStatus(ref, klass->GetStatus());
+ } else {
+ Thread* self = soa.Self();
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
}
- } else {
- Thread* self = soa.Self();
- DCHECK(self->IsExceptionPending());
- self->ClearException();
}
-}
+
+ private:
+ const ParallelCompilationManager* const manager_;
+};
void CompilerDriver::SetVerifiedDexFile(jobject class_loader, const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
@@ -1997,99 +2052,107 @@ void CompilerDriver::SetVerifiedDexFile(jobject class_loader, const DexFile& dex
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, dex_files,
thread_pool);
- context.ForAll(0, dex_file.NumClassDefs(), SetVerifiedClass, thread_count_);
+ SetVerifiedClassVisitor visitor(&context);
+ context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
}
-static void InitializeClass(const ParallelCompilationManager* manager, size_t class_def_index)
- LOCKS_EXCLUDED(Locks::mutator_lock_) {
- ATRACE_CALL();
- jobject jclass_loader = manager->GetClassLoader();
- const DexFile& dex_file = *manager->GetDexFile();
- const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- const DexFile::TypeId& class_type_id = dex_file.GetTypeId(class_def.class_idx_);
- const char* descriptor = dex_file.StringDataByIdx(class_type_id.descriptor_idx_);
+class InitializeClassVisitor : public CompilationVisitor {
+ public:
+ explicit InitializeClassVisitor(const ParallelCompilationManager* manager) : manager_(manager) {}
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<3> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
- Handle<mirror::Class> klass(
- hs.NewHandle(manager->GetClassLinker()->FindClass(soa.Self(), descriptor, class_loader)));
-
- if (klass.Get() != nullptr && !SkipClass(jclass_loader, dex_file, klass.Get())) {
- // Only try to initialize classes that were successfully verified.
- if (klass->IsVerified()) {
- // Attempt to initialize the class but bail if we either need to initialize the super-class
- // or static fields.
- manager->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, false);
- if (!klass->IsInitialized()) {
- // We don't want non-trivial class initialization occurring on multiple threads due to
- // deadlock problems. For example, a parent class is initialized (holding its lock) that
- // refers to a sub-class in its static/class initializer causing it to try to acquire the
- // sub-class' lock. While on a second thread the sub-class is initialized (holding its lock)
- // after first initializing its parents, whose locks are acquired. This leads to a
- // parent-to-child and a child-to-parent lock ordering and consequent potential deadlock.
- // We need to use an ObjectLock due to potential suspension in the interpreting code. Rather
- // than use a special Object for the purpose we use the Class of java.lang.Class.
- Handle<mirror::Class> h_klass(hs.NewHandle(klass->GetClass()));
- ObjectLock<mirror::Class> lock(soa.Self(), h_klass);
- // Attempt to initialize allowing initialization of parent classes but still not static
- // fields.
- manager->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true);
+ virtual void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE {
+ ATRACE_CALL();
+ jobject jclass_loader = manager_->GetClassLoader();
+ const DexFile& dex_file = *manager_->GetDexFile();
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+ const DexFile::TypeId& class_type_id = dex_file.GetTypeId(class_def.class_idx_);
+ const char* descriptor = dex_file.StringDataByIdx(class_type_id.descriptor_idx_);
+
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+ Handle<mirror::Class> klass(
+ hs.NewHandle(manager_->GetClassLinker()->FindClass(soa.Self(), descriptor, class_loader)));
+
+ if (klass.Get() != nullptr && !SkipClass(jclass_loader, dex_file, klass.Get())) {
+ // Only try to initialize classes that were successfully verified.
+ if (klass->IsVerified()) {
+ // Attempt to initialize the class but bail if we either need to initialize the super-class
+ // or static fields.
+ manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, false);
if (!klass->IsInitialized()) {
- // We need to initialize static fields, we only do this for image classes that aren't
- // marked with the $NoPreloadHolder (which implies this should not be initialized early).
- bool can_init_static_fields = manager->GetCompiler()->IsImage() &&
- manager->GetCompiler()->IsImageClass(descriptor) &&
- !StringPiece(descriptor).ends_with("$NoPreloadHolder;");
- if (can_init_static_fields) {
- VLOG(compiler) << "Initializing: " << descriptor;
- // TODO multithreading support. We should ensure the current compilation thread has
- // exclusive access to the runtime and the transaction. To achieve this, we could use
- // a ReaderWriterMutex but we're holding the mutator lock so we fail mutex sanity
- // checks in Thread::AssertThreadSuspensionIsAllowable.
- Runtime* const runtime = Runtime::Current();
- Transaction transaction;
-
- // Run the class initializer in transaction mode.
- runtime->EnterTransactionMode(&transaction);
- const mirror::Class::Status old_status = klass->GetStatus();
- bool success = manager->GetClassLinker()->EnsureInitialized(soa.Self(), klass, true,
- true);
- // TODO we detach transaction from runtime to indicate we quit the transactional
- // mode which prevents the GC from visiting objects modified during the transaction.
- // Ensure GC is not run so don't access freed objects when aborting transaction.
-
- ScopedAssertNoThreadSuspension ants(soa.Self(), "Transaction end");
- runtime->ExitTransactionMode();
-
- if (!success) {
- CHECK(soa.Self()->IsExceptionPending());
- mirror::Throwable* exception = soa.Self()->GetException();
- VLOG(compiler) << "Initialization of " << descriptor << " aborted because of "
- << exception->Dump();
- std::ostream* file_log = manager->GetCompiler()->
- GetCompilerOptions().GetInitFailureOutput();
- if (file_log != nullptr) {
- *file_log << descriptor << "\n";
- *file_log << exception->Dump() << "\n";
+ // We don't want non-trivial class initialization occurring on multiple threads due to
+ // deadlock problems. For example, a parent class is initialized (holding its lock) that
+ // refers to a sub-class in its static/class initializer causing it to try to acquire the
+ // sub-class' lock. While on a second thread the sub-class is initialized (holding its lock)
+ // after first initializing its parents, whose locks are acquired. This leads to a
+ // parent-to-child and a child-to-parent lock ordering and consequent potential deadlock.
+ // We need to use an ObjectLock due to potential suspension in the interpreting code. Rather
+ // than use a special Object for the purpose we use the Class of java.lang.Class.
+ Handle<mirror::Class> h_klass(hs.NewHandle(klass->GetClass()));
+ ObjectLock<mirror::Class> lock(soa.Self(), h_klass);
+ // Attempt to initialize allowing initialization of parent classes but still not static
+ // fields.
+ manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true);
+ if (!klass->IsInitialized()) {
+ // We need to initialize static fields, we only do this for image classes that aren't
+ // marked with the $NoPreloadHolder (which implies this should not be initialized early).
+ bool can_init_static_fields = manager_->GetCompiler()->IsImage() &&
+ manager_->GetCompiler()->IsImageClass(descriptor) &&
+ !StringPiece(descriptor).ends_with("$NoPreloadHolder;");
+ if (can_init_static_fields) {
+ VLOG(compiler) << "Initializing: " << descriptor;
+ // TODO multithreading support. We should ensure the current compilation thread has
+ // exclusive access to the runtime and the transaction. To achieve this, we could use
+ // a ReaderWriterMutex but we're holding the mutator lock so we fail mutex sanity
+ // checks in Thread::AssertThreadSuspensionIsAllowable.
+ Runtime* const runtime = Runtime::Current();
+ Transaction transaction;
+
+ // Run the class initializer in transaction mode.
+ runtime->EnterTransactionMode(&transaction);
+ const mirror::Class::Status old_status = klass->GetStatus();
+ bool success = manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, true,
+ true);
+ // TODO we detach transaction from runtime to indicate we quit the transactional
+ // mode which prevents the GC from visiting objects modified during the transaction.
+ // Ensure GC is not run so don't access freed objects when aborting transaction.
+
+ ScopedAssertNoThreadSuspension ants(soa.Self(), "Transaction end");
+ runtime->ExitTransactionMode();
+
+ if (!success) {
+ CHECK(soa.Self()->IsExceptionPending());
+ mirror::Throwable* exception = soa.Self()->GetException();
+ VLOG(compiler) << "Initialization of " << descriptor << " aborted because of "
+ << exception->Dump();
+ std::ostream* file_log = manager_->GetCompiler()->
+ GetCompilerOptions().GetInitFailureOutput();
+ if (file_log != nullptr) {
+ *file_log << descriptor << "\n";
+ *file_log << exception->Dump() << "\n";
+ }
+ soa.Self()->ClearException();
+ transaction.Rollback();
+ CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored";
}
- soa.Self()->ClearException();
- transaction.Rollback();
- CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored";
}
}
+ soa.Self()->AssertNoPendingException();
}
- soa.Self()->AssertNoPendingException();
}
+ // Record the final class status if necessary.
+ ClassReference ref(manager_->GetDexFile(), class_def_index);
+ manager_->GetCompiler()->RecordClassStatus(ref, klass->GetStatus());
}
- // Record the final class status if necessary.
- ClassReference ref(manager->GetDexFile(), class_def_index);
- manager->GetCompiler()->RecordClassStatus(ref, klass->GetStatus());
+ // Clear any class not found or verification exceptions.
+ soa.Self()->ClearException();
}
- // Clear any class not found or verification exceptions.
- soa.Self()->ClearException();
-}
+
+ private:
+ const ParallelCompilationManager* const manager_;
+};
void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
@@ -2105,7 +2168,8 @@ void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile&
} else {
thread_count = thread_count_;
}
- context.ForAll(0, dex_file.NumClassDefs(), InitializeClass, thread_count);
+ InitializeClassVisitor visitor(&context);
+ context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
}
void CompilerDriver::InitializeClasses(jobject class_loader,
@@ -2132,101 +2196,108 @@ void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFi
VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
}
-void CompilerDriver::CompileClass(const ParallelCompilationManager* manager,
- size_t class_def_index) {
- ATRACE_CALL();
- const DexFile& dex_file = *manager->GetDexFile();
- const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- ClassLinker* class_linker = manager->GetClassLinker();
- jobject jclass_loader = manager->GetClassLoader();
- Thread* self = Thread::Current();
- {
- // Use a scoped object access to perform to the quick SkipClass check.
- const char* descriptor = dex_file.GetClassDescriptor(class_def);
- ScopedObjectAccess soa(self);
- StackHandleScope<3> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
- Handle<mirror::Class> klass(
- hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
- if (klass.Get() == nullptr) {
- CHECK(soa.Self()->IsExceptionPending());
- soa.Self()->ClearException();
- } else if (SkipClass(jclass_loader, dex_file, klass.Get())) {
+class CompileClassVisitor : public CompilationVisitor {
+ public:
+ explicit CompileClassVisitor(const ParallelCompilationManager* manager) : manager_(manager) {}
+
+ virtual void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE {
+ ATRACE_CALL();
+ const DexFile& dex_file = *manager_->GetDexFile();
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+ ClassLinker* class_linker = manager_->GetClassLinker();
+ jobject jclass_loader = manager_->GetClassLoader();
+ Thread* self = Thread::Current();
+ {
+ // Use a scoped object access to perform to the quick SkipClass check.
+ const char* descriptor = dex_file.GetClassDescriptor(class_def);
+ ScopedObjectAccess soa(self);
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+ Handle<mirror::Class> klass(
+ hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
+ if (klass.Get() == nullptr) {
+ CHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ } else if (SkipClass(jclass_loader, dex_file, klass.Get())) {
+ return;
+ }
+ }
+ ClassReference ref(&dex_file, class_def_index);
+ // Skip compiling classes with generic verifier failures since they will still fail at runtime
+ if (manager_->GetCompiler()->verification_results_->IsClassRejected(ref)) {
+ return;
+ }
+ const uint8_t* class_data = dex_file.GetClassData(class_def);
+ if (class_data == nullptr) {
+ // empty class, probably a marker interface
return;
}
- }
- ClassReference ref(&dex_file, class_def_index);
- // Skip compiling classes with generic verifier failures since they will still fail at runtime
- if (manager->GetCompiler()->verification_results_->IsClassRejected(ref)) {
- return;
- }
- const uint8_t* class_data = dex_file.GetClassData(class_def);
- if (class_data == nullptr) {
- // empty class, probably a marker interface
- return;
- }
- CompilerDriver* const driver = manager->GetCompiler();
+ CompilerDriver* const driver = manager_->GetCompiler();
- // Can we run DEX-to-DEX compiler on this class ?
- DexToDexCompilationLevel dex_to_dex_compilation_level = kDontDexToDexCompile;
- {
- ScopedObjectAccess soa(self);
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
- dex_to_dex_compilation_level = driver->GetDexToDexCompilationlevel(
- soa.Self(), class_loader, dex_file, class_def);
- }
- ClassDataItemIterator it(dex_file, class_data);
- // Skip fields
- while (it.HasNextStaticField()) {
- it.Next();
- }
- while (it.HasNextInstanceField()) {
- it.Next();
- }
+ // Can we run DEX-to-DEX compiler on this class ?
+ DexToDexCompilationLevel dex_to_dex_compilation_level = kDontDexToDexCompile;
+ {
+ ScopedObjectAccess soa(self);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+ dex_to_dex_compilation_level = driver->GetDexToDexCompilationlevel(
+ soa.Self(), class_loader, dex_file, class_def);
+ }
+ ClassDataItemIterator it(dex_file, class_data);
+ // Skip fields
+ while (it.HasNextStaticField()) {
+ it.Next();
+ }
+ while (it.HasNextInstanceField()) {
+ it.Next();
+ }
- bool compilation_enabled = driver->IsClassToCompile(
- dex_file.StringByTypeIdx(class_def.class_idx_));
+ bool compilation_enabled = driver->IsClassToCompile(
+ dex_file.StringByTypeIdx(class_def.class_idx_));
- // Compile direct methods
- int64_t previous_direct_method_idx = -1;
- while (it.HasNextDirectMethod()) {
- uint32_t method_idx = it.GetMemberIndex();
- if (method_idx == previous_direct_method_idx) {
- // smali can create dex files with two encoded_methods sharing the same method_idx
- // http://code.google.com/p/smali/issues/detail?id=119
+ // Compile direct methods
+ int64_t previous_direct_method_idx = -1;
+ while (it.HasNextDirectMethod()) {
+ uint32_t method_idx = it.GetMemberIndex();
+ if (method_idx == previous_direct_method_idx) {
+ // smali can create dex files with two encoded_methods sharing the same method_idx
+ // http://code.google.com/p/smali/issues/detail?id=119
+ it.Next();
+ continue;
+ }
+ previous_direct_method_idx = method_idx;
+ driver->CompileMethod(self, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
+ it.GetMethodInvokeType(class_def), class_def_index,
+ method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
+ compilation_enabled);
it.Next();
- continue;
- }
- previous_direct_method_idx = method_idx;
- driver->CompileMethod(self, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
- it.GetMethodInvokeType(class_def), class_def_index,
- method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
- compilation_enabled);
- it.Next();
- }
- // Compile virtual methods
- int64_t previous_virtual_method_idx = -1;
- while (it.HasNextVirtualMethod()) {
- uint32_t method_idx = it.GetMemberIndex();
- if (method_idx == previous_virtual_method_idx) {
- // smali can create dex files with two encoded_methods sharing the same method_idx
- // http://code.google.com/p/smali/issues/detail?id=119
+ }
+ // Compile virtual methods
+ int64_t previous_virtual_method_idx = -1;
+ while (it.HasNextVirtualMethod()) {
+ uint32_t method_idx = it.GetMemberIndex();
+ if (method_idx == previous_virtual_method_idx) {
+ // smali can create dex files with two encoded_methods sharing the same method_idx
+ // http://code.google.com/p/smali/issues/detail?id=119
+ it.Next();
+ continue;
+ }
+ previous_virtual_method_idx = method_idx;
+ driver->CompileMethod(self, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
+ it.GetMethodInvokeType(class_def), class_def_index,
+ method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
+ compilation_enabled);
it.Next();
- continue;
}
- previous_virtual_method_idx = method_idx;
- driver->CompileMethod(self, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
- it.GetMethodInvokeType(class_def), class_def_index,
- method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
- compilation_enabled);
- it.Next();
+ DCHECK(!it.HasNext());
}
- DCHECK(!it.HasNext());
-}
+
+ private:
+ const ParallelCompilationManager* const manager_;
+};
void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
@@ -2234,7 +2305,8 @@ void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_fil
TimingLogger::ScopedTiming t("Compile Dex File", timings);
ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this,
&dex_file, dex_files, thread_pool);
- context.ForAll(0, dex_file.NumClassDefs(), CompilerDriver::CompileClass, thread_count_);
+ CompileClassVisitor visitor(&context);
+ context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
}
// Does the runtime for the InstructionSet provide an implementation returned by
@@ -2453,7 +2525,7 @@ bool CompilerDriver::WriteElf(const std::string& android_root,
const std::vector<const art::DexFile*>& dex_files,
OatWriter* oat_writer,
art::File* file)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
if (kProduce64BitELFFiles && Is64BitInstructionSet(GetInstructionSet())) {
return art::ElfWriterQuick64::Create(file, oat_writer, dex_files, android_root, is_host, *this);
} else {
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 5cf4044fd4..88e03a231f 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -114,14 +114,15 @@ class CompilerDriver {
void CompileAll(jobject class_loader, const std::vector<const DexFile*>& dex_files,
TimingLogger* timings)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
CompiledMethod* CompileMethod(Thread* self, ArtMethod*)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) WARN_UNUSED;
+ SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!compiled_methods_lock_) WARN_UNUSED;
// Compile a single Method.
void CompileOne(Thread* self, ArtMethod* method, TimingLogger* timings)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!compiled_methods_lock_, !compiled_classes_lock_);
VerificationResults* GetVerificationResults() const {
return verification_results_;
@@ -162,54 +163,56 @@ class CompilerDriver {
// Generate the trampolines that are invoked by unresolved direct methods.
const std::vector<uint8_t>* CreateInterpreterToInterpreterBridge() const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateInterpreterToCompiledCodeBridge() const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateJniDlsymLookup() const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateQuickGenericJniTrampoline() const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateQuickImtConflictTrampoline() const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateQuickResolutionTrampoline() const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateQuickToInterpreterBridge() const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
CompiledClass* GetCompiledClass(ClassReference ref) const
- LOCKS_EXCLUDED(compiled_classes_lock_);
+ REQUIRES(!compiled_classes_lock_);
CompiledMethod* GetCompiledMethod(MethodReference ref) const
- LOCKS_EXCLUDED(compiled_methods_lock_);
+ REQUIRES(!compiled_methods_lock_);
size_t GetNonRelativeLinkerPatchCount() const
- LOCKS_EXCLUDED(compiled_methods_lock_);
+ REQUIRES(!compiled_methods_lock_);
// Remove and delete a compiled method.
- void RemoveCompiledMethod(const MethodReference& method_ref);
+ void RemoveCompiledMethod(const MethodReference& method_ref) REQUIRES(!compiled_methods_lock_);
void AddRequiresConstructorBarrier(Thread* self, const DexFile* dex_file,
- uint16_t class_def_index);
+ uint16_t class_def_index)
+ REQUIRES(!freezing_constructor_lock_);
bool RequiresConstructorBarrier(Thread* self, const DexFile* dex_file,
- uint16_t class_def_index) const;
+ uint16_t class_def_index) const
+ REQUIRES(!freezing_constructor_lock_);
// Callbacks from compiler to see what runtime checks must be generated.
bool CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, uint32_t type_idx);
bool CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, uint32_t string_idx)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_);
// Are runtime access checks necessary in the compiled code?
bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexFile& dex_file,
uint32_t type_idx, bool* type_known_final = nullptr,
bool* type_known_abstract = nullptr,
bool* equals_referrers_class = nullptr)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_);
// Are runtime access and instantiable checks necessary in the code?
bool CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx, const DexFile& dex_file,
uint32_t type_idx)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_);
bool CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_idx,
bool* is_type_initialized, bool* use_direct_type_ptr,
@@ -223,22 +226,22 @@ class CompilerDriver {
// Get the DexCache for the
mirror::DexCache* GetDexCache(const DexCompilationUnit* mUnit)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
mirror::ClassLoader* GetClassLoader(ScopedObjectAccess& soa, const DexCompilationUnit* mUnit)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Resolve compiling method's class. Returns null on failure.
mirror::Class* ResolveCompilingMethodsClass(
const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
mirror::Class* ResolveClass(
const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader, uint16_t type_index,
const DexCompilationUnit* mUnit)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Resolve a field. Returns null on failure, including incompatible class change.
// NOTE: Unlike ClassLinker's ResolveField(), this method enforces is_static.
@@ -246,40 +249,40 @@ class CompilerDriver {
const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
uint32_t field_idx, bool is_static)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Resolve a field with a given dex file.
ArtField* ResolveFieldWithDexFile(
const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader, const DexFile* dex_file,
uint32_t field_idx, bool is_static)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Get declaration location of a resolved field.
void GetResolvedFieldDexFileLocation(
ArtField* resolved_field, const DexFile** declaring_dex_file,
uint16_t* declaring_class_idx, uint16_t* declaring_field_idx)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
- bool IsFieldVolatile(ArtField* field) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- MemberOffset GetFieldOffset(ArtField* field) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsFieldVolatile(ArtField* field) SHARED_REQUIRES(Locks::mutator_lock_);
+ MemberOffset GetFieldOffset(ArtField* field) SHARED_REQUIRES(Locks::mutator_lock_);
// Find a dex cache for a dex file.
inline mirror::DexCache* FindDexCache(const DexFile* dex_file)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Can we fast-path an IGET/IPUT access to an instance field? If yes, compute the field offset.
std::pair<bool, bool> IsFastInstanceField(
mirror::DexCache* dex_cache, mirror::Class* referrer_class,
ArtField* resolved_field, uint16_t field_idx)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Can we fast-path an SGET/SPUT access to a static field? If yes, compute the type index
// of the declaring class in the referrer's dex file.
std::pair<bool, bool> IsFastStaticField(
mirror::DexCache* dex_cache, mirror::Class* referrer_class,
ArtField* resolved_field, uint16_t field_idx, uint32_t* storage_index)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Return whether the declaring class of `resolved_method` is
// available to `referrer_class`. If this is true, compute the type
@@ -291,34 +294,34 @@ class CompilerDriver {
ArtMethod* resolved_method,
uint16_t method_idx,
uint32_t* storage_index)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Is static field's in referrer's class?
bool IsStaticFieldInReferrerClass(mirror::Class* referrer_class, ArtField* resolved_field)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Is static field's class initialized?
bool IsStaticFieldsClassInitialized(mirror::Class* referrer_class,
ArtField* resolved_field)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Resolve a method. Returns null on failure, including incompatible class change.
ArtMethod* ResolveMethod(
ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change = true)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Get declaration location of a resolved field.
void GetResolvedMethodDexFileLocation(
ArtMethod* resolved_method, const DexFile** declaring_dex_file,
uint16_t* declaring_class_idx, uint16_t* declaring_method_idx)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Get the index in the vtable of the method.
uint16_t GetResolvedMethodVTableIndex(
ArtMethod* resolved_method, InvokeType type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Can we fast-path an INVOKE? If no, returns 0. If yes, returns a non-zero opaque flags value
// for ProcessedInvoke() and computes the necessary lowering info.
@@ -328,13 +331,13 @@ class CompilerDriver {
mirror::Class* referrer_class, ArtMethod* resolved_method, InvokeType* invoke_type,
MethodReference* target_method, const MethodReference* devirt_target,
uintptr_t* direct_code, uintptr_t* direct_method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Is method's class initialized for an invoke?
// For static invokes to determine whether we need to consider potential call to <clinit>().
// For non-static invokes, assuming a non-null reference, the class is always initialized.
bool IsMethodsClassInitialized(mirror::Class* referrer_class, ArtMethod* resolved_method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Get the layout of dex cache arrays for a dex file. Returns invalid layout if the
// dex cache arrays don't have a fixed layout.
@@ -349,18 +352,18 @@ class CompilerDriver {
ArtField** resolved_field,
mirror::Class** referrer_class,
mirror::DexCache** dex_cache)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Can we fast path instance field access? Computes field's offset and volatility.
bool ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put,
MemberOffset* field_offset, bool* is_volatile)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_);
ArtField* ComputeInstanceFieldInfo(uint32_t field_idx,
const DexCompilationUnit* mUnit,
bool is_put,
const ScopedObjectAccess& soa)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Can we fastpath static field access? Computes field's offset, volatility and whether the
@@ -369,7 +372,7 @@ class CompilerDriver {
MemberOffset* field_offset, uint32_t* storage_index,
bool* is_referrers_class, bool* is_volatile, bool* is_initialized,
Primitive::Type* type)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_);
// Can we fastpath a interface, super class or virtual method call? Computes method's vtable
// index.
@@ -377,7 +380,7 @@ class CompilerDriver {
bool update_stats, bool enable_devirtualization,
InvokeType* type, MethodReference* target_method, int* vtable_idx,
uintptr_t* direct_code, uintptr_t* direct_method)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_);
const VerifiedMethod* GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const;
bool IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc);
@@ -445,7 +448,7 @@ class CompilerDriver {
bool IsMethodToCompile(const MethodReference& method_ref) const;
void RecordClassStatus(ClassReference ref, mirror::Class::Status status)
- LOCKS_EXCLUDED(compiled_classes_lock_);
+ REQUIRES(!compiled_classes_lock_);
// Checks if the specified method has been verified without failures. Returns
// false if the method is not in the verification results (GetVerificationResults).
@@ -487,7 +490,7 @@ class CompilerDriver {
ArtMember* resolved_member,
uint16_t member_idx,
uint32_t* storage_index)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Can `referrer_class` access the resolved `member`?
// Dispatch call to mirror::Class::CanAccessResolvedField or
@@ -499,17 +502,17 @@ class CompilerDriver {
ArtMember* member,
mirror::DexCache* dex_cache,
uint32_t field_idx)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Can we assume that the klass is initialized?
bool CanAssumeClassIsInitialized(mirror::Class* klass)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
bool CanReferrerAssumeClassIsInitialized(mirror::Class* referrer_class, mirror::Class* klass)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Can we assume that the klass is loaded?
bool CanAssumeClassIsLoaded(mirror::Class* klass)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// These flags are internal to CompilerDriver for collecting INVOKE resolution statistics.
// The only external contract is that unresolved method has flags 0 and resolved non-0.
@@ -540,71 +543,68 @@ class CompilerDriver {
/*out*/int* stats_flags,
MethodReference* target_method,
uintptr_t* direct_code, uintptr_t* direct_method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
private:
DexToDexCompilationLevel GetDexToDexCompilationlevel(
Thread* self, Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file,
- const DexFile::ClassDef& class_def) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const DexFile::ClassDef& class_def) SHARED_REQUIRES(Locks::mutator_lock_);
void PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
- void LoadImageClasses(TimingLogger* timings);
+ void LoadImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
// Attempt to resolve all type, methods, fields, and strings
// referenced from code in the dex file following PathClassLoader
// ordering semantics.
void Resolve(jobject class_loader, const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_);
void ResolveDexFile(jobject class_loader, const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_);
void Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings);
void VerifyDexFile(jobject class_loader, const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_);
void SetVerified(jobject class_loader, const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings);
void SetVerifiedDexFile(jobject class_loader, const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_);
void InitializeClasses(jobject class_loader, const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
void InitializeClasses(jobject class_loader, const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings)
- LOCKS_EXCLUDED(Locks::mutator_lock_, compiled_classes_lock_);
+ REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
- void UpdateImageClasses(TimingLogger* timings) LOCKS_EXCLUDED(Locks::mutator_lock_);
+ void UpdateImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
static void FindClinitImageClassesCallback(mirror::Object* object, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
void Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings);
void CompileDexFile(jobject class_loader, const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool, TimingLogger* timings)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_);
void CompileMethod(Thread* self, const DexFile::CodeItem* code_item, uint32_t access_flags,
InvokeType invoke_type, uint16_t class_def_idx, uint32_t method_idx,
jobject class_loader, const DexFile& dex_file,
DexToDexCompilationLevel dex_to_dex_compilation_level,
bool compilation_enabled)
- LOCKS_EXCLUDED(compiled_methods_lock_);
-
- static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!compiled_methods_lock_);
// Swap pool and allocator used for native allocations. May be file-backed. Needs to be first
// as other fields rely on this.
@@ -776,6 +776,7 @@ class CompilerDriver {
DedupeSet<ArrayRef<const uint8_t>,
SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_cfi_info_;
+ friend class CompileClassVisitor;
DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
};
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index b358f4f396..e35d07da83 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -37,7 +37,7 @@ namespace art {
class CompilerDriverTest : public CommonCompilerTest {
protected:
- void CompileAll(jobject class_loader) LOCKS_EXCLUDED(Locks::mutator_lock_) {
+ void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
TimingLogger timings("CompilerDriverTest::CompileAll", false, false);
TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
compiler_driver_->CompileAll(class_loader,
@@ -49,7 +49,7 @@ class CompilerDriverTest : public CommonCompilerTest {
void EnsureCompiled(jobject class_loader, const char* class_name, const char* method,
const char* signature, bool is_virtual)
- LOCKS_EXCLUDED(Locks::mutator_lock_) {
+ REQUIRES(!Locks::mutator_lock_) {
CompileAll(class_loader);
Thread::Current()->TransitionFromSuspendedToRunnable();
bool started = runtime_->Start();
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 226e6b7952..3f5a1eabb6 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -27,6 +27,8 @@ CompilerOptions::CompilerOptions()
small_method_threshold_(kDefaultSmallMethodThreshold),
tiny_method_threshold_(kDefaultTinyMethodThreshold),
num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold),
+ inline_depth_limit_(kDefaultInlineDepthLimit),
+ inline_max_code_units_(kDefaultInlineMaxCodeUnits),
include_patch_information_(kDefaultIncludePatchInformation),
top_k_profile_threshold_(kDefaultTopKProfileThreshold),
debuggable_(false),
@@ -52,6 +54,8 @@ CompilerOptions::CompilerOptions(CompilerFilter compiler_filter,
size_t small_method_threshold,
size_t tiny_method_threshold,
size_t num_dex_methods_threshold,
+ size_t inline_depth_limit,
+ size_t inline_max_code_units,
bool include_patch_information,
double top_k_profile_threshold,
bool debuggable,
@@ -71,6 +75,8 @@ CompilerOptions::CompilerOptions(CompilerFilter compiler_filter,
small_method_threshold_(small_method_threshold),
tiny_method_threshold_(tiny_method_threshold),
num_dex_methods_threshold_(num_dex_methods_threshold),
+ inline_depth_limit_(inline_depth_limit),
+ inline_max_code_units_(inline_max_code_units),
include_patch_information_(include_patch_information),
top_k_profile_threshold_(top_k_profile_threshold),
debuggable_(debuggable),
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index fe681e2a53..17b19dd51e 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -51,6 +51,8 @@ class CompilerOptions FINAL {
static constexpr double kDefaultTopKProfileThreshold = 90.0;
static const bool kDefaultGenerateDebugInfo = kIsDebugBuild;
static const bool kDefaultIncludePatchInformation = false;
+ static const size_t kDefaultInlineDepthLimit = 3;
+ static const size_t kDefaultInlineMaxCodeUnits = 18;
CompilerOptions();
~CompilerOptions();
@@ -61,6 +63,8 @@ class CompilerOptions FINAL {
size_t small_method_threshold,
size_t tiny_method_threshold,
size_t num_dex_methods_threshold,
+ size_t inline_depth_limit,
+ size_t inline_max_code_units,
bool include_patch_information,
double top_k_profile_threshold,
bool debuggable,
@@ -137,6 +141,14 @@ class CompilerOptions FINAL {
return num_dex_methods_threshold_;
}
+ size_t GetInlineDepthLimit() const {
+ return inline_depth_limit_;
+ }
+
+ size_t GetInlineMaxCodeUnits() const {
+ return inline_max_code_units_;
+ }
+
double GetTopKProfileThreshold() const {
return top_k_profile_threshold_;
}
@@ -202,6 +214,8 @@ class CompilerOptions FINAL {
const size_t small_method_threshold_;
const size_t tiny_method_threshold_;
const size_t num_dex_methods_threshold_;
+ const size_t inline_depth_limit_;
+ const size_t inline_max_code_units_;
const bool include_patch_information_;
// When using a profile file only the top K% of the profiled samples will be compiled.
const double top_k_profile_threshold_;
diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc
index 4d423d007f..a07d27c1d2 100644
--- a/compiler/dwarf/dwarf_test.cc
+++ b/compiler/dwarf/dwarf_test.cc
@@ -27,7 +27,7 @@ namespace art {
namespace dwarf {
// Run the tests only on host since we need objdump.
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
constexpr CFIFormat kCFIFormat = DW_DEBUG_FRAME_FORMAT;
@@ -336,7 +336,7 @@ TEST_F(DwarfTest, DebugInfo) {
CheckObjdumpOutput(is64bit, "-W");
}
-#endif // HAVE_ANDROID_OS
+#endif // __ANDROID__
} // namespace dwarf
} // namespace art
diff --git a/compiler/elf_writer.h b/compiler/elf_writer.h
index 8e13b51bbe..03f8ceb306 100644
--- a/compiler/elf_writer.h
+++ b/compiler/elf_writer.h
@@ -57,7 +57,7 @@ class ElfWriter {
const std::vector<const DexFile*>& dex_files,
const std::string& android_root,
bool is_host)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
+ SHARED_REQUIRES(Locks::mutator_lock_) = 0;
const CompilerDriver* const compiler_driver_;
File* const elf_file_;
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
index fd202eeb5f..83781abeff 100644
--- a/compiler/elf_writer_quick.h
+++ b/compiler/elf_writer_quick.h
@@ -33,7 +33,7 @@ class ElfWriterQuick FINAL : public ElfWriter {
const std::string& android_root,
bool is_host,
const CompilerDriver& driver)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
static void EncodeOatPatches(const std::vector<uintptr_t>& locations,
std::vector<uint8_t>* buffer);
@@ -44,7 +44,7 @@ class ElfWriterQuick FINAL : public ElfWriter {
const std::string& android_root,
bool is_host)
OVERRIDE
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
private:
ElfWriterQuick(const CompilerDriver& driver, File* elf_file)
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 2b65aa9337..17d75a3cd3 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -73,7 +73,7 @@ static constexpr bool kBinObjects = true;
static constexpr bool kComputeEagerResolvedStrings = false;
static void CheckNoDexObjectsCallback(Object* obj, void* arg ATTRIBUTE_UNUSED)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
Class* klass = obj->GetClass();
CHECK_NE(PrettyClass(klass), "com.android.dex.Dex");
}
@@ -141,7 +141,7 @@ bool ImageWriter::Write(const std::string& image_filename,
return false;
}
std::string error_msg;
- oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_location, nullptr, &error_msg);
+ oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_location, nullptr, outof(error_msg));
if (oat_file_ == nullptr) {
PLOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location
<< ": " << error_msg;
@@ -539,16 +539,19 @@ bool ImageWriter::AllocMemory() {
return true;
}
+class ComputeLazyFieldsForClassesVisitor : public ClassVisitor {
+ public:
+ bool Visit(Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ StackHandleScope<1> hs(Thread::Current());
+ mirror::Class::ComputeName(hs.NewHandle(c));
+ return true;
+ }
+};
+
void ImageWriter::ComputeLazyFieldsForImageClasses() {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- class_linker->VisitClassesWithoutClassesLock(ComputeLazyFieldsForClassesVisitor, nullptr);
-}
-
-bool ImageWriter::ComputeLazyFieldsForClassesVisitor(Class* c, void* /*arg*/) {
- Thread* self = Thread::Current();
- StackHandleScope<1> hs(self);
- mirror::Class::ComputeName(hs.NewHandle(c));
- return true;
+ ComputeLazyFieldsForClassesVisitor visitor;
+ class_linker->VisitClassesWithoutClassesLock(&visitor);
}
void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg ATTRIBUTE_UNUSED) {
@@ -592,9 +595,20 @@ bool ImageWriter::IsImageClass(Class* klass) {
return compiler_driver_.IsImageClass(klass->GetDescriptor(&temp));
}
-struct NonImageClasses {
- ImageWriter* image_writer;
- std::set<std::string>* non_image_classes;
+class NonImageClassesVisitor : public ClassVisitor {
+ public:
+ explicit NonImageClassesVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {}
+
+ bool Visit(Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (!image_writer_->IsImageClass(klass)) {
+ std::string temp;
+ non_image_classes_.insert(klass->GetDescriptor(&temp));
+ }
+ return true;
+ }
+
+ std::set<std::string> non_image_classes_;
+ ImageWriter* const image_writer_;
};
void ImageWriter::PruneNonImageClasses() {
@@ -606,14 +620,11 @@ void ImageWriter::PruneNonImageClasses() {
Thread* self = Thread::Current();
// Make a list of classes we would like to prune.
- std::set<std::string> non_image_classes;
- NonImageClasses context;
- context.image_writer = this;
- context.non_image_classes = &non_image_classes;
- class_linker->VisitClasses(NonImageClassesVisitor, &context);
+ NonImageClassesVisitor visitor(this);
+ class_linker->VisitClasses(&visitor);
// Remove the undesired classes from the class roots.
- for (const std::string& it : non_image_classes) {
+ for (const std::string& it : visitor.non_image_classes_) {
bool result = class_linker->RemoveClass(it.c_str(), nullptr);
DCHECK(result);
}
@@ -669,15 +680,6 @@ void ImageWriter::PruneNonImageClasses() {
class_linker->DropFindArrayClassCache();
}
-bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) {
- NonImageClasses* context = reinterpret_cast<NonImageClasses*>(arg);
- if (!context->image_writer->IsImageClass(klass)) {
- std::string temp;
- context->non_image_classes->insert(klass->GetDescriptor(&temp));
- }
- return true;
-}
-
void ImageWriter::CheckNonImageClassesRemoved() {
if (compiler_driver_.GetImageClasses() != nullptr) {
gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -1035,7 +1037,7 @@ class FixupRootVisitor : public RootVisitor {
}
void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED)
- OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
for (size_t i = 0; i < count; ++i) {
*roots[i] = ImageAddress(*roots[i]);
}
@@ -1043,7 +1045,7 @@ class FixupRootVisitor : public RootVisitor {
void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
const RootInfo& info ATTRIBUTE_UNUSED)
- OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
for (size_t i = 0; i < count; ++i) {
roots[i]->Assign(ImageAddress(roots[i]->AsMirrorPtr()));
}
@@ -1052,7 +1054,7 @@ class FixupRootVisitor : public RootVisitor {
private:
ImageWriter* const image_writer_;
- mirror::Object* ImageAddress(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Object* ImageAddress(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_) {
const size_t offset = image_writer_->GetImageOffset(obj);
auto* const dest = reinterpret_cast<Object*>(image_writer_->image_begin_ + offset);
VLOG(compiler) << "Update root from " << obj << " to " << dest;
@@ -1189,8 +1191,15 @@ class FixupVisitor {
FixupVisitor(ImageWriter* image_writer, Object* copy) : image_writer_(image_writer), copy_(copy) {
}
+ // Ignore class roots since we don't have a way to map them to the destination. These are handled
+ // with other logic.
+ void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED)
+ const {}
+ void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
+
+
void operator()(Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
- EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
+ REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
Object* ref = obj->GetFieldObject<Object, kVerifyNone>(offset);
// Use SetFieldObjectWithoutWriteBarrier to avoid card marking since we are writing to the
// image.
@@ -1200,8 +1209,7 @@ class FixupVisitor {
// java.lang.ref.Reference visitor.
void operator()(mirror::Class* klass ATTRIBUTE_UNUSED, mirror::Reference* ref) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
copy_->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(
mirror::Reference::ReferentOffset(), image_writer_->GetImageAddress(ref->GetReferent()));
}
@@ -1217,15 +1225,14 @@ class FixupClassVisitor FINAL : public FixupVisitor {
}
void operator()(Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
- EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
+ REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
DCHECK(obj->IsClass());
FixupVisitor::operator()(obj, offset, /*is_static*/false);
}
void operator()(mirror::Class* klass ATTRIBUTE_UNUSED,
mirror::Reference* ref ATTRIBUTE_UNUSED) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
LOG(FATAL) << "Reference not expected here.";
}
};
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 1523383657..cabd918354 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -69,15 +69,15 @@ class ImageWriter FINAL {
}
template <typename T>
- T* GetImageAddress(T* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ T* GetImageAddress(T* object) const SHARED_REQUIRES(Locks::mutator_lock_) {
return object == nullptr ? nullptr :
reinterpret_cast<T*>(image_begin_ + GetImageOffset(object));
}
- ArtMethod* GetImageMethodAddress(ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ArtMethod* GetImageMethodAddress(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
mirror::HeapReference<mirror::Object>* GetDexCacheArrayElementImageAddress(
- const DexFile* dex_file, uint32_t offset) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const DexFile* dex_file, uint32_t offset) const SHARED_REQUIRES(Locks::mutator_lock_) {
auto it = dex_cache_array_starts_.find(dex_file);
DCHECK(it != dex_cache_array_starts_.end());
return reinterpret_cast<mirror::HeapReference<mirror::Object>*>(
@@ -88,7 +88,7 @@ class ImageWriter FINAL {
bool Write(const std::string& image_filename, const std::string& oat_filename,
const std::string& oat_location)
- LOCKS_EXCLUDED(Locks::mutator_lock_);
+ REQUIRES(!Locks::mutator_lock_);
uintptr_t GetOatDataBegin() {
return reinterpret_cast<uintptr_t>(oat_data_begin_);
@@ -98,7 +98,7 @@ class ImageWriter FINAL {
bool AllocMemory();
// Mark the objects defined in this space in the given live bitmap.
- void RecordImageAllocations() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void RecordImageAllocations() SHARED_REQUIRES(Locks::mutator_lock_);
// Classify different kinds of bins that objects end up getting packed into during image writing.
enum Bin {
@@ -165,32 +165,32 @@ class ImageWriter FINAL {
// We use the lock word to store the offset of the object in the image.
void AssignImageOffset(mirror::Object* object, BinSlot bin_slot)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
void SetImageOffset(mirror::Object* object, size_t offset)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
bool IsImageOffsetAssigned(mirror::Object* object) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- size_t GetImageOffset(mirror::Object* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
+ size_t GetImageOffset(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_);
void UpdateImageOffset(mirror::Object* obj, uintptr_t offset)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
- void PrepareDexCacheArraySlots() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void AssignImageBinSlot(mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void PrepareDexCacheArraySlots() SHARED_REQUIRES(Locks::mutator_lock_);
+ void AssignImageBinSlot(mirror::Object* object) SHARED_REQUIRES(Locks::mutator_lock_);
void SetImageBinSlot(mirror::Object* object, BinSlot bin_slot)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
bool IsImageBinSlotAssigned(mirror::Object* object) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- BinSlot GetImageBinSlot(mirror::Object* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
+ BinSlot GetImageBinSlot(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_);
- void AddMethodPointerArray(mirror::PointerArray* arr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void AddMethodPointerArray(mirror::PointerArray* arr) SHARED_REQUIRES(Locks::mutator_lock_);
static void* GetImageAddressCallback(void* writer, mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
return reinterpret_cast<ImageWriter*>(writer)->GetImageAddress(obj);
}
mirror::Object* GetLocalAddress(mirror::Object* object) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
size_t offset = GetImageOffset(object);
uint8_t* dst = image_->Begin() + offset;
return reinterpret_cast<mirror::Object*>(dst);
@@ -209,74 +209,70 @@ class ImageWriter FINAL {
}
// Returns true if the class was in the original requested image classes list.
- bool IsImageClass(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsImageClass(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_);
// Debug aid that list of requested image classes.
void DumpImageClasses();
// Preinitializes some otherwise lazy fields (such as Class name) to avoid runtime image dirtying.
void ComputeLazyFieldsForImageClasses()
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static bool ComputeLazyFieldsForClassesVisitor(mirror::Class* klass, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Wire dex cache resolved strings to strings in the image to avoid runtime resolution.
- void ComputeEagerResolvedStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void ComputeEagerResolvedStrings() SHARED_REQUIRES(Locks::mutator_lock_);
static void ComputeEagerResolvedStringsCallback(mirror::Object* obj, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Remove unwanted classes from various roots.
- void PruneNonImageClasses() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static bool NonImageClassesVisitor(mirror::Class* c, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void PruneNonImageClasses() SHARED_REQUIRES(Locks::mutator_lock_);
// Verify unwanted classes removed.
- void CheckNonImageClassesRemoved() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void CheckNonImageClassesRemoved() SHARED_REQUIRES(Locks::mutator_lock_);
static void CheckNonImageClassesRemovedCallback(mirror::Object* obj, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Lays out where the image objects will be at runtime.
void CalculateNewObjectOffsets()
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
void CreateHeader(size_t oat_loaded_size, size_t oat_data_offset)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
mirror::ObjectArray<mirror::Object>* CreateImageRoots() const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
void CalculateObjectBinSlots(mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
void UnbinObjectsIntoOffset(mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
void WalkInstanceFields(mirror::Object* obj, mirror::Class* klass)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
void WalkFieldsInOrder(mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
static void WalkFieldsCallback(mirror::Object* obj, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
static void UnbinObjectsIntoOffsetCallback(mirror::Object* obj, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Creates the contiguous image in memory and adjusts pointers.
- void CopyAndFixupNativeData() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void CopyAndFixupObjects() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void CopyAndFixupNativeData() SHARED_REQUIRES(Locks::mutator_lock_);
+ void CopyAndFixupObjects() SHARED_REQUIRES(Locks::mutator_lock_);
static void CopyAndFixupObjectsCallback(mirror::Object* obj, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void CopyAndFixupObject(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
+ void CopyAndFixupObject(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
void CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
void FixupClass(mirror::Class* orig, mirror::Class* copy)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
void FixupObject(mirror::Object* orig, mirror::Object* copy)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
void FixupPointerArray(mirror::Object* dst, mirror::PointerArray* arr, mirror::Class* klass,
- Bin array_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ Bin array_type) SHARED_REQUIRES(Locks::mutator_lock_);
// Get quick code for non-resolution/imt_conflict/abstract method.
const uint8_t* GetQuickCode(ArtMethod* method, bool* quick_is_interpreted)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
const uint8_t* GetQuickEntryPoint(ArtMethod* method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Patches references in OatFile to expect runtime addresses.
void SetOatChecksumFromElfFile(File* elf_file);
@@ -285,10 +281,10 @@ class ImageWriter FINAL {
size_t GetBinSizeSum(Bin up_to = kBinSize) const;
// Return true if a method is likely to be dirtied at runtime.
- bool WillMethodBeDirty(ArtMethod* m) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool WillMethodBeDirty(ArtMethod* m) const SHARED_REQUIRES(Locks::mutator_lock_);
// Assign the offset for an ArtMethod.
- void AssignMethodOffset(ArtMethod* method, Bin bin) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void AssignMethodOffset(ArtMethod* method, Bin bin) SHARED_REQUIRES(Locks::mutator_lock_);
const CompilerDriver& compiler_driver_;
@@ -376,6 +372,7 @@ class ImageWriter FINAL {
friend class FixupClassVisitor;
friend class FixupRootVisitor;
friend class FixupVisitor;
+ friend class NonImageClassesVisitor;
DISALLOW_COPY_AND_ASSIGN(ImageWriter);
};
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index a122cebf50..c95bac24fd 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -55,7 +55,7 @@ extern "C" void jit_unload(void* handle) {
}
extern "C" bool jit_compile_method(void* handle, ArtMethod* method, Thread* self)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
auto* jit_compiler = reinterpret_cast<JitCompiler*>(handle);
DCHECK(jit_compiler != nullptr);
return jit_compiler->CompileMethod(self, method);
@@ -71,6 +71,8 @@ JitCompiler::JitCompiler() : total_time_(0) {
CompilerOptions::kDefaultSmallMethodThreshold,
CompilerOptions::kDefaultTinyMethodThreshold,
CompilerOptions::kDefaultNumDexMethodsThreshold,
+ CompilerOptions::kDefaultInlineDepthLimit,
+ CompilerOptions::kDefaultInlineMaxCodeUnits,
/* include_patch_information */ false,
CompilerOptions::kDefaultTopKProfileThreshold,
Runtime::Current()->IsDebuggable(),
diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h
index b0010e0eb2..ef68caa5fa 100644
--- a/compiler/jit/jit_compiler.h
+++ b/compiler/jit/jit_compiler.h
@@ -38,11 +38,11 @@ class JitCompiler {
static JitCompiler* Create();
virtual ~JitCompiler();
bool CompileMethod(Thread* self, ArtMethod* method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
// This is in the compiler since the runtime doesn't have access to the compiled method
// structures.
bool AddToCodeCache(ArtMethod* method, const CompiledMethod* compiled_method,
- OatFile::OatMethod* out_method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ OatFile::OatMethod* out_method) SHARED_REQUIRES(Locks::mutator_lock_);
CompilerCallbacks* GetCompilerCallbacks() const;
size_t GetTotalCompileTime() const {
return total_time_;
@@ -63,7 +63,7 @@ class JitCompiler {
const CompiledMethod* compiled_method, uint8_t* reserve_begin, uint8_t* reserve_end,
const uint8_t* mapping_table, const uint8_t* vmap_table, const uint8_t* gc_map);
bool MakeExecutable(CompiledMethod* compiled_method, ArtMethod* method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
DISALLOW_COPY_AND_ASSIGN(JitCompiler);
};
diff --git a/compiler/jni/jni_cfi_test.cc b/compiler/jni/jni_cfi_test.cc
index 016f28ef1e..0bfe8a276a 100644
--- a/compiler/jni/jni_cfi_test.cc
+++ b/compiler/jni/jni_cfi_test.cc
@@ -28,7 +28,7 @@
namespace art {
// Run the tests only on host.
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
class JNICFITest : public CFITest {
public:
@@ -88,6 +88,6 @@ TEST_ISA(kX86_64)
TEST_ISA(kMips)
TEST_ISA(kMips64)
-#endif // HAVE_ANDROID_OS
+#endif // __ANDROID__
} // namespace art
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 074775633f..05a33d7a7f 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -16,6 +16,7 @@
#include "arch/instruction_set_features.h"
#include "art_method-inl.h"
+#include "base/out.h"
#include "class_linker.h"
#include "common_compiler_test.h"
#include "compiled_method.h"
@@ -44,7 +45,7 @@ class OatTest : public CommonCompilerTest {
void CheckMethod(ArtMethod* method,
const OatFile::OatMethod& oat_method,
const DexFile& dex_file)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
const CompiledMethod* compiled_method =
compiler_driver_->GetCompiledMethod(MethodReference(&dex_file,
method->GetDexMethodIndex()));
@@ -83,7 +84,7 @@ TEST_F(OatTest, WriteRead) {
std::string error_msg;
std::unique_ptr<const InstructionSetFeatures> insn_features(
- InstructionSetFeatures::FromVariant(insn_set, "default", &error_msg));
+ InstructionSetFeatures::FromVariant(insn_set, "default", outof(error_msg)));
ASSERT_TRUE(insn_features.get() != nullptr) << error_msg;
compiler_options_.reset(new CompilerOptions);
verification_results_.reset(new VerificationResults(compiler_options_.get()));
@@ -123,7 +124,7 @@ TEST_F(OatTest, WriteRead) {
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
}
std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), nullptr,
- nullptr, false, nullptr, &error_msg));
+ nullptr, false, nullptr, outof(error_msg)));
ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
const OatHeader& oat_header = oat_file->GetOatHeader();
ASSERT_TRUE(oat_header.IsValid());
@@ -183,14 +184,14 @@ TEST_F(OatTest, OatHeaderSizeCheck) {
EXPECT_EQ(72U, sizeof(OatHeader));
EXPECT_EQ(4U, sizeof(OatMethodOffsets));
EXPECT_EQ(28U, sizeof(OatQuickMethodHeader));
- EXPECT_EQ(112 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
+ EXPECT_EQ(113 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
}
TEST_F(OatTest, OatHeaderIsValid) {
InstructionSet insn_set = kX86;
std::string error_msg;
std::unique_ptr<const InstructionSetFeatures> insn_features(
- InstructionSetFeatures::FromVariant(insn_set, "default", &error_msg));
+ InstructionSetFeatures::FromVariant(insn_set, "default", outof(error_msg)));
ASSERT_TRUE(insn_features.get() != nullptr) << error_msg;
std::vector<const DexFile*> dex_files;
uint32_t image_file_location_oat_checksum = 0;
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 4318ea5b6c..64e748776d 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -365,7 +365,7 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
}
bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
@@ -560,7 +560,7 @@ class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor {
}
bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it ATTRIBUTE_UNUSED)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
@@ -601,7 +601,7 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
}
bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
@@ -665,7 +665,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
}
bool StartClass(const DexFile* dex_file, size_t class_def_index)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
OatDexMethodVisitor::StartClass(dex_file, class_def_index);
if (dex_cache_ == nullptr || dex_cache_->GetDexFile() != dex_file) {
dex_cache_ = class_linker_->FindDexCache(*dex_file);
@@ -673,7 +673,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
return true;
}
- bool EndClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool EndClass() SHARED_REQUIRES(Locks::mutator_lock_) {
bool result = OatDexMethodVisitor::EndClass();
if (oat_class_index_ == writer_->oat_classes_.size()) {
DCHECK(result); // OatDexMethodVisitor::EndClass() never fails.
@@ -687,7 +687,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
}
bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
@@ -793,7 +793,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
}
ArtMethod* GetTargetMethod(const LinkerPatch& patch)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
MethodReference ref = patch.TargetMethod();
mirror::DexCache* dex_cache =
(dex_file_ == ref.dex_file) ? dex_cache_ : class_linker_->FindDexCache(*ref.dex_file);
@@ -803,7 +803,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
return method;
}
- uint32_t GetTargetOffset(const LinkerPatch& patch) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ uint32_t GetTargetOffset(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
auto target_it = writer_->method_offset_map_.map.find(patch.TargetMethod());
uint32_t target_offset =
(target_it != writer_->method_offset_map_.map.end()) ? target_it->second : 0u;
@@ -828,7 +828,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
}
mirror::Class* GetTargetType(const LinkerPatch& patch)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
mirror::DexCache* dex_cache = (dex_file_ == patch.TargetTypeDexFile())
? dex_cache_ : class_linker_->FindDexCache(*patch.TargetTypeDexFile());
mirror::Class* type = dex_cache->GetResolvedType(patch.TargetTypeIndex());
@@ -836,7 +836,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
return type;
}
- uint32_t GetDexCacheOffset(const LinkerPatch& patch) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ uint32_t GetDexCacheOffset(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
if (writer_->image_writer_ != nullptr) {
auto* element = writer_->image_writer_->GetDexCacheArrayElementImageAddress(
patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset());
@@ -849,7 +849,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
}
void PatchObjectAddress(std::vector<uint8_t>* code, uint32_t offset, mirror::Object* object)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
// NOTE: Direct method pointers across oat files don't use linker patches. However, direct
// type pointers across oat files do. (TODO: Investigate why.)
if (writer_->image_writer_ != nullptr) {
@@ -865,7 +865,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
}
void PatchMethodAddress(std::vector<uint8_t>* code, uint32_t offset, ArtMethod* method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
// NOTE: Direct method pointers across oat files don't use linker patches. However, direct
// type pointers across oat files do. (TODO: Investigate why.)
if (writer_->image_writer_ != nullptr) {
@@ -882,7 +882,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
}
void PatchCodeAddress(std::vector<uint8_t>* code, uint32_t offset, uint32_t target_offset)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
uint32_t address = writer_->image_writer_ == nullptr ? target_offset :
PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() +
writer_->oat_data_offset_ + target_offset);
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 82b9377c07..3baf43872e 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -165,9 +165,9 @@ class OatWriter {
size_t InitOatClasses(size_t offset);
size_t InitOatMaps(size_t offset);
size_t InitOatCode(size_t offset)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
size_t InitOatCodeDexFiles(size_t offset)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
bool WriteTables(OutputStream* out, const size_t file_offset);
size_t WriteMaps(OutputStream* out, const size_t file_offset, size_t relative_offset);
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 52a3a1534a..88414980b8 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -357,9 +357,10 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item)
HInstruction* first_insn = block->GetFirstInstruction();
if (first_insn->IsLoadException()) {
// Catch block starts with a LoadException. Split the block after the
- // StoreLocal that must come after the load.
+ // StoreLocal and ClearException which must come after the load.
DCHECK(first_insn->GetNext()->IsStoreLocal());
- block = block->SplitBefore(first_insn->GetNext()->GetNext());
+ DCHECK(first_insn->GetNext()->GetNext()->IsClearException());
+ block = block->SplitBefore(first_insn->GetNext()->GetNext()->GetNext());
} else {
// Catch block does not load the exception. Split at the beginning to
// create an empty catch block.
@@ -2552,6 +2553,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
case Instruction::MOVE_EXCEPTION: {
current_block_->AddInstruction(new (arena_) HLoadException());
UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction());
+ current_block_->AddInstruction(new (arena_) HClearException());
break;
}
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 75b8f068ab..b0a4ce2b60 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -4290,6 +4290,10 @@ void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) {
__ Bind(slow_path->GetExitLabel());
}
+static int32_t GetExceptionTlsOffset() {
+ return Thread::ExceptionOffset<kArmWordSize>().Int32Value();
+}
+
void LocationsBuilderARM::VisitLoadException(HLoadException* load) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
@@ -4298,10 +4302,16 @@ void LocationsBuilderARM::VisitLoadException(HLoadException* load) {
void InstructionCodeGeneratorARM::VisitLoadException(HLoadException* load) {
Register out = load->GetLocations()->Out().AsRegister<Register>();
- int32_t offset = Thread::ExceptionOffset<kArmWordSize>().Int32Value();
- __ LoadFromOffset(kLoadWord, out, TR, offset);
+ __ LoadFromOffset(kLoadWord, out, TR, GetExceptionTlsOffset());
+}
+
+void LocationsBuilderARM::VisitClearException(HClearException* clear) {
+ new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
+}
+
+void InstructionCodeGeneratorARM::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
__ LoadImmediate(IP, 0);
- __ StoreToOffset(kStoreWord, IP, TR, offset);
+ __ StoreToOffset(kStoreWord, IP, TR, GetExceptionTlsOffset());
}
void LocationsBuilderARM::VisitThrow(HThrow* instruction) {
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 11de4ee673..bbde7e8efd 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2496,6 +2496,10 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) {
}
}
+static MemOperand GetExceptionTlsAddress() {
+ return MemOperand(tr, Thread::ExceptionOffset<kArm64WordSize>().Int32Value());
+}
+
void LocationsBuilderARM64::VisitLoadException(HLoadException* load) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
@@ -2503,9 +2507,15 @@ void LocationsBuilderARM64::VisitLoadException(HLoadException* load) {
}
void InstructionCodeGeneratorARM64::VisitLoadException(HLoadException* instruction) {
- MemOperand exception = MemOperand(tr, Thread::ExceptionOffset<kArm64WordSize>().Int32Value());
- __ Ldr(OutputRegister(instruction), exception);
- __ Str(wzr, exception);
+ __ Ldr(OutputRegister(instruction), GetExceptionTlsAddress());
+}
+
+void LocationsBuilderARM64::VisitClearException(HClearException* clear) {
+ new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
+}
+
+void InstructionCodeGeneratorARM64::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
+ __ Str(wzr, GetExceptionTlsAddress());
}
void LocationsBuilderARM64::VisitLoadLocal(HLoadLocal* load) {
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index e7d2ec6341..a5bad654c9 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -2544,6 +2544,10 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) {
}
}
+static int32_t GetExceptionTlsOffset() {
+ return Thread::ExceptionOffset<kMips64WordSize>().Int32Value();
+}
+
void LocationsBuilderMIPS64::VisitLoadException(HLoadException* load) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
@@ -2552,8 +2556,15 @@ void LocationsBuilderMIPS64::VisitLoadException(HLoadException* load) {
void InstructionCodeGeneratorMIPS64::VisitLoadException(HLoadException* load) {
GpuRegister out = load->GetLocations()->Out().AsRegister<GpuRegister>();
- __ LoadFromOffset(kLoadUnsignedWord, out, TR, Thread::ExceptionOffset<kMips64WordSize>().Int32Value());
- __ StoreToOffset(kStoreWord, ZERO, TR, Thread::ExceptionOffset<kMips64WordSize>().Int32Value());
+ __ LoadFromOffset(kLoadUnsignedWord, out, TR, GetExceptionTlsOffset());
+}
+
+void LocationsBuilderMIPS64::VisitClearException(HClearException* clear) {
+ new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
+ __ StoreToOffset(kStoreWord, ZERO, TR, GetExceptionTlsOffset());
}
void LocationsBuilderMIPS64::VisitLoadLocal(HLoadLocal* load) {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index e15eff9056..fdef06b116 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -2348,7 +2348,12 @@ void LocationsBuilderX86::VisitMul(HMul* mul) {
case Primitive::kPrimInt:
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
- locations->SetOut(Location::SameAsFirstInput());
+ if (mul->InputAt(1)->IsIntConstant()) {
+ // Can use 3 operand multiply.
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ } else {
+ locations->SetOut(Location::SameAsFirstInput());
+ }
break;
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
@@ -2376,21 +2381,24 @@ void InstructionCodeGeneratorX86::VisitMul(HMul* mul) {
LocationSummary* locations = mul->GetLocations();
Location first = locations->InAt(0);
Location second = locations->InAt(1);
- DCHECK(first.Equals(locations->Out()));
+ Location out = locations->Out();
switch (mul->GetResultType()) {
- case Primitive::kPrimInt: {
- if (second.IsRegister()) {
+ case Primitive::kPrimInt:
+ // The constant may have ended up in a register, so test explicitly to avoid
+ // problems where the output may not be the same as the first operand.
+ if (mul->InputAt(1)->IsIntConstant()) {
+ Immediate imm(mul->InputAt(1)->AsIntConstant()->GetValue());
+ __ imull(out.AsRegister<Register>(), first.AsRegister<Register>(), imm);
+ } else if (second.IsRegister()) {
+ DCHECK(first.Equals(out));
__ imull(first.AsRegister<Register>(), second.AsRegister<Register>());
- } else if (second.IsConstant()) {
- Immediate imm(second.GetConstant()->AsIntConstant()->GetValue());
- __ imull(first.AsRegister<Register>(), imm);
} else {
DCHECK(second.IsStackSlot());
+ DCHECK(first.Equals(out));
__ imull(first.AsRegister<Register>(), Address(ESP, second.GetStackIndex()));
}
break;
- }
case Primitive::kPrimLong: {
Register in1_hi = first.AsRegisterPairHigh<Register>();
@@ -4535,7 +4543,11 @@ void ParallelMoveResolverX86::EmitSwap(size_t index) {
Location destination = move->GetDestination();
if (source.IsRegister() && destination.IsRegister()) {
- __ xchgl(destination.AsRegister<Register>(), source.AsRegister<Register>());
+ // Use XOR swap algorithm to avoid serializing XCHG instruction or using a temporary.
+ DCHECK_NE(destination.AsRegister<Register>(), source.AsRegister<Register>());
+ __ xorl(destination.AsRegister<Register>(), source.AsRegister<Register>());
+ __ xorl(source.AsRegister<Register>(), destination.AsRegister<Register>());
+ __ xorl(destination.AsRegister<Register>(), source.AsRegister<Register>());
} else if (source.IsRegister() && destination.IsStackSlot()) {
Exchange(source.AsRegister<Register>(), destination.GetStackIndex());
} else if (source.IsStackSlot() && destination.IsRegister()) {
@@ -4681,6 +4693,10 @@ void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) {
__ Bind(slow_path->GetExitLabel());
}
+static Address GetExceptionTlsAddress() {
+ return Address::Absolute(Thread::ExceptionOffset<kX86WordSize>().Int32Value());
+}
+
void LocationsBuilderX86::VisitLoadException(HLoadException* load) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
@@ -4688,9 +4704,15 @@ void LocationsBuilderX86::VisitLoadException(HLoadException* load) {
}
void InstructionCodeGeneratorX86::VisitLoadException(HLoadException* load) {
- Address address = Address::Absolute(Thread::ExceptionOffset<kX86WordSize>().Int32Value());
- __ fs()->movl(load->GetLocations()->Out().AsRegister<Register>(), address);
- __ fs()->movl(address, Immediate(0));
+ __ fs()->movl(load->GetLocations()->Out().AsRegister<Register>(), GetExceptionTlsAddress());
+}
+
+void LocationsBuilderX86::VisitClearException(HClearException* clear) {
+ new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
+}
+
+void InstructionCodeGeneratorX86::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
+ __ fs()->movl(GetExceptionTlsAddress(), Immediate(0));
}
void LocationsBuilderX86::VisitThrow(HThrow* instruction) {
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index a95ce68657..4fe93f99b0 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -2535,13 +2535,19 @@ void LocationsBuilderX86_64::VisitMul(HMul* mul) {
case Primitive::kPrimInt: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
- locations->SetOut(Location::SameAsFirstInput());
+ if (mul->InputAt(1)->IsIntConstant()) {
+ // Can use 3 operand multiply.
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ } else {
+ locations->SetOut(Location::SameAsFirstInput());
+ }
break;
}
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RegisterOrInt32LongConstant(mul->InputAt(1)));
- if (locations->InAt(1).IsConstant()) {
+ locations->SetInAt(1, Location::Any());
+ if (mul->InputAt(1)->IsLongConstant() &&
+ IsInt<32>(mul->InputAt(1)->AsLongConstant()->GetValue())) {
// Can use 3 operand multiply.
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
} else {
@@ -2566,37 +2572,51 @@ void InstructionCodeGeneratorX86_64::VisitMul(HMul* mul) {
LocationSummary* locations = mul->GetLocations();
Location first = locations->InAt(0);
Location second = locations->InAt(1);
+ Location out = locations->Out();
switch (mul->GetResultType()) {
- case Primitive::kPrimInt: {
- DCHECK(first.Equals(locations->Out()));
- if (second.IsRegister()) {
+ case Primitive::kPrimInt:
+ // The constant may have ended up in a register, so test explicitly to avoid
+ // problems where the output may not be the same as the first operand.
+ if (mul->InputAt(1)->IsIntConstant()) {
+ Immediate imm(mul->InputAt(1)->AsIntConstant()->GetValue());
+ __ imull(out.AsRegister<CpuRegister>(), first.AsRegister<CpuRegister>(), imm);
+ } else if (second.IsRegister()) {
+ DCHECK(first.Equals(out));
__ imull(first.AsRegister<CpuRegister>(), second.AsRegister<CpuRegister>());
- } else if (second.IsConstant()) {
- Immediate imm(second.GetConstant()->AsIntConstant()->GetValue());
- __ imull(first.AsRegister<CpuRegister>(), imm);
} else {
+ DCHECK(first.Equals(out));
DCHECK(second.IsStackSlot());
__ imull(first.AsRegister<CpuRegister>(),
Address(CpuRegister(RSP), second.GetStackIndex()));
}
break;
- }
case Primitive::kPrimLong: {
- if (second.IsConstant()) {
- int64_t value = second.GetConstant()->AsLongConstant()->GetValue();
- DCHECK(IsInt<32>(value));
- __ imulq(locations->Out().AsRegister<CpuRegister>(),
- first.AsRegister<CpuRegister>(),
- Immediate(static_cast<int32_t>(value)));
- } else {
- DCHECK(first.Equals(locations->Out()));
+ // The constant may have ended up in a register, so test explicitly to avoid
+ // problems where the output may not be the same as the first operand.
+ if (mul->InputAt(1)->IsLongConstant()) {
+ int64_t value = mul->InputAt(1)->AsLongConstant()->GetValue();
+ if (IsInt<32>(value)) {
+ __ imulq(out.AsRegister<CpuRegister>(), first.AsRegister<CpuRegister>(),
+ Immediate(static_cast<int32_t>(value)));
+ } else {
+ // Have to use the constant area.
+ DCHECK(first.Equals(out));
+ __ imulq(first.AsRegister<CpuRegister>(), codegen_->LiteralInt64Address(value));
+ }
+ } else if (second.IsRegister()) {
+ DCHECK(first.Equals(out));
__ imulq(first.AsRegister<CpuRegister>(), second.AsRegister<CpuRegister>());
+ } else {
+ DCHECK(second.IsDoubleStackSlot());
+ DCHECK(first.Equals(out));
+ __ imulq(first.AsRegister<CpuRegister>(),
+ Address(CpuRegister(RSP), second.GetStackIndex()));
}
break;
}
case Primitive::kPrimFloat: {
- DCHECK(first.Equals(locations->Out()));
+ DCHECK(first.Equals(out));
if (second.IsFpuRegister()) {
__ mulss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (second.IsConstant()) {
@@ -2611,7 +2631,7 @@ void InstructionCodeGeneratorX86_64::VisitMul(HMul* mul) {
}
case Primitive::kPrimDouble: {
- DCHECK(first.Equals(locations->Out()));
+ DCHECK(first.Equals(out));
if (second.IsFpuRegister()) {
__ mulsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (second.IsConstant()) {
@@ -4507,6 +4527,10 @@ void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) {
__ Bind(slow_path->GetExitLabel());
}
+static Address GetExceptionTlsAddress() {
+ return Address::Absolute(Thread::ExceptionOffset<kX86_64WordSize>().Int32Value(), true);
+}
+
void LocationsBuilderX86_64::VisitLoadException(HLoadException* load) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall);
@@ -4514,10 +4538,15 @@ void LocationsBuilderX86_64::VisitLoadException(HLoadException* load) {
}
void InstructionCodeGeneratorX86_64::VisitLoadException(HLoadException* load) {
- Address address = Address::Absolute(
- Thread::ExceptionOffset<kX86_64WordSize>().Int32Value(), true);
- __ gs()->movl(load->GetLocations()->Out().AsRegister<CpuRegister>(), address);
- __ gs()->movl(address, Immediate(0));
+ __ gs()->movl(load->GetLocations()->Out().AsRegister<CpuRegister>(), GetExceptionTlsAddress());
+}
+
+void LocationsBuilderX86_64::VisitClearException(HClearException* clear) {
+ new (GetGraph()->GetArena()) LocationSummary(clear, LocationSummary::kNoCall);
+}
+
+void InstructionCodeGeneratorX86_64::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
+ __ gs()->movl(GetExceptionTlsAddress(), Immediate(0));
}
void LocationsBuilderX86_64::VisitThrow(HThrow* instruction) {
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index cfebb77dd7..e4bc9e68e1 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -89,6 +89,33 @@ void GraphChecker::VisitBasicBlock(HBasicBlock* block) {
block->GetBlockId()));
}
+ // Ensure that the only Return(Void) and Throw jump to Exit. An exiting
+ // TryBoundary may be between a Throw and the Exit if the Throw is in a try.
+ if (block->IsExitBlock()) {
+ for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) {
+ HBasicBlock* predecessor = block->GetPredecessors().Get(i);
+ if (predecessor->IsSingleTryBoundary()
+ && !predecessor->GetLastInstruction()->AsTryBoundary()->IsEntry()) {
+ HBasicBlock* real_predecessor = predecessor->GetSinglePredecessor();
+ HInstruction* last_instruction = real_predecessor->GetLastInstruction();
+ if (!last_instruction->IsThrow()) {
+ AddError(StringPrintf("Unexpected TryBoundary between %s:%d and Exit.",
+ last_instruction->DebugName(),
+ last_instruction->GetId()));
+ }
+ } else {
+ HInstruction* last_instruction = predecessor->GetLastInstruction();
+ if (!last_instruction->IsReturn()
+ && !last_instruction->IsReturnVoid()
+ && !last_instruction->IsThrow()) {
+ AddError(StringPrintf("Unexpected instruction %s:%d jumps into the exit block.",
+ last_instruction->DebugName(),
+ last_instruction->GetId()));
+ }
+ }
+ }
+ }
+
// Visit this block's list of phis.
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
@@ -328,6 +355,39 @@ void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) {
void SSAChecker::VisitBasicBlock(HBasicBlock* block) {
super_type::VisitBasicBlock(block);
+ // Ensure that only catch blocks have exceptional predecessors, and if they do
+ // these are instructions which throw into them.
+ if (block->IsCatchBlock()) {
+ for (size_t i = 0, e = block->GetExceptionalPredecessors().Size(); i < e; ++i) {
+ HInstruction* thrower = block->GetExceptionalPredecessors().Get(i);
+ HBasicBlock* try_block = thrower->GetBlock();
+ if (!thrower->CanThrow()) {
+ AddError(StringPrintf("Exceptional predecessor %s:%d of catch block %d does not throw.",
+ thrower->DebugName(),
+ thrower->GetId(),
+ block->GetBlockId()));
+ } else if (!try_block->IsInTry()) {
+ AddError(StringPrintf("Exceptional predecessor %s:%d of catch block %d "
+ "is not in a try block.",
+ thrower->DebugName(),
+ thrower->GetId(),
+ block->GetBlockId()));
+ } else if (!try_block->GetTryEntry()->HasExceptionHandler(*block)) {
+ AddError(StringPrintf("Catch block %d is not an exception handler of "
+ "its exceptional predecessor %s:%d.",
+ block->GetBlockId(),
+ thrower->DebugName(),
+ thrower->GetId()));
+ }
+ }
+ } else {
+ if (!block->GetExceptionalPredecessors().IsEmpty()) {
+ AddError(StringPrintf("Normal block %d has %zu exceptional predecessors.",
+ block->GetBlockId(),
+ block->GetExceptionalPredecessors().Size()));
+ }
+ }
+
// Ensure that catch blocks are not normal successors, and normal blocks are
// never exceptional successors.
const size_t num_normal_successors = block->NumberOfNormalSuccessors();
@@ -512,6 +572,7 @@ void SSAChecker::CheckLoop(HBasicBlock* loop_header) {
void SSAChecker::VisitInstruction(HInstruction* instruction) {
super_type::VisitInstruction(instruction);
+ HBasicBlock* block = instruction->GetBlock();
// Ensure an instruction dominates all its uses.
for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
@@ -543,6 +604,24 @@ void SSAChecker::VisitInstruction(HInstruction* instruction) {
}
}
}
+
+ // Ensure that throwing instructions in try blocks are listed as exceptional
+ // predecessors in their exception handlers.
+ if (instruction->CanThrow() && block->IsInTry()) {
+ for (HExceptionHandlerIterator handler_it(*block->GetTryEntry());
+ !handler_it.Done();
+ handler_it.Advance()) {
+ if (!handler_it.Current()->GetExceptionalPredecessors().Contains(instruction)) {
+ AddError(StringPrintf("Instruction %s:%d is in try block %d and can throw "
+ "but its exception handler %d does not list it in "
+ "its exceptional predecessors.",
+ instruction->DebugName(),
+ instruction->GetId(),
+ block->GetBlockId(),
+ handler_it.Current()->GetBlockId()));
+ }
+ }
+ }
}
static Primitive::Type PrimitiveKind(Primitive::Type type) {
@@ -590,11 +669,32 @@ void SSAChecker::VisitPhi(HPhi* phi) {
if (phi->IsCatchPhi()) {
// The number of inputs of a catch phi corresponds to the total number of
// throwing instructions caught by this catch block.
+ const GrowableArray<HInstruction*>& predecessors =
+ phi->GetBlock()->GetExceptionalPredecessors();
+ if (phi->InputCount() != predecessors.Size()) {
+ AddError(StringPrintf(
+ "Phi %d in catch block %d has %zu inputs, "
+ "but catch block %d has %zu exceptional predecessors.",
+ phi->GetId(), phi->GetBlock()->GetBlockId(), phi->InputCount(),
+ phi->GetBlock()->GetBlockId(), predecessors.Size()));
+ } else {
+ for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
+ HInstruction* input = phi->InputAt(i);
+ HInstruction* thrower = predecessors.Get(i);
+ if (!input->StrictlyDominates(thrower)) {
+ AddError(StringPrintf(
+ "Input %d at index %zu of phi %d from catch block %d does not "
+ "dominate the throwing instruction %s:%d.",
+ input->GetId(), i, phi->GetId(), phi->GetBlock()->GetBlockId(),
+ thrower->DebugName(), thrower->GetId()));
+ }
+ }
+ }
} else {
// Ensure the number of inputs of a non-catch phi is the same as the number
// of its predecessors.
const GrowableArray<HBasicBlock*>& predecessors =
- phi->GetBlock()->GetPredecessors();
+ phi->GetBlock()->GetPredecessors();
if (phi->InputCount() != predecessors.Size()) {
AddError(StringPrintf(
"Phi %d in block %d has %zu inputs, "
diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc
index eca0d9344f..0f6677519e 100644
--- a/compiler/optimizing/graph_checker_test.cc
+++ b/compiler/optimizing/graph_checker_test.cc
@@ -25,14 +25,14 @@ namespace art {
* Create a simple control-flow graph composed of two blocks:
*
* BasicBlock 0, succ: 1
- * 0: Goto 1
+ * 0: ReturnVoid 1
* BasicBlock 1, pred: 0
* 1: Exit
*/
HGraph* CreateSimpleCFG(ArenaAllocator* allocator) {
HGraph* graph = CreateGraph(allocator);
HBasicBlock* entry_block = new (allocator) HBasicBlock(graph);
- entry_block->AddInstruction(new (allocator) HGoto());
+ entry_block->AddInstruction(new (allocator) HReturnVoid());
graph->AddBlock(entry_block);
graph->SetEntryBlock(entry_block);
HBasicBlock* exit_block = new (allocator) HBasicBlock(graph);
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index d6b5636edc..069a7a460b 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -386,6 +386,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
StartAttributeStream("recursive") << std::boolalpha
<< invoke->IsRecursive()
<< std::noboolalpha;
+ StartAttributeStream("intrinsic") << invoke->GetIntrinsic();
}
void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE {
@@ -464,21 +465,19 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
} else {
StartAttributeStream("loop") << "B" << info->GetHeader()->GetBlockId();
}
- } else if (IsReferenceTypePropagationPass() && is_after_pass_) {
- if (instruction->GetType() == Primitive::kPrimNot) {
- if (instruction->IsLoadClass()) {
- ReferenceTypeInfo info = instruction->AsLoadClass()->GetLoadedClassRTI();
- ScopedObjectAccess soa(Thread::Current());
- DCHECK(info.IsValid()) << "Invalid RTI for " << instruction->DebugName();
- StartAttributeStream("klass") << PrettyDescriptor(info.GetTypeHandle().Get());
- StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha;
- } else {
- ReferenceTypeInfo info = instruction->GetReferenceTypeInfo();
- ScopedObjectAccess soa(Thread::Current());
- DCHECK(info.IsValid()) << "Invalid RTI for " << instruction->DebugName();
- StartAttributeStream("klass") << PrettyDescriptor(info.GetTypeHandle().Get());
- StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha;
- }
+ } else if (IsReferenceTypePropagationPass()
+ && (instruction->GetType() == Primitive::kPrimNot)) {
+ ReferenceTypeInfo info = instruction->IsLoadClass()
+ ? instruction->AsLoadClass()->GetLoadedClassRTI()
+ : instruction->GetReferenceTypeInfo();
+ ScopedObjectAccess soa(Thread::Current());
+ if (info.IsValid()) {
+ StartAttributeStream("klass") << PrettyDescriptor(info.GetTypeHandle().Get());
+ StartAttributeStream("can_be_null")
+ << std::boolalpha << instruction->CanBeNull() << std::noboolalpha;
+ StartAttributeStream("exact") << std::boolalpha << info.IsExact() << std::noboolalpha;
+ } else {
+ DCHECK(!is_after_pass_) << "Type info should be valid after reference type propagation";
}
}
if (disasm_info_ != nullptr) {
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 1551c1531a..01065959d8 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -22,8 +22,10 @@
#include "constant_folding.h"
#include "dead_code_elimination.h"
#include "driver/compiler_driver-inl.h"
+#include "driver/compiler_options.h"
#include "driver/dex_compilation_unit.h"
#include "instruction_simplifier.h"
+#include "intrinsics.h"
#include "mirror/class_loader.h"
#include "mirror/dex_cache.h"
#include "nodes.h"
@@ -38,9 +40,6 @@
namespace art {
-static constexpr int kMaxInlineCodeUnits = 18;
-static constexpr int kDepthLimit = 3;
-
void HInliner::Run() {
if (graph_->IsDebuggable()) {
// For simplicity, we currently never inline when the graph is debuggable. This avoids
@@ -86,7 +85,7 @@ void HInliner::Run() {
}
static bool IsMethodOrDeclaringClassFinal(ArtMethod* method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
return method->IsFinal() || method->GetDeclaringClass()->IsFinal();
}
@@ -96,7 +95,7 @@ static bool IsMethodOrDeclaringClassFinal(ArtMethod* method)
* Return nullptr if the runtime target cannot be proven.
*/
static ArtMethod* FindVirtualOrInterfaceTarget(HInvoke* invoke, ArtMethod* resolved_method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
if (IsMethodOrDeclaringClassFinal(resolved_method)) {
// No need to lookup further, the resolved method will be the target.
return resolved_method;
@@ -162,7 +161,7 @@ static ArtMethod* FindVirtualOrInterfaceTarget(HInvoke* invoke, ArtMethod* resol
static uint32_t FindMethodIndexIn(ArtMethod* method,
const DexFile& dex_file,
uint32_t referrer_index)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
if (method->GetDexFile()->GetLocation().compare(dex_file.GetLocation()) == 0) {
return method->GetDexMethodIndex();
} else {
@@ -219,7 +218,8 @@ bool HInliner::TryInline(HInvoke* invoke_instruction, uint32_t method_index) con
return false;
}
- if (code_item->insns_size_in_code_units_ > kMaxInlineCodeUnits) {
+ size_t inline_max_code_units = compiler_driver_->GetCompilerOptions().GetInlineMaxCodeUnits();
+ if (code_item->insns_size_in_code_units_ > inline_max_code_units) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " is too big to inline";
return false;
@@ -271,11 +271,11 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
const DexFile& callee_dex_file = *resolved_method->GetDexFile();
uint32_t method_index = resolved_method->GetDexMethodIndex();
-
+ ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
DexCompilationUnit dex_compilation_unit(
nullptr,
caller_compilation_unit_.GetClassLoader(),
- caller_compilation_unit_.GetClassLinker(),
+ class_linker,
*resolved_method->GetDexFile(),
code_item,
resolved_method->GetDeclaringClass()->GetDexClassDefIndex(),
@@ -356,8 +356,10 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
HConstantFolding fold(callee_graph);
ReferenceTypePropagation type_propagation(callee_graph, handles_);
InstructionSimplifier simplify(callee_graph, stats_);
+ IntrinsicsRecognizer intrinsics(callee_graph, compiler_driver_);
HOptimization* optimizations[] = {
+ &intrinsics,
&dce,
&fold,
&type_propagation,
@@ -369,7 +371,7 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
optimization->Run();
}
- if (depth_ + 1 < kDepthLimit) {
+ if (depth_ + 1 < compiler_driver_->GetCompilerOptions().GetInlineDepthLimit()) {
HInliner inliner(callee_graph,
outer_compilation_unit_,
dex_compilation_unit,
@@ -448,7 +450,33 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
}
}
- callee_graph->InlineInto(graph_, invoke_instruction);
+ HInstruction* return_replacement = callee_graph->InlineInto(graph_, invoke_instruction);
+
+ // When merging the graph we might create a new NullConstant in the caller graph which does
+ // not have the chance to be typed. We assign the correct type here so that we can keep the
+ // assertion that every reference has a valid type. This also simplifies checks along the way.
+ HNullConstant* null_constant = graph_->GetNullConstant();
+ if (!null_constant->GetReferenceTypeInfo().IsValid()) {
+ ReferenceTypeInfo::TypeHandle obj_handle =
+ handles_->NewHandle(class_linker->GetClassRoot(ClassLinker::kJavaLangObject));
+ null_constant->SetReferenceTypeInfo(
+ ReferenceTypeInfo::Create(obj_handle, false /* is_exact */));
+ }
+
+ if ((return_replacement != nullptr)
+ && (return_replacement->GetType() == Primitive::kPrimNot)) {
+ if (!return_replacement->GetReferenceTypeInfo().IsValid()) {
+ // Make sure that we have a valid type for the return. We may get an invalid one when
+ // we inline invokes with multiple branches and create a Phi for the result.
+ // TODO: we could be more precise by merging the phi inputs but that requires
+ // some functionality from the reference type propagation.
+ DCHECK(return_replacement->IsPhi());
+ ReferenceTypeInfo::TypeHandle return_handle =
+ handles_->NewHandle(resolved_method->GetReturnType());
+ return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create(
+ return_handle, return_handle->IsFinal() /* is_exact */));
+ }
+ }
return true;
}
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 1089812beb..d3911456fb 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -195,6 +195,7 @@ bool InstructionSimplifierVisitor::IsDominatedByInputNullCheck(HInstruction* ins
// Returns whether doing a type test between the class of `object` against `klass` has
// a statically known outcome. The result of the test is stored in `outcome`.
static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bool* outcome) {
+ DCHECK(!object->IsNullConstant()) << "Null constants should be special cased";
ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo();
ScopedObjectAccess soa(Thread::Current());
if (!obj_rti.IsValid()) {
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 8ef13e125e..55e964ea8d 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -103,6 +103,16 @@ static Intrinsics GetIntrinsic(InlineMethod method) {
LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
UNREACHABLE();
}
+ case kIntrinsicNumberOfLeadingZeros:
+ switch (GetType(method.d.data, true)) {
+ case Primitive::kPrimInt:
+ return Intrinsics::kIntegerNumberOfLeadingZeros;
+ case Primitive::kPrimLong:
+ return Intrinsics::kLongNumberOfLeadingZeros;
+ default:
+ LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+ UNREACHABLE();
+ }
// Abs.
case kIntrinsicAbsDouble:
@@ -359,7 +369,7 @@ void IntrinsicsRecognizer::Run() {
std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) {
switch (intrinsic) {
case Intrinsics::kNone:
- os << "No intrinsic.";
+ os << "None";
break;
#define OPTIMIZING_INTRINSICS(Name, IsStatic) \
case Intrinsics::k ## Name: \
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index b4dbf75f0a..a79765426f 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -224,6 +224,48 @@ static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
}
+static void GenNumberOfLeadingZeros(LocationSummary* locations,
+ Primitive::Type type,
+ ArmAssembler* assembler) {
+ Location in = locations->InAt(0);
+ Register out = locations->Out().AsRegister<Register>();
+
+ DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
+
+ if (type == Primitive::kPrimLong) {
+ Register in_reg_lo = in.AsRegisterPairLow<Register>();
+ Register in_reg_hi = in.AsRegisterPairHigh<Register>();
+ Label end;
+ __ clz(out, in_reg_hi);
+ __ CompareAndBranchIfNonZero(in_reg_hi, &end);
+ __ clz(out, in_reg_lo);
+ __ AddConstant(out, 32);
+ __ Bind(&end);
+ } else {
+ __ clz(out, in.AsRegister<Register>());
+ }
+}
+
+void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
+ GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
+ GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+}
+
static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
Location in = locations->InAt(0);
Location out = locations->Out();
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 78ac167a87..2c93feaaa6 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -260,6 +260,33 @@ void IntrinsicCodeGeneratorARM64::VisitShortReverseBytes(HInvoke* invoke) {
GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
}
+static void GenNumberOfLeadingZeros(LocationSummary* locations,
+ Primitive::Type type,
+ vixl::MacroAssembler* masm) {
+ DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+ Location in = locations->InAt(0);
+ Location out = locations->Out();
+
+ __ Clz(RegisterFrom(out, type), RegisterFrom(in, type));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
+ GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
+ GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
+}
+
static void GenReverse(LocationSummary* locations,
Primitive::Type type,
vixl::MacroAssembler* masm) {
diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h
index 2c9248f52c..d28c5a3f2f 100644
--- a/compiler/optimizing/intrinsics_list.h
+++ b/compiler/optimizing/intrinsics_list.h
@@ -27,8 +27,10 @@
V(FloatIntBitsToFloat, kStatic) \
V(IntegerReverse, kStatic) \
V(IntegerReverseBytes, kStatic) \
+ V(IntegerNumberOfLeadingZeros, kStatic) \
V(LongReverse, kStatic) \
V(LongReverseBytes, kStatic) \
+ V(LongNumberOfLeadingZeros, kStatic) \
V(ShortReverseBytes, kStatic) \
V(MathAbsDouble, kStatic) \
V(MathAbsFloat, kStatic) \
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 0d6ca09f31..993c005d5a 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1756,6 +1756,8 @@ UNIMPLEMENTED_INTRINSIC(MathRoundDouble)
UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
+UNIMPLEMENTED_INTRINSIC(IntegerNumberOfLeadingZeros)
+UNIMPLEMENTED_INTRINSIC(LongNumberOfLeadingZeros)
#undef UNIMPLEMENTED_INTRINSIC
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index ea342e9382..62cdb4c34a 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -690,7 +690,7 @@ static void CreateSSE41FPToIntLocations(ArenaAllocator* arena,
LocationSummary::kNoCall,
kIntrinsified);
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetOut(Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
locations->AddTemp(Location::RequiresFpuRegister());
return;
}
@@ -732,6 +732,9 @@ void IntrinsicCodeGeneratorX86_64::VisitMathRoundFloat(HInvoke* invoke) {
// And truncate to an integer.
__ roundss(inPlusPointFive, inPlusPointFive, Immediate(1));
+ // Load maxInt into out.
+ codegen_->Load64BitValue(out, kPrimIntMax);
+
// if inPlusPointFive >= maxInt goto done
__ comiss(inPlusPointFive, codegen_->LiteralFloatAddress(static_cast<float>(kPrimIntMax)));
__ j(kAboveEqual, &done);
@@ -776,6 +779,9 @@ void IntrinsicCodeGeneratorX86_64::VisitMathRoundDouble(HInvoke* invoke) {
// And truncate to an integer.
__ roundsd(inPlusPointFive, inPlusPointFive, Immediate(1));
+ // Load maxLong into out.
+ codegen_->Load64BitValue(out, kPrimLongMax);
+
// if inPlusPointFive >= maxLong goto done
__ comisd(inPlusPointFive, codegen_->LiteralDoubleAddress(static_cast<double>(kPrimLongMax)));
__ j(kAboveEqual, &done);
@@ -1615,6 +1621,8 @@ void IntrinsicCodeGeneratorX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSE
UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
+UNIMPLEMENTED_INTRINSIC(IntegerNumberOfLeadingZeros)
+UNIMPLEMENTED_INTRINSIC(LongNumberOfLeadingZeros)
#undef UNIMPLEMENTED_INTRINSIC
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
new file mode 100644
index 0000000000..2fc66e6de4
--- /dev/null
+++ b/compiler/optimizing/licm_test.cc
@@ -0,0 +1,195 @@
+/*
+ * 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 "base/arena_allocator.h"
+#include "builder.h"
+#include "gtest/gtest.h"
+#include "licm.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+#include "side_effects_analysis.h"
+
+namespace art {
+
+/**
+ * Fixture class for the LICM tests.
+ */
+class LICMTest : public testing::Test {
+ public:
+ LICMTest() : pool_(), allocator_(&pool_) {
+ graph_ = CreateGraph(&allocator_);
+ }
+
+ ~LICMTest() { }
+
+ // Builds a singly-nested loop structure in CFG. Tests can further populate
+ // the basic blocks with instructions to set up interesting scenarios.
+ void BuildLoop() {
+ entry_ = new (&allocator_) HBasicBlock(graph_);
+ loop_preheader_ = new (&allocator_) HBasicBlock(graph_);
+ loop_header_ = new (&allocator_) HBasicBlock(graph_);
+ loop_body_ = new (&allocator_) HBasicBlock(graph_);
+ exit_ = new (&allocator_) HBasicBlock(graph_);
+
+ graph_->AddBlock(entry_);
+ graph_->AddBlock(loop_preheader_);
+ graph_->AddBlock(loop_header_);
+ graph_->AddBlock(loop_body_);
+ graph_->AddBlock(exit_);
+
+ graph_->SetEntryBlock(entry_);
+ graph_->SetExitBlock(exit_);
+
+ // Set up loop flow in CFG.
+ entry_->AddSuccessor(loop_preheader_);
+ loop_preheader_->AddSuccessor(loop_header_);
+ loop_header_->AddSuccessor(loop_body_);
+ loop_header_->AddSuccessor(exit_);
+ loop_body_->AddSuccessor(loop_header_);
+
+ // Provide boiler-plate instructions.
+ parameter_ = new (&allocator_) HParameterValue(0, Primitive::kPrimNot);
+ entry_->AddInstruction(parameter_);
+ constant_ = new (&allocator_) HConstant(Primitive::kPrimInt);
+ loop_preheader_->AddInstruction(constant_);
+ loop_header_->AddInstruction(new (&allocator_) HIf(parameter_));
+ loop_body_->AddInstruction(new (&allocator_) HGoto());
+ exit_->AddInstruction(new (&allocator_) HExit());
+ }
+
+ // Performs LICM optimizations (after proper set up).
+ void PerformLICM() {
+ ASSERT_TRUE(graph_->TryBuildingSsa());
+ SideEffectsAnalysis side_effects(graph_);
+ side_effects.Run();
+ LICM licm(graph_, side_effects);
+ licm.Run();
+ }
+
+ // General building fields.
+ ArenaPool pool_;
+ ArenaAllocator allocator_;
+ HGraph* graph_;
+
+ // Specific basic blocks.
+ HBasicBlock* entry_;
+ HBasicBlock* loop_preheader_;
+ HBasicBlock* loop_header_;
+ HBasicBlock* loop_body_;
+ HBasicBlock* exit_;
+
+ HInstruction* parameter_; // "this"
+ HInstruction* constant_;
+};
+
+//
+// The actual LICM tests.
+//
+
+TEST_F(LICMTest, ConstantHoisting) {
+ BuildLoop();
+
+ // Populate the loop with instructions: set array to constant.
+ HInstruction* constant = new (&allocator_) HConstant(Primitive::kPrimDouble);
+ loop_body_->InsertInstructionBefore(constant, loop_body_->GetLastInstruction());
+ HInstruction* set_array = new (&allocator_) HArraySet(
+ parameter_, constant_, constant, Primitive::kPrimDouble, 0);
+ loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
+
+ EXPECT_EQ(constant->GetBlock(), loop_body_);
+ EXPECT_EQ(set_array->GetBlock(), loop_body_);
+ PerformLICM();
+ EXPECT_EQ(constant->GetBlock(), loop_preheader_);
+ EXPECT_EQ(set_array->GetBlock(), loop_body_);
+}
+
+TEST_F(LICMTest, FieldHoisting) {
+ BuildLoop();
+
+ // Populate the loop with instructions: set/get field with different types.
+ HInstruction* get_field = new (&allocator_) HInstanceFieldGet(
+ parameter_, Primitive::kPrimLong, MemberOffset(10),
+ false, kUnknownFieldIndex, graph_->GetDexFile());
+ loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
+ HInstruction* set_field = new (&allocator_) HInstanceFieldSet(
+ parameter_, constant_, Primitive::kPrimInt, MemberOffset(20),
+ false, kUnknownFieldIndex, graph_->GetDexFile());
+ loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
+
+ EXPECT_EQ(get_field->GetBlock(), loop_body_);
+ EXPECT_EQ(set_field->GetBlock(), loop_body_);
+ PerformLICM();
+ EXPECT_EQ(get_field->GetBlock(), loop_preheader_);
+ EXPECT_EQ(set_field->GetBlock(), loop_body_);
+}
+
+TEST_F(LICMTest, NoFieldHoisting) {
+ BuildLoop();
+
+ // Populate the loop with instructions: set/get field with same types.
+ HInstruction* get_field = new (&allocator_) HInstanceFieldGet(
+ parameter_, Primitive::kPrimLong, MemberOffset(10),
+ false, kUnknownFieldIndex, graph_->GetDexFile());
+ loop_body_->InsertInstructionBefore(get_field, loop_body_->GetLastInstruction());
+ HInstruction* set_field = new (&allocator_) HInstanceFieldSet(
+ parameter_, get_field, Primitive::kPrimLong, MemberOffset(10),
+ false, kUnknownFieldIndex, graph_->GetDexFile());
+ loop_body_->InsertInstructionBefore(set_field, loop_body_->GetLastInstruction());
+
+ EXPECT_EQ(get_field->GetBlock(), loop_body_);
+ EXPECT_EQ(set_field->GetBlock(), loop_body_);
+ PerformLICM();
+ EXPECT_EQ(get_field->GetBlock(), loop_body_);
+ EXPECT_EQ(set_field->GetBlock(), loop_body_);
+}
+
+TEST_F(LICMTest, ArrayHoisting) {
+ BuildLoop();
+
+ // Populate the loop with instructions: set/get array with different types.
+ HInstruction* get_array = new (&allocator_) HArrayGet(
+ parameter_, constant_, Primitive::kPrimLong);
+ loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
+ HInstruction* set_array = new (&allocator_) HArraySet(
+ parameter_, constant_, constant_, Primitive::kPrimInt, 0);
+ loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
+
+ EXPECT_EQ(get_array->GetBlock(), loop_body_);
+ EXPECT_EQ(set_array->GetBlock(), loop_body_);
+ PerformLICM();
+ EXPECT_EQ(get_array->GetBlock(), loop_preheader_);
+ EXPECT_EQ(set_array->GetBlock(), loop_body_);
+}
+
+TEST_F(LICMTest, NoArrayHoisting) {
+ BuildLoop();
+
+ // Populate the loop with instructions: set/get array with same types.
+ HInstruction* get_array = new (&allocator_) HArrayGet(
+ parameter_, constant_, Primitive::kPrimLong);
+ loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
+ HInstruction* set_array = new (&allocator_) HArraySet(
+ parameter_, get_array, constant_, Primitive::kPrimLong, 0);
+ loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
+
+ EXPECT_EQ(get_array->GetBlock(), loop_body_);
+ EXPECT_EQ(set_array->GetBlock(), loop_body_);
+ PerformLICM();
+ EXPECT_EQ(get_array->GetBlock(), loop_body_);
+ EXPECT_EQ(set_array->GetBlock(), loop_body_);
+}
+
+} // namespace art
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index efaf48cc9f..d7795f9dff 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -564,6 +564,13 @@ bool HBasicBlock::Dominates(HBasicBlock* other) const {
return false;
}
+void HBasicBlock::AddExceptionalPredecessor(HInstruction* exceptional_predecessor) {
+ DCHECK(exceptional_predecessor->CanThrow());
+ DCHECK(exceptional_predecessor->GetBlock()->IsInTry());
+ DCHECK(exceptional_predecessor->GetBlock()->GetTryEntry()->HasExceptionHandler(*this));
+ exceptional_predecessors_.Add(exceptional_predecessor);
+}
+
static void UpdateInputsUsers(HInstruction* instruction) {
for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
instruction->InputAt(i)->AddUseAt(instruction, i);
@@ -1221,10 +1228,12 @@ bool HTryBoundary::HasSameExceptionHandlersAs(const HTryBoundary& other) const {
return false;
}
- // Exception handler lists cannot contain duplicates, which makes it
- // sufficient to test inclusion only in one direction.
- for (HExceptionHandlerIterator it(other); !it.Done(); it.Advance()) {
- if (!HasExceptionHandler(*it.Current())) {
+ // Exception handlers need to be stored in the same order.
+ for (HExceptionHandlerIterator it1(*this), it2(other);
+ !it1.Done();
+ it1.Advance(), it2.Advance()) {
+ DCHECK(!it2.Done());
+ if (it1.Current() != it2.Current()) {
return false;
}
}
@@ -1481,7 +1490,7 @@ void HGraph::DeleteDeadBlock(HBasicBlock* block) {
blocks_.Put(block->GetBlockId(), nullptr);
}
-void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
+HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
DCHECK(HasExitBlock()) << "Unimplemented scenario";
// Update the environments in this graph to have the invoke's environment
// as parent.
@@ -1506,6 +1515,7 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
outer_graph->SetHasBoundsChecks(true);
}
+ HInstruction* return_value = nullptr;
if (GetBlocks().Size() == 3) {
// Simple case of an entry block, a body block, and an exit block.
// Put the body block's instruction into `invoke`'s block.
@@ -1520,7 +1530,8 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
// Replace the invoke with the return value of the inlined graph.
if (last->IsReturn()) {
- invoke->ReplaceWith(last->InputAt(0));
+ return_value = last->InputAt(0);
+ invoke->ReplaceWith(return_value);
} else {
DCHECK(last->IsReturnVoid());
}
@@ -1542,7 +1553,6 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
// Update all predecessors of the exit block (now the `to` block)
// to not `HReturn` but `HGoto` instead.
- HInstruction* return_value = nullptr;
bool returns_void = to->GetPredecessors().Get(0)->GetLastInstruction()->IsReturnVoid();
if (to->GetPredecessors().Size() == 1) {
HBasicBlock* predecessor = to->GetPredecessors().Get(0);
@@ -1676,6 +1686,8 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
// Finally remove the invoke from the caller.
invoke->GetBlock()->RemoveInstruction(invoke);
+
+ return return_value;
}
/*
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 1190fae914..638ac95631 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -58,6 +58,7 @@ class SsaBuilder;
static const int kDefaultNumberOfBlocks = 8;
static const int kDefaultNumberOfSuccessors = 2;
static const int kDefaultNumberOfPredecessors = 2;
+static const int kDefaultNumberOfExceptionalPredecessors = 0;
static const int kDefaultNumberOfDominatedBlocks = 1;
static const int kDefaultNumberOfBackEdges = 1;
@@ -210,7 +211,9 @@ class HGraph : public ArenaObject<kArenaAllocMisc> {
void ComputeTryBlockInformation();
// Inline this graph in `outer_graph`, replacing the given `invoke` instruction.
- void InlineInto(HGraph* outer_graph, HInvoke* invoke);
+ // Returns the instruction used to replace the invoke expression or null if the
+ // invoke is for a void method.
+ HInstruction* InlineInto(HGraph* outer_graph, HInvoke* invoke);
// Need to add a couple of blocks to test if the loop body is entered and
// put deoptimization instructions, etc.
@@ -306,7 +309,12 @@ class HGraph : public ArenaObject<kArenaAllocMisc> {
// already, it is created and inserted into the graph. This method is only for
// integral types.
HConstant* GetConstant(Primitive::Type type, int64_t value);
+
+ // TODO: This is problematic for the consistency of reference type propagation
+ // because it can be created anytime after the pass and thus it will be left
+ // with an invalid type.
HNullConstant* GetNullConstant();
+
HIntConstant* GetIntConstant(int32_t value) {
return CreateConstant(value, &cached_int_constants_);
}
@@ -557,6 +565,7 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> {
explicit HBasicBlock(HGraph* graph, uint32_t dex_pc = kNoDexPc)
: graph_(graph),
predecessors_(graph->GetArena(), kDefaultNumberOfPredecessors),
+ exceptional_predecessors_(graph->GetArena(), kDefaultNumberOfExceptionalPredecessors),
successors_(graph->GetArena(), kDefaultNumberOfSuccessors),
loop_information_(nullptr),
dominator_(nullptr),
@@ -571,6 +580,10 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> {
return predecessors_;
}
+ const GrowableArray<HInstruction*>& GetExceptionalPredecessors() const {
+ return exceptional_predecessors_;
+ }
+
const GrowableArray<HBasicBlock*>& GetSuccessors() const {
return successors_;
}
@@ -639,6 +652,8 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> {
HInstruction* GetLastPhi() const { return phis_.last_instruction_; }
const HInstructionList& GetPhis() const { return phis_; }
+ void AddExceptionalPredecessor(HInstruction* exceptional_predecessor);
+
void AddSuccessor(HBasicBlock* block) {
successors_.Add(block);
block->predecessors_.Add(this);
@@ -678,6 +693,10 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> {
predecessors_.Delete(block);
}
+ void RemoveExceptionalPredecessor(HInstruction* instruction) {
+ exceptional_predecessors_.Delete(instruction);
+ }
+
void RemoveSuccessor(HBasicBlock* block) {
successors_.Delete(block);
}
@@ -714,6 +733,15 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> {
return -1;
}
+ size_t GetExceptionalPredecessorIndexOf(HInstruction* exceptional_predecessor) const {
+ for (size_t i = 0, e = exceptional_predecessors_.Size(); i < e; ++i) {
+ if (exceptional_predecessors_.Get(i) == exceptional_predecessor) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
size_t GetSuccessorIndexOf(HBasicBlock* successor) const {
for (size_t i = 0, e = successors_.Size(); i < e; ++i) {
if (successors_.Get(i) == successor) {
@@ -874,6 +902,7 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> {
private:
HGraph* graph_;
GrowableArray<HBasicBlock*> predecessors_;
+ GrowableArray<HInstruction*> exceptional_predecessors_;
GrowableArray<HBasicBlock*> successors_;
HInstructionList instructions_;
HInstructionList phis_;
@@ -932,6 +961,7 @@ class HLoopInformationOutwardIterator : public ValueObject {
M(BoundsCheck, Instruction) \
M(BoundType, Instruction) \
M(CheckCast, Instruction) \
+ M(ClearException, Instruction) \
M(ClinitCheck, Instruction) \
M(Compare, BinaryOperation) \
M(Condition, BinaryOperation) \
@@ -1467,26 +1497,27 @@ class ReferenceTypeInfo : ValueObject {
static ReferenceTypeInfo CreateInvalid() { return ReferenceTypeInfo(); }
- static bool IsValidHandle(TypeHandle handle) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ static bool IsValidHandle(TypeHandle handle) SHARED_REQUIRES(Locks::mutator_lock_) {
return handle.GetReference() != nullptr;
}
- bool IsValid() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool IsValid() const SHARED_REQUIRES(Locks::mutator_lock_) {
return IsValidHandle(type_handle_);
}
bool IsExact() const { return is_exact_; }
- bool IsObjectClass() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+
+ bool IsObjectClass() const SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(IsValid());
return GetTypeHandle()->IsObjectClass();
}
- bool IsInterface() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool IsInterface() const SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(IsValid());
return GetTypeHandle()->IsInterface();
}
Handle<mirror::Class> GetTypeHandle() const { return type_handle_; }
- bool IsSupertypeOf(ReferenceTypeInfo rti) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool IsSupertypeOf(ReferenceTypeInfo rti) const SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(IsValid());
DCHECK(rti.IsValid());
return GetTypeHandle()->IsAssignableFrom(rti.GetTypeHandle().Get());
@@ -1495,7 +1526,7 @@ class ReferenceTypeInfo : ValueObject {
// Returns true if the type information provide the same amount of details.
// Note that it does not mean that the instructions have the same actual type
// (because the type can be the result of a merge).
- bool IsEqual(ReferenceTypeInfo rti) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool IsEqual(ReferenceTypeInfo rti) SHARED_REQUIRES(Locks::mutator_lock_) {
if (!IsValid() && !rti.IsValid()) {
// Invalid types are equal.
return true;
@@ -3589,7 +3620,7 @@ class HInstanceFieldGet : public HExpression<1> {
const DexFile& dex_file)
: HExpression(
field_type,
- SideEffects::SideEffects::FieldReadOfType(field_type, is_volatile)),
+ SideEffects::FieldReadOfType(field_type, is_volatile)),
field_info_(field_offset, field_type, is_volatile, field_idx, dex_file) {
SetRawInputAt(0, value);
}
@@ -4036,7 +4067,7 @@ class HStaticFieldGet : public HExpression<1> {
const DexFile& dex_file)
: HExpression(
field_type,
- SideEffects::SideEffects::FieldReadOfType(field_type, is_volatile)),
+ SideEffects::FieldReadOfType(field_type, is_volatile)),
field_info_(field_offset, field_type, is_volatile, field_idx, dex_file) {
SetRawInputAt(0, cls);
}
@@ -4112,6 +4143,18 @@ class HLoadException : public HExpression<0> {
DISALLOW_COPY_AND_ASSIGN(HLoadException);
};
+// Implicit part of move-exception which clears thread-local exception storage.
+// Must not be removed because the runtime expects the TLS to get cleared.
+class HClearException : public HTemplateInstruction<0> {
+ public:
+ HClearException() : HTemplateInstruction(SideEffects::AllWrites()) {}
+
+ DECLARE_INSTRUCTION(ClearException);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HClearException);
+};
+
class HThrow : public HTemplateInstruction<1> {
public:
HThrow(HInstruction* exception, uint32_t dex_pc)
@@ -4184,7 +4227,8 @@ class HBoundType : public HExpression<1> {
HBoundType(HInstruction* input, ReferenceTypeInfo upper_bound, bool upper_can_be_null)
: HExpression(Primitive::kPrimNot, SideEffects::None()),
upper_bound_(upper_bound),
- upper_can_be_null_(upper_can_be_null) {
+ upper_can_be_null_(upper_can_be_null),
+ can_be_null_(upper_can_be_null) {
DCHECK_EQ(input->GetType(), Primitive::kPrimNot);
SetRawInputAt(0, input);
SetReferenceTypeInfo(upper_bound_);
diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc
index fe3bb1a2b4..f455571636 100644
--- a/compiler/optimizing/optimizing_cfi_test.cc
+++ b/compiler/optimizing/optimizing_cfi_test.cc
@@ -29,7 +29,7 @@
namespace art {
// Run the tests only on host.
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
class OptimizingCFITest : public CFITest {
public:
@@ -125,6 +125,6 @@ TEST_ISA(kArm64)
TEST_ISA(kX86)
TEST_ISA(kX86_64)
-#endif // HAVE_ANDROID_OS
+#endif // __ANDROID__
} // namespace art
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 601d668995..6a50b7d4a4 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -86,7 +86,7 @@ class CodeVectorAllocator FINAL : public CodeAllocator {
* Filter to apply to the visualizer. Methods whose name contain that filter will
* be dumped.
*/
-static const char* kStringFilter = "";
+static constexpr const char kStringFilter[] = "";
class PassScope;
@@ -105,12 +105,14 @@ class PassObserver : public ValueObject {
visualizer_enabled_(!compiler_driver->GetDumpCfgFileName().empty()),
visualizer_(visualizer_output, graph, *codegen),
graph_in_bad_state_(false) {
- if (strstr(method_name, kStringFilter) == nullptr) {
- timing_logger_enabled_ = visualizer_enabled_ = false;
- }
- if (visualizer_enabled_) {
- visualizer_.PrintHeader(method_name_);
- codegen->SetDisassemblyInformation(&disasm_info_);
+ if (timing_logger_enabled_ || visualizer_enabled_) {
+ if (!IsVerboseMethod(compiler_driver, method_name)) {
+ timing_logger_enabled_ = visualizer_enabled_ = false;
+ }
+ if (visualizer_enabled_) {
+ visualizer_.PrintHeader(method_name_);
+ codegen->SetDisassemblyInformation(&disasm_info_);
+ }
}
}
@@ -169,6 +171,23 @@ class PassObserver : public ValueObject {
}
}
+ static bool IsVerboseMethod(CompilerDriver* compiler_driver, const char* method_name) {
+ // Test an exact match to --verbose-methods. If verbose-methods is set, this overrides an
+ // empty kStringFilter matching all methods.
+ if (compiler_driver->GetCompilerOptions().HasVerboseMethods()) {
+ return compiler_driver->GetCompilerOptions().IsVerboseMethod(method_name);
+ }
+
+ // Test the kStringFilter sub-string. constexpr helper variable to silence unreachable-code
+ // warning when the string is empty.
+ constexpr bool kStringFilterEmpty = arraysize(kStringFilter) <= 1;
+ if (kStringFilterEmpty || strstr(method_name, kStringFilter) != nullptr) {
+ return true;
+ }
+
+ return false;
+ }
+
HGraph* const graph_;
const char* method_name_;
@@ -237,7 +256,7 @@ class OptimizingCompiler FINAL : public Compiler {
}
uintptr_t GetEntryPointOf(ArtMethod* method) const OVERRIDE
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCodePtrSize(
InstructionSetPointerSize(GetCompilerDriver()->GetInstructionSet())));
}
@@ -350,6 +369,36 @@ static void RunOptimizations(HOptimization* optimizations[],
}
}
+static void MaybeRunInliner(HGraph* graph,
+ CompilerDriver* driver,
+ OptimizingCompilerStats* stats,
+ const DexCompilationUnit& dex_compilation_unit,
+ PassObserver* pass_observer,
+ StackHandleScopeCollection* handles) {
+ const CompilerOptions& compiler_options = driver->GetCompilerOptions();
+ bool should_inline = (compiler_options.GetInlineDepthLimit() > 0)
+ && (compiler_options.GetInlineMaxCodeUnits() > 0);
+ if (!should_inline) {
+ return;
+ }
+
+ ArenaAllocator* arena = graph->GetArena();
+ HInliner* inliner = new (arena) HInliner(
+ graph, dex_compilation_unit, dex_compilation_unit, driver, handles, stats);
+ ReferenceTypePropagation* type_propagation =
+ new (arena) ReferenceTypePropagation(graph, handles,
+ "reference_type_propagation_after_inlining");
+
+ HOptimization* optimizations[] = {
+ inliner,
+ // Run another type propagation phase: inlining will open up more opportunities
+ // to remove checkcast/instanceof and null checks.
+ type_propagation,
+ };
+
+ RunOptimizations(optimizations, arraysize(optimizations), pass_observer);
+}
+
static void RunOptimizations(HGraph* graph,
CompilerDriver* driver,
OptimizingCompilerStats* stats,
@@ -364,10 +413,6 @@ static void RunOptimizations(HGraph* graph,
HConstantFolding* fold1 = new (arena) HConstantFolding(graph);
InstructionSimplifier* simplify1 = new (arena) InstructionSimplifier(graph, stats);
HBooleanSimplifier* boolean_simplify = new (arena) HBooleanSimplifier(graph);
-
- HInliner* inliner = new (arena) HInliner(
- graph, dex_compilation_unit, dex_compilation_unit, driver, handles, stats);
-
HConstantFolding* fold2 = new (arena) HConstantFolding(graph, "constant_folding_after_inlining");
SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects);
@@ -379,29 +424,29 @@ static void RunOptimizations(HGraph* graph,
graph, stats, "instruction_simplifier_after_types");
InstructionSimplifier* simplify3 = new (arena) InstructionSimplifier(
graph, stats, "instruction_simplifier_after_bce");
- ReferenceTypePropagation* type_propagation2 =
- new (arena) ReferenceTypePropagation(
- graph, handles, "reference_type_propagation_after_inlining");
InstructionSimplifier* simplify4 = new (arena) InstructionSimplifier(
graph, stats, "instruction_simplifier_before_codegen");
IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, driver);
- HOptimization* optimizations[] = {
+ HOptimization* optimizations1[] = {
intrinsics,
fold1,
simplify1,
type_propagation,
dce1,
- simplify2,
- inliner,
- // Run another type propagation phase: inlining will open up more opprotunities
- // to remove checkast/instanceof and null checks.
- type_propagation2,
+ simplify2
+ };
+
+ RunOptimizations(optimizations1, arraysize(optimizations1), pass_observer);
+
+ MaybeRunInliner(graph, driver, stats, dex_compilation_unit, pass_observer, handles);
+
+ HOptimization* optimizations2[] = {
// BooleanSimplifier depends on the InstructionSimplifier removing redundant
// suspend checks to recognize empty blocks.
boolean_simplify,
- fold2,
+ fold2, // TODO: if we don't inline we can also skip fold2.
side_effects,
gvn,
licm,
@@ -414,7 +459,7 @@ static void RunOptimizations(HGraph* graph,
simplify4,
};
- RunOptimizations(optimizations, arraysize(optimizations), pass_observer);
+ RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer);
}
// The stack map we generate must be 4-byte aligned on ARM. Since existing
diff --git a/compiler/optimizing/parallel_move_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc
index 54ea6f19d4..f9d812f6a6 100644
--- a/compiler/optimizing/parallel_move_resolver.cc
+++ b/compiler/optimizing/parallel_move_resolver.cc
@@ -38,6 +38,20 @@ void ParallelMoveResolverWithSwap::EmitNativeCode(HParallelMove* parallel_move)
// Build up a worklist of moves.
BuildInitialMoveList(parallel_move);
+ // Move stack/stack slot to take advantage of a free register on constrained machines.
+ for (size_t i = 0; i < moves_.Size(); ++i) {
+ const MoveOperands& move = *moves_.Get(i);
+ // Ignore constants and moves already eliminated.
+ if (move.IsEliminated() || move.GetSource().IsConstant()) {
+ continue;
+ }
+
+ if ((move.GetSource().IsStackSlot() || move.GetSource().IsDoubleStackSlot()) &&
+ (move.GetDestination().IsStackSlot() || move.GetDestination().IsDoubleStackSlot())) {
+ PerformMove(i);
+ }
+ }
+
for (size_t i = 0; i < moves_.Size(); ++i) {
const MoveOperands& move = *moves_.Get(i);
// Skip constants to perform them last. They don't block other moves
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index d11a441a6e..d1c1134565 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -153,7 +153,7 @@ static HBoundType* CreateBoundType(ArenaAllocator* arena,
HInstruction* obj,
HLoadClass* load_class,
bool upper_can_be_null)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo();
ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
HBoundType* bound_type = new (arena) HBoundType(obj, class_rti, upper_can_be_null);
@@ -180,7 +180,7 @@ static bool ShouldCreateBoundType(HInstruction* position,
ReferenceTypeInfo upper_bound,
HInstruction* dominator_instr,
HBasicBlock* dominator_block)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
// If the position where we should insert the bound type is not already a
// a bound type then we need to create one.
if (position == nullptr || !position->IsBoundType()) {
@@ -388,6 +388,8 @@ void RTPVisitor::UpdateReferenceTypeInfo(HInstruction* instr,
}
void RTPVisitor::VisitNullConstant(HNullConstant* instr) {
+ // TODO: The null constant could be bound contextually (e.g. based on return statements)
+ // to a more precise type.
instr->SetReferenceTypeInfo(
ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false));
}
@@ -438,8 +440,10 @@ void RTPVisitor::VisitLoadClass(HLoadClass* instr) {
Runtime::Current()->GetClassLinker()->FindDexCache(instr->GetDexFile());
// Get type from dex cache assuming it was populated by the verifier.
mirror::Class* resolved_class = dex_cache->GetResolvedType(instr->GetTypeIndex());
- DCHECK(resolved_class != nullptr);
- ReferenceTypeInfo::TypeHandle handle = handles_->NewHandle(resolved_class);
+ // TODO: investigating why we are still getting unresolved classes: b/22821472.
+ ReferenceTypeInfo::TypeHandle handle = (resolved_class != nullptr)
+ ? handles_->NewHandle(resolved_class)
+ : object_class_handle_;
instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(handle, /* is_exact */ true));
instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(class_class_handle_, /* is_exact */ true));
}
@@ -546,7 +550,7 @@ ReferenceTypeInfo ReferenceTypePropagation::MergeTypes(const ReferenceTypeInfo&
static void UpdateArrayGet(HArrayGet* instr,
StackHandleScopeCollection* handles,
ReferenceTypeInfo::TypeHandle object_class_handle)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK_EQ(Primitive::kPrimNot, instr->GetType());
ReferenceTypeInfo parent_rti = instr->InputAt(0)->GetReferenceTypeInfo();
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 9196b56e37..14d4a82e9b 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -41,8 +41,8 @@ class ReferenceTypePropagation : public HOptimization {
private:
void VisitPhi(HPhi* phi);
void VisitBasicBlock(HBasicBlock* block);
- void UpdateBoundType(HBoundType* bound_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void UpdatePhi(HPhi* phi) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void UpdateBoundType(HBoundType* bound_type) SHARED_REQUIRES(Locks::mutator_lock_);
+ void UpdatePhi(HPhi* phi) SHARED_REQUIRES(Locks::mutator_lock_);
void BoundTypeForIfNotNull(HBasicBlock* block);
void BoundTypeForIfInstanceOf(HBasicBlock* block);
void ProcessWorklist();
@@ -53,7 +53,7 @@ class ReferenceTypePropagation : public HOptimization {
bool UpdateReferenceTypeInfo(HInstruction* instr);
ReferenceTypeInfo MergeTypes(const ReferenceTypeInfo& a, const ReferenceTypeInfo& b)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
StackHandleScopeCollection* handles_;
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index ff2e6ad821..2c34e4dd03 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -570,7 +570,9 @@ void SsaBuilder::VisitInstruction(HInstruction* instruction) {
if (instruction->GetBlock()->IsInTry() && instruction->CanThrow()) {
HTryBoundary* try_block = instruction->GetBlock()->GetTryEntry();
for (HExceptionHandlerIterator it(*try_block); !it.Done(); it.Advance()) {
- GrowableArray<HInstruction*>* handler_locals = GetLocalsFor(it.Current());
+ HBasicBlock* handler = it.Current();
+ handler->AddExceptionalPredecessor(instruction);
+ GrowableArray<HInstruction*>* handler_locals = GetLocalsFor(handler);
for (size_t i = 0, e = current_locals_->Size(); i < e; ++i) {
HInstruction* local_value = current_locals_->Get(i);
if (local_value != nullptr) {
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 701dbb019b..40502c173b 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -225,7 +225,7 @@ void SsaLivenessAnalysis::ComputeLiveRanges() {
// SsaLivenessAnalysis.
for (size_t i = 0, e = environment->Size(); i < e; ++i) {
HInstruction* instruction = environment->GetInstructionAt(i);
- bool should_be_live = ShouldBeLiveForEnvironment(instruction);
+ bool should_be_live = ShouldBeLiveForEnvironment(current, instruction);
if (should_be_live) {
DCHECK(instruction->HasSsaIndex());
live_in->SetBit(instruction->GetSsaIndex());
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 220ee6a8d0..a7044de850 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -1201,8 +1201,14 @@ class SsaLivenessAnalysis : public ValueObject {
// Update the live_out set of the block and returns whether it has changed.
bool UpdateLiveOut(const HBasicBlock& block);
- static bool ShouldBeLiveForEnvironment(HInstruction* instruction) {
+ // Returns whether `instruction` in an HEnvironment held by `env_holder`
+ // should be kept live by the HEnvironment.
+ static bool ShouldBeLiveForEnvironment(HInstruction* env_holder,
+ HInstruction* instruction) {
if (instruction == nullptr) return false;
+ // A value that's not live in compiled code may still be needed in interpreter,
+ // due to code motion, etc.
+ if (env_holder->IsDeoptimize()) return true;
if (instruction->GetBlock()->GetGraph()->IsDebuggable()) return true;
return instruction->GetType() == Primitive::kPrimNot;
}
diff --git a/compiler/trampolines/trampoline_compiler.h b/compiler/trampolines/trampoline_compiler.h
index bdab2796d8..9fb22452ea 100644
--- a/compiler/trampolines/trampoline_compiler.h
+++ b/compiler/trampolines/trampoline_compiler.h
@@ -27,10 +27,10 @@ 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_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateTrampoline64(InstructionSet isa, EntryPointCallingConvention abi,
ThreadOffset<8> entry_point_offset)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_);
} // namespace art
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 413b9eaa8c..b499dddb0c 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -133,14 +133,27 @@ uint32_t Thumb2Assembler::AdjustFixups() {
AdjustFixupIfNeeded(&fixup, &current_code_size, &fixups_to_recalculate);
}
while (!fixups_to_recalculate.empty()) {
- // Pop the fixup.
- FixupId fixup_id = fixups_to_recalculate.front();
- fixups_to_recalculate.pop_front();
- Fixup* fixup = GetFixup(fixup_id);
- DCHECK_NE(buffer_.Load<int16_t>(fixup->GetLocation()), 0);
- buffer_.Store<int16_t>(fixup->GetLocation(), 0);
- // See if it needs adjustment.
- AdjustFixupIfNeeded(fixup, &current_code_size, &fixups_to_recalculate);
+ do {
+ // Pop the fixup.
+ FixupId fixup_id = fixups_to_recalculate.front();
+ fixups_to_recalculate.pop_front();
+ Fixup* fixup = GetFixup(fixup_id);
+ DCHECK_NE(buffer_.Load<int16_t>(fixup->GetLocation()), 0);
+ buffer_.Store<int16_t>(fixup->GetLocation(), 0);
+ // See if it needs adjustment.
+ AdjustFixupIfNeeded(fixup, &current_code_size, &fixups_to_recalculate);
+ } while (!fixups_to_recalculate.empty());
+
+ if ((current_code_size & 2) != 0 && !literals_.empty()) {
+ // If we need to add padding before literals, this may just push some out of range,
+ // so recalculate all load literals. This makes up for the fact that we don't mark
+ // load literal as a dependency of all previous Fixups even though it actually is.
+ for (Fixup& fixup : fixups_) {
+ if (fixup.IsLoadLiteral()) {
+ AdjustFixupIfNeeded(&fixup, &current_code_size, &fixups_to_recalculate);
+ }
+ }
+ }
}
if (kIsDebugBuild) {
// Check that no fixup is marked as being in fixups_to_recalculate anymore.
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index 838554ee6d..41eb5d36f2 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -489,6 +489,10 @@ class Thumb2Assembler FINAL : public ArmAssembler {
return type_;
}
+ bool IsLoadLiteral() const {
+ return GetType() >= kLoadLiteralNarrow;
+ }
+
Size GetOriginalSize() const {
return original_size_;
}
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 68b7931a0c..84f5cb16fb 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -950,4 +950,73 @@ TEST_F(AssemblerThumb2Test, LoadLiteralDoubleFar) {
__ GetAdjustedPosition(label.Position()));
}
+TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiBDueToAlignmentOnSecondPass) {
+ // First part: as TwoCbzBeyondMaxOffset but add one 16-bit instruction to the end,
+ // so that the size is not Aligned<4>(.). On the first pass, the assembler resizes
+ // the second CBZ because it's out of range, then it will resize the first CBZ
+ // which has been pushed out of range. Thus, after the first pass, the code size
+ // will appear Aligned<4>(.) but the final size will not be.
+ Label label0, label1, label2;
+ __ cbz(arm::R0, &label1);
+ constexpr size_t kLdrR0R0Count1 = 63;
+ for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ Bind(&label0);
+ __ cbz(arm::R0, &label2);
+ __ Bind(&label1);
+ constexpr size_t kLdrR0R0Count2 = 65;
+ for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ Bind(&label2);
+ __ ldr(arm::R0, arm::Address(arm::R0));
+
+ std::string expected_part1 =
+ "cmp r0, #0\n" // cbz r0, label1
+ "beq.n 1f\n" +
+ RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
+ "0:\n"
+ "cmp r0, #0\n" // cbz r0, label2
+ "beq.n 2f\n"
+ "1:\n" +
+ RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
+ "2:\n" // Here the offset is Aligned<4>(.).
+ "ldr r0, [r0]\n"; // Make the first part
+
+ // Second part: as LoadLiteralMax1KiB with the caveat that the offset of the load
+ // literal will not be Aligned<4>(.) but it will appear to be when we process the
+ // instruction during the first pass, so the literal will need a padding and it
+ // will push the literal out of range, so we shall end up with "ldr.w".
+ arm::Literal* literal = __ NewLiteral<int32_t>(0x12345678);
+ __ LoadLiteral(arm::R0, literal);
+ Label label;
+ __ Bind(&label);
+ constexpr size_t kLdrR0R0Count = 511;
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+
+ std::string expected =
+ expected_part1 +
+ "1:\n"
+ "ldr.w r0, [pc, #((2f - 1b - 2) & ~2)]\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".align 2, 0\n"
+ "2:\n"
+ ".word 0x12345678\n";
+ DriverStr(expected, "LoadLiteralMax1KiB");
+
+ EXPECT_EQ(static_cast<uint32_t>(label.Position()) + 6u,
+ __ GetAdjustedPosition(label.Position()));
+}
+
+TEST_F(AssemblerThumb2Test, Clz) {
+ __ clz(arm::R0, arm::R1);
+
+ const char* expected = "clz r0, r1\n";
+
+ DriverStr(expected, "clz");
+}
+
} // namespace art
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 20f61f942b..cb01cea8ef 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -32,7 +32,7 @@ namespace arm {
// Include results file (generated manually)
#include "assembler_thumb_test_expected.cc.inc"
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
// This controls whether the results are printed to the
// screen or compared against the expected output.
// To generate new expected output, set this to true and
@@ -72,7 +72,7 @@ void InitResults() {
}
std::string GetToolsDir() {
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
// This will only work on the host. There is no as, objcopy or objdump on the device.
static std::string toolsdir;
@@ -89,7 +89,7 @@ std::string GetToolsDir() {
}
void DumpAndCheck(std::vector<uint8_t>& code, const char* testname, const char* const* results) {
-#ifndef HAVE_ANDROID_OS
+#ifndef __ANDROID__
static std::string toolsdir = GetToolsDir();
ScratchFile file;
diff --git a/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc
index 325ee4fa01..42ed8810f8 100644
--- a/compiler/utils/swap_space.cc
+++ b/compiler/utils/swap_space.cc
@@ -143,7 +143,6 @@ SpaceChunk SwapSpace::NewFileChunk(size_t min_size) {
LOG(ERROR) << "Unable to mmap new swap file chunk.";
LOG(ERROR) << "Current size: " << size_ << " requested: " << next_part << "/" << min_size;
LOG(ERROR) << "Free list:";
- MutexLock lock(Thread::Current(), lock_);
DumpFreeMap(free_by_size_);
LOG(ERROR) << "In free list: " << CollectFree(free_by_start_, free_by_size_);
LOG(FATAL) << "Aborting...";
diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h
index 691df4a945..f7c772d673 100644
--- a/compiler/utils/swap_space.h
+++ b/compiler/utils/swap_space.h
@@ -60,15 +60,15 @@ class SwapSpace {
public:
SwapSpace(int fd, size_t initial_size);
~SwapSpace();
- void* Alloc(size_t size) LOCKS_EXCLUDED(lock_);
- void Free(void* ptr, size_t size) LOCKS_EXCLUDED(lock_);
+ void* Alloc(size_t size) REQUIRES(!lock_);
+ void Free(void* ptr, size_t size) REQUIRES(!lock_);
size_t GetSize() {
return size_;
}
private:
- SpaceChunk NewFileChunk(size_t min_size);
+ SpaceChunk NewFileChunk(size_t min_size) REQUIRES(lock_);
int fd_;
size_t size_;
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index fa85ada864..8c2a3ed637 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -145,6 +145,13 @@ void X86Assembler::movl(const Address& dst, Label* lbl) {
EmitLabel(lbl, dst.length_ + 5);
}
+void X86Assembler::movntl(const Address& dst, Register src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x0F);
+ EmitUint8(0xC3);
+ EmitOperand(src, dst);
+}
+
void X86Assembler::bswapl(Register dst) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x0F);
@@ -1194,11 +1201,26 @@ void X86Assembler::imull(Register dst, Register src) {
}
-void X86Assembler::imull(Register reg, const Immediate& imm) {
+void X86Assembler::imull(Register dst, Register src, const Immediate& imm) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
- EmitUint8(0x69);
- EmitOperand(reg, Operand(reg));
- EmitImmediate(imm);
+ // See whether imm can be represented as a sign-extended 8bit value.
+ int32_t v32 = static_cast<int32_t>(imm.value());
+ if (IsInt<8>(v32)) {
+ // Sign-extension works.
+ EmitUint8(0x6B);
+ EmitOperand(dst, Operand(src));
+ EmitUint8(static_cast<uint8_t>(v32 & 0xFF));
+ } else {
+ // Not representable, use full immediate.
+ EmitUint8(0x69);
+ EmitOperand(dst, Operand(src));
+ EmitImmediate(imm);
+ }
+}
+
+
+void X86Assembler::imull(Register reg, const Immediate& imm) {
+ imull(reg, reg, imm);
}
@@ -1515,6 +1537,21 @@ void X86Assembler::repne_scasw() {
}
+void X86Assembler::repe_cmpsw() {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0xF3);
+ EmitUint8(0xA7);
+}
+
+
+void X86Assembler::repe_cmpsl() {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xF3);
+ EmitUint8(0xA7);
+}
+
+
X86Assembler* X86Assembler::lock() {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xF0);
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index d1b4e1dc5f..d9c1b404c9 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -231,6 +231,8 @@ class X86Assembler FINAL : public Assembler {
void movl(const Address& dst, const Immediate& imm);
void movl(const Address& dst, Label* lbl);
+ void movntl(const Address& dst, Register src);
+
void bswapl(Register dst);
void movzxb(Register dst, ByteRegister src);
@@ -409,6 +411,7 @@ class X86Assembler FINAL : public Assembler {
void imull(Register dst, Register src);
void imull(Register reg, const Immediate& imm);
+ void imull(Register dst, Register src, const Immediate& imm);
void imull(Register reg, const Address& address);
void imull(Register reg);
@@ -465,6 +468,8 @@ class X86Assembler FINAL : public Assembler {
void jmp(Label* label);
void repne_scasw();
+ void repe_cmpsw();
+ void repe_cmpsl();
X86Assembler* lock();
void cmpxchgl(const Address& address, Register reg);
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index aacc57bb0c..b664d2342f 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -105,6 +105,16 @@ TEST_F(AssemblerX86Test, Movl) {
DriverStr(expected, "movl");
}
+TEST_F(AssemblerX86Test, Movntl) {
+ GetAssembler()->movntl(x86::Address(x86::EDI, x86::EBX, x86::TIMES_4, 12), x86::EAX);
+ GetAssembler()->movntl(x86::Address(x86::EDI, 0), x86::EAX);
+ const char* expected =
+ "movntil %EAX, 0xc(%EDI,%EBX,4)\n"
+ "movntil %EAX, (%EDI)\n";
+
+ DriverStr(expected, "movntl");
+}
+
TEST_F(AssemblerX86Test, psrlq) {
GetAssembler()->psrlq(x86::XMM0, CreateImmediate(32));
const char* expected = "psrlq $0x20, %xmm0\n";
@@ -196,4 +206,16 @@ TEST_F(AssemblerX86Test, Repnescasw) {
DriverStr(expected, "Repnescasw");
}
+TEST_F(AssemblerX86Test, Repecmpsw) {
+ GetAssembler()->repe_cmpsw();
+ const char* expected = "repe cmpsw\n";
+ DriverStr(expected, "Repecmpsw");
+}
+
+TEST_F(AssemblerX86Test, Repecmpsl) {
+ GetAssembler()->repe_cmpsl();
+ const char* expected = "repe cmpsl\n";
+ DriverStr(expected, "Repecmpsl");
+}
+
} // namespace art
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index f35f51c494..22e7b9b120 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -194,6 +194,21 @@ void X86_64Assembler::movl(const Address& dst, const Immediate& imm) {
EmitImmediate(imm);
}
+void X86_64Assembler::movntl(const Address& dst, CpuRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitOptionalRex32(src, dst);
+ EmitUint8(0x0F);
+ EmitUint8(0xC3);
+ EmitOperand(src.LowBits(), dst);
+}
+
+void X86_64Assembler::movntq(const Address& dst, CpuRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitRex64(src, dst);
+ EmitUint8(0x0F);
+ EmitUint8(0xC3);
+ EmitOperand(src.LowBits(), dst);
+}
void X86_64Assembler::cmov(Condition c, CpuRegister dst, CpuRegister src) {
cmov(c, dst, src, true);
@@ -1672,28 +1687,33 @@ void X86_64Assembler::imull(CpuRegister dst, CpuRegister src) {
EmitOperand(dst.LowBits(), Operand(src));
}
-void X86_64Assembler::imull(CpuRegister reg, const Immediate& imm) {
+void X86_64Assembler::imull(CpuRegister dst, CpuRegister src, const Immediate& imm) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
CHECK(imm.is_int32()); // imull only supports 32b immediate.
- EmitOptionalRex32(reg, reg);
+ EmitOptionalRex32(dst, src);
// See whether imm can be represented as a sign-extended 8bit value.
int32_t v32 = static_cast<int32_t>(imm.value());
if (IsInt<8>(v32)) {
// Sign-extension works.
EmitUint8(0x6B);
- EmitOperand(reg.LowBits(), Operand(reg));
+ EmitOperand(dst.LowBits(), Operand(src));
EmitUint8(static_cast<uint8_t>(v32 & 0xFF));
} else {
// Not representable, use full immediate.
EmitUint8(0x69);
- EmitOperand(reg.LowBits(), Operand(reg));
+ EmitOperand(dst.LowBits(), Operand(src));
EmitImmediate(imm);
}
}
+void X86_64Assembler::imull(CpuRegister reg, const Immediate& imm) {
+ imull(reg, reg, imm);
+}
+
+
void X86_64Assembler::imull(CpuRegister reg, const Address& address) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitOptionalRex32(reg, address);
@@ -2073,6 +2093,29 @@ void X86_64Assembler::repne_scasw() {
}
+void X86_64Assembler::repe_cmpsw() {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0xF3);
+ EmitUint8(0xA7);
+}
+
+
+void X86_64Assembler::repe_cmpsl() {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xF3);
+ EmitUint8(0xA7);
+}
+
+
+void X86_64Assembler::repe_cmpsq() {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0xF3);
+ EmitRex64();
+ EmitUint8(0xA7);
+}
+
+
void X86_64Assembler::LoadDoubleConstant(XmmRegister dst, double value) {
// TODO: Need to have a code constants table.
int64_t constant = bit_cast<int64_t, double>(value);
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 61ffeab1e8..b8e5fb6e1d 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -326,6 +326,9 @@ class X86_64Assembler FINAL : public Assembler {
void movq(CpuRegister dst, CpuRegister src);
void movl(CpuRegister dst, CpuRegister src);
+ void movntl(const Address& dst, CpuRegister src);
+ void movntq(const Address& dst, CpuRegister src);
+
void movq(CpuRegister dst, const Address& src);
void movl(CpuRegister dst, const Address& src);
void movq(const Address& dst, CpuRegister src);
@@ -539,6 +542,7 @@ class X86_64Assembler FINAL : public Assembler {
void imull(CpuRegister dst, CpuRegister src);
void imull(CpuRegister reg, const Immediate& imm);
+ void imull(CpuRegister dst, CpuRegister src, const Immediate& imm);
void imull(CpuRegister reg, const Address& address);
void imulq(CpuRegister src);
@@ -603,6 +607,9 @@ class X86_64Assembler FINAL : public Assembler {
void bswapq(CpuRegister dst);
void repne_scasw();
+ void repe_cmpsw();
+ void repe_cmpsl();
+ void repe_cmpsq();
//
// Macros for High-level operations.
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 6da5c35731..296487e798 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -35,7 +35,7 @@ TEST(AssemblerX86_64, CreateBuffer) {
ASSERT_EQ(static_cast<size_t>(5), buffer.Size());
}
-#ifdef HAVE_ANDROID_OS
+#ifdef __ANDROID__
static constexpr size_t kRandomIterations = 1000; // Devices might be puny, don't stress them...
#else
static constexpr size_t kRandomIterations = 100000; // Hosts are pretty powerful.
@@ -674,6 +674,46 @@ TEST_F(AssemblerX86_64Test, MovqAddrImm) {
DriverStr(expected, "movq");
}
+TEST_F(AssemblerX86_64Test, Movntl) {
+ GetAssembler()->movntl(x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12), x86_64::CpuRegister(x86_64::RAX));
+ GetAssembler()->movntl(x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), x86_64::CpuRegister(x86_64::RAX));
+ GetAssembler()->movntl(x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), x86_64::CpuRegister(x86_64::RAX));
+ GetAssembler()->movntl(x86_64::Address(x86_64::CpuRegister(x86_64::R13), 0), x86_64::CpuRegister(x86_64::RAX));
+ GetAssembler()->movntl(x86_64::Address(
+ x86_64::CpuRegister(x86_64::R13), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_1, 0), x86_64::CpuRegister(x86_64::R9));
+ const char* expected =
+ "movntil %EAX, 0xc(%RDI,%RBX,4)\n"
+ "movntil %EAX, 0xc(%RDI,%R9,4)\n"
+ "movntil %EAX, 0xc(%RDI,%R9,4)\n"
+ "movntil %EAX, (%R13)\n"
+ "movntil %R9d, (%R13,%R9,1)\n";
+
+ DriverStr(expected, "movntl");
+}
+
+TEST_F(AssemblerX86_64Test, Movntq) {
+ GetAssembler()->movntq(x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12), x86_64::CpuRegister(x86_64::RAX));
+ GetAssembler()->movntq(x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), x86_64::CpuRegister(x86_64::RAX));
+ GetAssembler()->movntq(x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), x86_64::CpuRegister(x86_64::RAX));
+ GetAssembler()->movntq(x86_64::Address(x86_64::CpuRegister(x86_64::R13), 0), x86_64::CpuRegister(x86_64::RAX));
+ GetAssembler()->movntq(x86_64::Address(
+ x86_64::CpuRegister(x86_64::R13), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_1, 0), x86_64::CpuRegister(x86_64::R9));
+ const char* expected =
+ "movntiq %RAX, 0xc(%RDI,%RBX,4)\n"
+ "movntiq %RAX, 0xc(%RDI,%R9,4)\n"
+ "movntiq %RAX, 0xc(%RDI,%R9,4)\n"
+ "movntiq %RAX, (%R13)\n"
+ "movntiq %R9, (%R13,%R9,1)\n";
+
+ DriverStr(expected, "movntq");
+}
+
TEST_F(AssemblerX86_64Test, Cvtsi2ssAddr) {
GetAssembler()->cvtsi2ss(x86_64::XmmRegister(x86_64::XMM0),
x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0),
@@ -1263,4 +1303,22 @@ TEST_F(AssemblerX86_64Test, Repnescasw) {
DriverStr(expected, "Repnescasw");
}
+TEST_F(AssemblerX86_64Test, Repecmpsw) {
+ GetAssembler()->repe_cmpsw();
+ const char* expected = "repe cmpsw\n";
+ DriverStr(expected, "Repecmpsw");
+}
+
+TEST_F(AssemblerX86_64Test, Repecmpsl) {
+ GetAssembler()->repe_cmpsl();
+ const char* expected = "repe cmpsl\n";
+ DriverStr(expected, "Repecmpsl");
+}
+
+TEST_F(AssemblerX86_64Test, Repecmpsq) {
+ GetAssembler()->repe_cmpsq();
+ const char* expected = "repe cmpsq\n";
+ DriverStr(expected, "Repecmpsq");
+}
+
} // namespace art